目录

U2F(Universal 2 Factor)标准是由Yubico公司Google发起的FIDO(Fast IDentity Online)联盟推出的标准, 旨在提供一个方便的免驱动、通用型的密码认证令牌, 期望能在让用户在有U2F认证的情况下, 即使用短密码, 也能实现高强度的认证.
而且这种认证是不依赖中心服务器的, 完全基于公私钥/PKI体系.

U2F的物理层可以是USB-HID, 也可以是NFC(目前Yubikey Neo支持), 也可以是BTLE(在国外开会的时候遇到Yubico的Sales, 他们说这两年就会有支持BTLE的Yubikey, 这样iPhone就能用上了).

U2F-Zero是一个以BSD协议开源的U2F令牌. 代码在github上的conorpp/u2f-zero. 文档见其Wiki.

U2F-Zero在Amazon上可以购买, 售价8刀. 作者还放出了一个视频, 是他找了一部2小时的电影, 一边看一边手工烧录….

我口水流了一地, 于是联合yaboo一起造了几只出来.

下面打算先讲使用, 再讲U2F的原理, 最后再讲一讲U2F-Zero的硬件及制造流程.

图:U2F-Zero

U2F-Zero的使用

U2F-Zero上有15个Slot, 可以存15组公私钥对. (后面会提到, Yubikey是如何实现无限多组U2F的)

(2017-01-29 更新: 最近的commit已经增加了与Yubikey类似的Key Wrapping机制.)

浏览器: 网页认证

直接注册使用就可以了. Chrome浏览器原生支持, Firefox需要装一个插件. Firefox的原生支持正在积极推进中. Opera 也已经支持了(2016年9月起). IE Edge 预计在2017年上半年推出U2F支持.

U2F目前支持的在线服务:

也可以参考这个清单. 另外, 可以去 https://demo.yubico.com/u2f 上进行调试.

Linux: 以pam.d/sudo举例

以Debian系为例:

echo 'KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"' >> /etc/udev/rules.d/70-u2f.rules
sudo udevadm trigger

sudo apt-get install libpam-u2f pamu2fcfg
# 如果源里没有, 可以先添加ppa:
# sudo add-apt-repository ppa:yubico/stable
# sudo apt-get update
man pamu2fcfg

pamu2fcfg >> /etc/u2f_mappings

sudo chmod 644 /etc/u2f_mappings
sudo chown root:root /etc/u2f_mappings

#以下是必须有密码和U2F同时通过, 才放行
echo "auth required pam_u2f.so authfile=/etc/u2f_mappings cue" >> /etc/pam.d/sudo 

# 如果想password或U2F二者其一即可认证的话, 把以下
# auth sufficient pam_u2f.so authfile=/etc/u2f_mappings cue
# 加到 /etc/pam.d/sudo 的开头

# 如果在Ubuntu里,想要登录的时候也能用
# 可以加到 /etc/pam.d/common-auth
echo "auth sufficient pam_u2f.so authfile=/etc/u2f_mappings cue" >> /etc/pam.d/common-auth

如果有多个U2F Key, 不能直接在u2f_mappings里放两行, 同一个用户名只能放一行, 按以下格式, 以:分隔:

<username1>:<KeyHandle1>,<UserKey1>:<KeyHandle2>,<UserKey2>:...
<username2>:<KeyHandle1>,<UserKey1>:<KeyHandle2>,<UserKey2>:...

如:

scateu:BJkQhw,04efe995ba9361b9530e2d3572662f01da08e6f28e77cfdac8981000128c0597ee5d706b7eaee9ea3a79f572654c482daa0ef9193407a3a2b379e284c08bb95a59:axh4xfEr6o_i6z8BAXcW24Q_2AWGgfx2HiW7FURLV-Wz-hCPIh_UWS1ANASSUDsxNDklsZsf2tqQ_ECy4KdRmA,04c113c247a2233665f58c1f949f25c91f9408b7dc769e69c844e147fabc6cba73be629f2dc4a8c559aeab72ca24fcd5bce221b29ea5cd0a52131f2426625376d7

(可选) 指定 AppID 和 Origin

例如:

$ pamu2fcfg -opam://scadeMBP.lan -ipam://scateudeMBP.lan

auth       sufficient pam_u2f.so origin=pam://scadeMBP.lan appid=pam://scadeMBP.lan 

建议指定一下, 否则hostname一旦被修改(例如hostname是由DHCP生成的)就有可能认证失效.

以及, 这样子可以复用一个Key, 如何操作留作习题.

(可选) 打开调试

auth       sufficient pam_u2f.so debug

(可选) 打开提示

auth       sufficient pam_u2f.so cue

当需要你按一下U2F的时候, 会有一行提示:

Please touch the device.

这样sudo的时候就不至于以为它已经在执行了, 而事实上却在等你的U2F用户交互.

udev rules

如果在Linux/macOS上用, 发现需要sudo才能用的话, 需要手工配置一下udev rules.

https://github.com/Yubico/libu2f-host/blob/master/70-u2f.rules

另外, u2f-hidraw-policy 项目提供了一个能够检测所有u2f设备的udev rules程序.

核心的判别代码
/*
 * Detect U2F tokens.  See:
 * https://fidoalliance.org/specs/fido-u2f-HID-protocol-v1.0-rd-20141008.pdf
 * http://www.usb.org/developers/hidpage/HUTRR48.pdf
 */

if (type == HID_RPTDESC_TYPE_LOCAL &&
    tag == HID_RPTDESC_LOCAL_ITEM_USAGE) {
	if (usage_page == 0xf1d0 && value == 0x1)
		is_u2f_token = 1;
}

pam-u2f的参考链接

macOS: sudo

macOS的方法见: http://nicluo.com/secure-your-mac-with-yubico-u2f-fido/

基本一样, 留作习题.

U2F-Zero的Python管理配置程序: client.py

可以wipe掉15组key.

$ python client.py 
usage: client.py <action> [<arguments>] [-s serial-number]
actions:
     configure <output-file>: setup the device configuration.  must specify pubkey output.
     rng: Continuously dump random numbers from the devices hardware RNG.
     seed: update the hardware RNG seed with input from stdin
     wipe: wipe all registered keys on U2F Zero.  Must also press button 5 times.  Not reversible.
     list: list all connected U2F Zero tokens.
     wink: blink the LED

Windows 登录中使用 U2F

微软有一篇博客说要支持FIDO, 不过没找到在哪里.
Authasas公司貌似实现了在Windows Login中使用U2F.
Yubico说在Windows 10 Anniversary Edition里很快就可以支持, 不过需要在Windows App Store里装一个应用, 基于Windows Hello框架. 名字叫”YubiKey for Windows Hello”.

另外, 找到了FIDO Booster, 免费版本有限制, 只能用一个U2F Key.
(下载链接: Windown 10,
64bit/
32bit Win7,
64bit/
32bit
)
测试了一下, Yubikey可以用, 但不知道为啥, U2F-Zero在注册的时候, 按了按钮没办法继续. 我来修一下? #ChallengeAccepted!

其它U2F硬件

Amazon的搜索页面. 国产的有飞天. 淘宝上有售, 不过店家貌似被禁限售规则搞的非常愤怒(lol).

U2F-Zero的特殊用法: 随机数发生器

ATECC508 芯片中有 “Internal High quality FIPS Random Number Generator (RNG)”, 这个 FIPS 是指 Federal Information Processing Standard.

(补充: 密码学中的随机数发生器, 其不可预测性比统计学偏差(随机性)更重要, 以下只是测试一下它的统计学偏差(随机性))

python client.py rng

## 测试随机数的熵
sudo apt-get install ent
# brew install ent #macOS上brew可安装
$ dd if=/dev/urandom of=/dev/stdout bs=1024 count=82 | ent
/dev/urandom 的 ent 测试结果:
Entropy = 7.997559 bits per byte.

Optimum compression would reduce the size
of this 83968 byte file by 0 percent.

Chi square distribution for 83968 samples is 285.90, and randomly
would exceed this value 8.92 percent of the times.

Arithmetic mean value of data bytes is 127.5114 (127.5 = random).
Monte Carlo value for Pi is 3.166499929 (error 0.79 percent).
Serial correlation coefficient is 0.002367 (totally uncorrelated = 0.0).
U2F-Zero 的 ent 测试结果:
Entropy = 7.997905 bits per byte.

Optimum compression would reduce the size
of this 84109 byte file by 0 percent.

Chi square distribution for 84109 samples is 245.11, and randomly
would exceed this value 66.06 percent of the times.

Arithmetic mean value of data bytes is 127.4697 (127.5 = random).
Monte Carlo value for Pi is 3.120844628 (error 0.66 percent).
Serial correlation coefficient is -0.004439 (totally uncorrelated = 0.0).
对照组 /dev/zero 的 ent 测试结果:
scateu@scadeMBP:~$ dd if=/dev/zero of=/dev/stdout bs=1024 count=3000 | ent
Entropy = 0.000000 bits per byte.

Optimum compression would reduce the size
of this 3072000 byte file by 100 percent.

Chi square distribution for 3072000 samples is 783360000.00, and randomly
would exceed this value less than 0.01 percent of the times.

Arithmetic mean value of data bytes is 0.0000 (127.5 = random).
Monte Carlo value for Pi is 4.000000000 (error 27.32 percent).
Serial correlation coefficient is undefined (all values equal!).

请教了一下统计学, 她说:

读了一下chisquare那部分, 那个percentage像是p value一样.
按照这个标准第二个generator更random.
虽然都基本通过了假设检验的标准.
percentage越接近50越random.

ent程序的文档上说:

If the percentage is greater than 99% or less than 1%, the sequence is almost certainly not random. If the percentage is between 99% and 95% or between 1% and 5%, the sequence is suspect. Percentages between 90% and 95% and 5% and 10% indicate the sequence is “almost suspect”. Note that our JPEG file, while very dense in information, is far from random as revealed by the chi-square test.

TODO: 给 U2F-Zero 提供改进

清理某个 Keyhandle

TODO

像Yubikey那样, 支持无限多个绑定

TODO

U2F 原理

概述

Host 方面主要的代码在 u2f-zero/tools/u2f_zero_client/client.py 这里.

tests/目录里有测试用例, 可供参考.

以下摘自U2F Overview:

The U2F device and protocol need to guarantee user privacy and security. At the core of the protocol, the U2F device has a capability (ideally, embodied in a secure element) which mints an origin-specific public/private key pair. The U2F device gives the public key and a Key Handle to the origin online service or website during the user registration step.
Later, when the user performs an authentication, the origin online service or website sends the Key Handle back to the U2F device via the browser. The U2F device uses the Key Handle to identify the user’s private key, and creates a signature which is sent back to the origin to verify the presence of the U2F device. Thus, the Key Handle is simply an identifier of a particular key on the U2F device.
The key pair created by the U2F device during registration is origin specific. During registration, the browser sends the U2F device a hash of the origin (combination of protocol, hostname and port). The U2F device returns a public key and a Key Handle. Very importantly, the U2F device encodes the requesting origin into the Key Handle. Later, when the user attempts to authenticate, the server sends the user’s Key Handle back to the browser. The browser sends this Key Handle and the hash of the origin which is requesting the authentication. The U2F device ensures that it had issued this Key Handle to that particular origin hash before performing any signing operation. If there is a mismatch no signature is returned.
This origin check ensures that the public keys and Key Handles issued by a U2F device to a particular online service or website cannot be exercised by a different online service or website (i.e., a site with a different name on a valid SSL certificate). This is a critical privacy property – assuming the browser is working as it should, a site can verify identity strongly with a user’s U2F device only with a key which has been issued to that particular site by that particular U2F device. If this origin check was not present, a public key and Key Handle issued by a U2F device could be used as a ‘supercookie’ which allows multiple colluding sites to strongly verify and correlate a particular user’s identity.

participant Device
participant Browser
participant Server
Browser->Server: username and password
Note over Server: verify password
Note over Server: generate challenge
Server->Browser:  challenge
Browser->Device: challenge
Note over Device: user touches button
Device-->Browser: response
Browser->Server: response
Note over Server: verify response

数据格式

In this current version of U2F, the framing is defined based on the ISO7816-4:2005 extended APDU format.

参考代码

测试flynn/u2f(对Yubikey):
(sid)scateu@localhost:~/dev/go/u2f-go/u2ftoken/example$ sudo ./main
2016/10/30 21:36:54 manufacturer = "Yubico", product = "Yubico Yubikey NEO OTP+U2F+CCID", vid = 0x0116, pid = 0x1050
2016/10/30 21:36:55 version: U2F_V2
2016/10/30 21:36:55 registering, provide user presence
2016/10/30 21:37:07 registered: 0504[1518 fewer characters...]6dcf85
2016/10/30 21:37:07 key handle: 8479a4fc387a890acb796f0e80eb4fc1fea31ede4a39b7923e6d256edf9e9207edb280101fa4373728f10dd6d66cdc9959deda38faffc3f8156d26f9cea71a60
google/u2f-ref-code 的测试(对Yubikey):
(sid)scateu@localhost:~/tmp/u2f-ref-code/u2f-tests/HID$ sudo ./HIDTest  /dev/hidraw1
PASS(test_Idle())
PASS(test_Init())
PASS(test_BasicInit())
PASS(test_Unknown(U2FHID_SYNC))
PASS(test_InitOnNonBroadcastEchoesCID())
PASS(test_InitUnderLock())
PASS(test_InitSelfAborts())
PASS(test_InitOther())
PASS(test_OptionalWink())
PASS(test_Lock())
PASS(test_Echo())
PASS(test_LongEcho())
PASS(test_Timeout())
PASS(test_WrongSeq())
PASS(test_NotCont())
PASS(test_NotFirst())
PASS(test_Limits())
PASS(test_Busy())
PASS(test_LeadingZero())
PASS(test_Idle(2.0))
PASS(test_NothingOnChannel0())
PASS(test_OnlyInitOnBroadcast())
PASS(test_Descriptor())

(sid)scateu@localhost:~/tmp/u2f-ref-code/u2f-tests/HID$ sudo ./U2FTest /dev/hidraw1
PASS(check_Compilation())
PASS(test_Version())
PASS(test_UnknownINS())
PASS(test_WrongLength_U2F_VERSION())
PASS(test_WrongLength_U2F_REGISTER())
PASS(test_BadCLA())
PASS(test_Enroll(0x6985))
Touch device and hit enter..
PASS(test_Enroll(0x9000))
PASS(test_Sign(0x6985))
PASS(test_Sign(0x6985, true))
PASS(test_Sign(0x6a80))
PASS(test_Sign(0x6a80))
Touch device and hit enter..
PASS(test_Sign(0x6985, true))
PASS(ctr1 = test_Sign(0x9000))
PASS(test_Sign(0x6985))
Touch device and hit enter..
PASS(ctr2 = test_Sign(0x9000))

U2F-Zero Firmware 代码阅读

开启调试输出

firmware/inc/app.h
42://#define U2F_PRINT

测试模式

需要定义或干宏如: SHA_TEST 等. 见tests/test.c

USB HID 指令集

自定义的命令 custom.h:
#define U2F_CUSTOM_GET_RNG		0x21
#define U2F_CUSTOM_SEED_RNG		0x22
#define U2F_CUSTOM_WIPE_KEYS		0x23
#define U2F_CUSTOM_WINK			0x24
SETUP时的标准指令:
#define U2F_CONFIG_GET_SERIAL_NUM			0x80
#define	U2F_CONFIG_IS_BUILD				0x81
#define U2F_CONFIG_IS_CONFIGURED			0x82
#define U2F_CONFIG_LOCK					0x83
#define U2F_CONFIG_GENKEY				0x84

U2F交互的指令处理大部分在 u2f_hid.c: void u2f_hid_request(struct u2f_hid_msg* req)里面.

U2F Native Command:
// U2F native commands
#define U2F_REGISTER 						0x01
#define U2F_AUTHENTICATE 					0x02
#define U2F_VERSION 						0x03
#define U2F_VENDOR_FIRST 					0xc0
#define U2F_VENDOR_LAST 					0xff

所以总体的处理流程是 u2f_hid --> u2f --> u2f_atecc

最大容量能存几个证书?

15个:

inc/app.h:
#define U2F_ATTESTATION_KEY_SLOT        15

Yubikey U2F 为什么可以关联无穷多组? 而U2F-Zero只能存15组

There is no practical limit to the U2F secured services the Security Key can be associated with. During the registration process, the key pairs are generated on the device (secure element) but the key pairs are not stored on the Security Key. Instead, the key pair (public key and encrypted private key) are stored by each relying party/service that initiated the registration. Therefore, this approach allows for an unlimited number of services to be associated with the Security Key.

U2F标准中是支持两种方式, 一种是把Key Pair存到Yubikey里, 但是这样的话有存储上限; 另一种是让网站那一侧存储生成的Public Key和被Yubikey加密过的Private Key, 认证的时候把这两个都发给Yubikey, 这样Yubikey就拿设备主密钥解密, 从而有了这个Private Key, 从而可以其实无限组U2F认证. 而这种方式虽然从机制是来说是安全的, 但是毕竟Private Key离开了设备, 并不令人开心. 于是Yubikey的实现方案是引入一个随机数Nonce, 网站那一侧存Nonce, 认证的时候把Nonce发到Yubikey上, 从而重生成出原来的Private Key.

Yubikey的方式见下图:

Yubico关于Yubikey U2F可无限注册的图示

Note: 在多数密码系统里, 可以根据Private Key生成Public Key:

That depends on the crypto system.
In RSA, we have (citing Wikipedia):
The public key consists of the modulus n and the public (or encryption) exponent e. The private key consists of the modulus n and the private (or decryption) exponent d which must be kept secret.
Now if we have n and d (the private key), we are only missing e for the public key. But e is often fairly small (less than three digits), or even fixed (a common value is 65537). In these cases getting the public key is trivial.
For Elliptic Curve Diffie-Hellman, the private key is d, and the public key dG (with G also public), so it’s trivial as well.
In most asymmetrical crypto system implementation, the only fact that is ensured is that you cannot find the private key from the public key. The other way round, finding the public key from the private key is trivial in most case.

(此部分也转载到了这个中文wiki.)

另外, 在Github上最近也有人在讨论这件事.

demo.yubico.com

(另一个DEMO: https://akisec.com/demo/)

https://demo.yubico.com/u2f

注册过程:
Login Data
username: scateu
password: scateu

Registration Data
origin: https://demo.yubico.com
version: U2F_V2
challenge: 9J4Y-5JFuGlWxA8Oz-ue_6i8160lhMMSCSn2Sst4ybM
appId: https://demo.yubico.com

Response Data
clientData: {"typ":"navigator.id.finishEnrollment","challenge":"9J4Y-5JFuGlWxA8Oz-ue_6i8160lhMMSCSn2Sst4ybM","origin":"https://demo.yubico.com","cid_pubkey":"unused"}
registrationData: 0504[1519 fewer characters...]33d

Attestation Certificate
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 174263295 (0xa630bff)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=Yubico U2F Root CA Serial 457200631
        Validity
            Not Before: Aug  1 00:00:00 2014 GMT
            Not After : Sep  4 00:00:00 2050 GMT
        Subject: CN=Yubico U2F EE Serial 174263295
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: 
                    04:a4:23:64:5d:ba:8b:23:ed:6c:d9:e5:e4:8b:93:
		    [3 fewer lines..]
                    00:d4:26:d0:9f
                ASN1 OID: prime256v1
        X509v3 extensions:
            1.3.6.1.4.1.41482.2: 
                1.3.6.1.4.1.41482.1.2
    Signature Algorithm: sha256WithRSAEncryption
         65:39:b0:32:a1:cf:c4:48:d2:07:ae:14:9b:0a:b6:b4:60:ca:
	 [13 lines removed....]
         65:51:61:b4
-----BEGIN CERTIFICATE-----
MIICLjCCARigAwIBAgIECmML/zALBgkqhkiG9w0BAQswLjEsMCoGA1UEAxMjWXVi
[10 lines removed...]
F3MgmDRkJ++/a2pV0wAYfPC3tC57BtBdH/UXEB8xZVFhtA==
-----END CERTIFICATE-----

验证过程:
Login Data
username: scateu
password: scateu

Challenge Data
version: U2F_V2
challenge: CbxdAfbOc_S09coJQCs7JOvx9TYHAFSfPlyWLHUZddI
keyHandle: c_NlkEhsJMDN7r_yVXrYAXSS-ZSrAAeep2QJirChH49Frp41_4j9Q_VvKlGS2pOgSBdlGGVjOb8x6NT7n1w4yQ

Response Data
clientData: {"typ":"navigator.id.getAssertion","challenge":"CbxdAfbOc_S09coJQCs7JOvx9TYHAFSfPlyWLHUZddI","origin":"https://demo.yubico.com","cid_pubkey":"unused"}
signatureData: AQAAAAwwRgIhAOEUulp7hYWZuxWfCHEgDPdr4WRwRI7a1j2eKyA8xbyXAiEA-xXrglVrMro7bFr7dAg-MoXi2PSfjhufhD9nJWHdDcw

Authentication Parameters
touch: true
counter: 12

文献参考

U2F-Zero硬件分析及制造流程

a=>operation: Atmel
ATECC508A 
密码学芯片:>http://www.atmel.com/Images/Atmel-8923S-CryptoAuth-ATECC508A-Datasheet-Summary.pdf
b=>operation: Silabs 
EFM8UB11F16G 
单片机 :>https://www.silabs.com/Support%20Documents/TechnicalDocs/EFM8UB1_DataSheet.pdf
c=>inputoutput: USB
d=>operation: 主机

a(right)->b(right)->c(right)->d

Datasheet

密码学芯片 Atmel ATECC508 的特性列表:
  • Cryptographic Co-processor with Secure Hardware-based Key Storage
  • Performs High-Speed Public Key (PKI) Algorithms
    • ECDSA: FIPS186-3 Elliptic Curve Digital Signature Algorithm
    • ECDH: FIPS SP800-56A Elliptic Curve Diffie-Hellman Algorithm
  • NIST Standard P256 Elliptic Curve Support
  • SHA-256 Hash Algorithm with HMAC Option
  • Host and Client Operations
  • 256-bit Key Length
  • Storage for up to 16 Keys
  • Two high-endurance monotonic counters
  • Guaranteed Unique 72-bit Serial Number
  • Internal High-quality FIPS Random Number Generator (RNG)
  • 10Kb EEPROM Memory for Keys, Certificates, and Data
  • Storage for up to 16 Keys
  • Multiple Options for Consumption Logging and One Time Write Information
  • Intrusion Latch for External Tamper Switch or Power-on Chip Enablement. Multiple I/O Options:
    • High-speed Single Pin Interface, with One GPIO Pin
    • 1MHz Standard I2C Interface
  • 2.0V to 5.5V Supply Voltage Range
  • 1.8V to 5.5V IO levels
  • < 150nA Sleep Current
  • 8-pad UDFN, 8-lead SOIC, and 3-lead CONTACT Packages

USB 烧写流程

详细流程见其Wiki:Building a U2F Token

  1. 使用Simplicity Studio(一定要v3)和编程器, 刷入一个初始固件SETUP.hex, 供后面配置证书使用.
    • 编程器刷写时, 要把U2F-Zero插到电脑上供电
    • (这个固件也可以自行编译, 编译的时候要开启ATECC_SETUP_DEVICE宏, 才能生成这一种固件)
  2. (只做一遍就行) ca/genca.sh 生成 CA 证书
  3. (每一个Key都要做一遍) ./setup_device.sh gencert/ca/key.pem
    • 功能为: “lock ATECC508 configuration, pull public key, and create attestation certificate”
  4. 上一步中, ../firmware/src/cert.c已经被重写, 于是打开IDE, 重新编译固件, 再次烧入Key.

MAYBE 使用Arduino做为编程器: https://github.com/x893/C2.Flash

Attestation Cert

[Attestation]{认证,鉴证,证实}
证书是用于验证USB Key的合法性.
W3C:FIDO 2.0:Key Attestation Format 中有表述.

有趣的是, U2F-Zero 的 inc/u2f.h 里有这样一个定义:

// Key id/handle for the private key for attestation
#define U2F_ATTESTATION_HANDLE              ((uint8_t *)"\x00\x00\x00\x00")

另外, 在 Registration 过程中, 需要用 Attestation Key 对 req 进行签署. (用的是ECDSA算法)
而在 Authentication 过程中, 调用的是 req 里传入的 Key Handle 对应的 Key 来签署了. (用的也是ECDSA算法)

对于U2F-Zero来说, EEPROM上可以存16组key. 其中第16组是Attestation Key. 用于验证设备本身. 这个Key Pair会在密码学芯片上产生, 导出Public Key, 然后被根证书签一下, 然后写到单片机固件中, 烧回单片机上.

分析: u2f-zero/tools/setup_device.sh 中的证书生成过程

为了看的明白, 我把setup_device.sh脱一下水

由于setup_device.sh里面被作者加入了一些适用于批量烧写及SN号写入的脚本, 看起来很混乱. 把这些逻辑去除, 发现这个设置脚本就做了几件事:

echo "configuring..."
client.py configure pubkey.hex >/dev/null
echo "generate attestation certificate..."
gencert.sh gencert/ca/key.pem "$(cat pubkey.hex)" attest.der > ../firmware/src/cert.c
echo "Open Simplicity Studio and rebuild final program."
echo "Then you can erase and reprogram U2F Token."
  • 与设备交互, 生成 pubkey.hex 文件(猜测应该是在设备上生成了私钥)
  • 调用 gencert.sh 生成 attest.der
    • hex2pubkey
      • pubkey.hex恢复成pem格式pubkey.pem(是一个prime256v1 ECC public key)
      • hex的格式是, 前64字节是坐标x, 后64字节是坐标y.
    • signcert
      • 用CA的私钥gencert/ca/key.pem签了一下上述公钥pubkey.pem, 导出为证书attest.der文件.
      • signcert.c里面默认使用了CN=u2fzero.com等证书Subject信息. 按wiki的说法是, 如果有需要的话, 可以改这个.
    • 调用cbytes.py, 处理了一下中间生成的attest.der(转成ASCII, 打印到标准输出), 管道写入 ../firmware/src/cert.c

NOTE: src/cert.cu2f_register()中被u2f_get_attestation_cert调用, 通过USB返回给Host.

NOTE: ../firmware/src/descriptors.c里有定义U2F-Zero的SN.

U2F-Zero预置的Attestation Cert

位于firmware/src/cert.c里面:

Signature Algorithm: ecdsa-with-SHA256
Issuer: C=VA, O=ConorCo LLC, CN=u2fzero.com
firmware/src/cert.c 预置证书的详细内容
$ man x509 #可以参考Man Page
$ openssl x509 -in b.pem -text
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 1 (0x1)
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=VA, O=ConorCo LLC, CN=u2fzero.com
        Validity
            Not Before: Jul 26 04:53:09 2016 GMT
            Not After : Jul 25 04:53:09 2022 GMT
        Subject: C=VA, O=ConorCo LLC, CN=u2fzero.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
            EC Public Key:
                pub:
                    04:7e:b9:41:e6:14:25:3d:85:b2:45:3f:dc:df:6d:
                    0f:02:52:d5:da:fb:5a:fe:db:a8:f2:01:e0:03:c9:
                    e5:2b:1d:bf:64:03:34:33:e6:e0:c1:a1:21:53:6d:
                    a5:7c:c5:82:b5:d1:53:54:fb:99:bb:27:ec:18:78:
                    48:23:34:09:5b
                ASN1 OID: prime256v1
    Signature Algorithm: ecdsa-with-SHA256
        30:46:02:21:00:b9:01:10:f1:18:b4:f0:bd:35:3b:6a:60:55:
        8d:e8:3a:88:70:8a:3c:03:2b:14:56:58:fe:11:29:7d:3a:05:
        ce:02:21:00:f4:d8:89:1a:fc:36:5a:d7:f2:e9:8d:5a:b3:4b:
        ae:a2:a1:48:80:db:37:14:c3:b7:56:bb:2b:12:69:f2:07:cd
-----BEGIN CERTIFICATE-----
MIIBWzCCAQACAQEwCgYIKoZIzj0EAwIwOTELMAkGA1UEBhMCVkExFDASBgNVBAoM
C0Nvbm9yQ28gTExDMRQwEgYDVQQDDAt1MmZ6ZXJvLmNvbTAeFw0xNjA3MjYwNDUz
MDlaFw0yMjA3MjUwNDUzMDlaMDkxCzAJBgNVBAYTAlZBMRQwEgYDVQQKDAtDb25v
ckNvIExMQzEUMBIGA1UEAwwLdTJmemVyby5jb20wWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAAR+uUHmFCU9hbJFP9zfbQ8CUtXa+1r+26jyAeADyeUrHb9kAzQz5uDB
oSFTbaV8xYK10VNU+5m7J+wYeEgjNAlbMAoGCCqGSM49BAMCA0kAMEYCIQC5ARDx
GLTwvTU7amBVjeg6iHCKPAMrFFZY/hEpfToFzgIhAPTYiRr8NlrX8umNWrNLrqKh
SIDbNxTDt1a7KxJp8gfN
-----END CERTIFICATE-----

TODO client.py configure 过程

本节参考Application Note: Personalization Guide.

  • 一共有16个Slot
  • ATECC508A的I2C地址是0xC0
    -

按照u2f-zero/tools/u2f_zero_client/client.py中的do_configure

config: (共113 bytes)

0000: 0123 6d10 0000 5000 d72c a571 eec0 8500  .#m...P..,.q....
0010: c000 5500 83a0 83a0 83a0 83a0 83a0 83a0  ..U.............
0020: 83a0 83a0 83a0 83a0 83a0 83a0 83a0 83a0  ................
0030: 83a0 83a0 ffff ffff 0000 0000 ffff ffff  ................
0040: 0000 0000 ffff ffff ffff ffff ffff ffff  ................
0050: ffff ffff 0000 5555 ffff 0000 0000 0000  ......UU........
0060: 1300 1300 1300 1300 1300 1300 1300 1300  ................
0070: 1300 1300 1300 1300 1300 1300 1300 3300  ..............3.
0080: 0a                                       .

习题: 使用python hid控制一下U2F-Zero

Wink一下: (以下摘自client.py, 并不是U2F的通用协议)

import hid
h = hid.device()
h.open(0x10c4, 0x8acf, None)
h.write([0,0xff,0xff,0xff,0xff, 0x24, 0, 0])

See Also