#はじめに
前回「CTAP2 お勉強メモ#1」の続き。
これはCTAPのお勉強をしたメモです。
WebAuthn(ウェブオースン)ではなく、CTAP(シータップ)であります。
目次
#1.サンプルプログラム概要(info.exe)
####info.exeの実行結果
C:\project\libfido2\build\examples\Debug>info.exe "\\?\HID#VID_1050&PID_0120#6&1b5e4874&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
proto: 0x02
major: 0x05
minor: 0x00
build: 0x02
caps: 0x05 (wink, cbor, msg)
version strings: U2F_V2, FIDO_2_0
extension strings: hmac-secret
aaguid: f8a011f38c0a4d15800617111f9edc7d
options: rk, up, noplat, noclientPin
maxmsgsiz: 1200
pin protocols: 1
どうやらYubikeyの情報を出すだけのようです。infoですので当然ですね。
####処理はこんな感じ
- HIDをOpenしてYubikeyと接続
- CTAPHID_INITコマンドを送信して応答をGET
- CTAPHID_CBORコマンドを送信して応答をGET
- HIDをClose
次にこの処理の詳細を説明します。
#2. HIDをOpenしてYubikeyと接続
- 起動引数で指定したYubikeyのパスに接続します。
- USBに接続されたYubikey(HIDデバイス)へのアクセスはOpen,Send,Receive,Closeといった低レベルAPIを使って行います。
- これらのAPIは、hidapi.dllというライブラリを使っています。
- この低レベル部分はCTAP仕様で定義されているものではないので詳細は割愛。
##★CTAPHIDコマンドについて
~CTAP仕様書~
8.1. USB Human Interface Device (USB HID)
8.1.4. Message and packet structure
~
CTAP仕様で定義されているのは、HID通信で送受信するパケット仕様以降です。
CTAPHID_INITコマンドの説明の前に、CTAPHIDコマンドについて説明する必要があります。
CTAPでHID通信では以下の仕様が定められています。(細かく見るとかなりめんどくさいルールが色々あるようです。いろんなケースを想定しているのでしょう・・・)
- HID通信用のパケット仕様がある。
- パケットは初期パケット(initialization packets)と継続パケット(continuation packets)があり、フォーマットが少し違う。
- 1パケットは64byte と決まっていて、送信データが1個のパケットに収まりきらない場合、初期パケット+継続パケットの複数パケットに分割して送信する。
- 1パケットの送信データが64byteより少なくても後方にゼロを詰めて必ず64byteで送信する。
#####初期パケット(initialization packets)
項目 | サイズ | 説明1 | 説明2 |
---|---|---|---|
CID | 4 | Channel identifier | チャネル識別子※ |
CMD | 1 | Command identifier (bit 7 always set) | コマンド(7bit目は1にしとけと書いてある) |
BCNTH | 1 | High part of payload length | 送信データのサイズ※ |
BCNTL | 1 | Low part of payload length | 送信データのサイズ※ |
DATA | x | Payload data | 送信データ |
- ※チャネル識別子(CID)について
- CIDという識別子を指定する必要がある。
- これはデバイス側で採番するものであり、クライアントは採番されたCIDを指定する必要がある。
- では、どうやって採番するのか、ということですが、CIDに0xffffffffを指定してCTAPHID_INITを送信すると、Responseで採番されたCIDをGETすることができる。
- ※送信データのサイズ
- データサイズはBCNTHとBCNTLに分かれていて、要はushortの下位バイトと上位バイトって事かと思います。
- このパケットのサイズではなく(パケットは64byteと決まっているので)送信データ全体のサイズをここにセットします。
#####継続パケット(continuation packets)
項目 | サイズ | 説明1 | 説明2 |
---|---|---|---|
CID | 4 | Channel identifier | チャネル識別子 |
SEQ | 1 | Packet sequence 0x00..0x7f (bit 7 always cleared) | 0~のパケット連番 |
DATA | x | Payload data | 送信データ |
#3. CTAPHID_INITコマンドを送信して応答をGET
というわけで、CTAPHID_INITの説明
ここでの送受信データは以下の通り
// Platform → Authenticator 送信データ
// 1packet(64byte)
ff ff ff ff 86 00 08 fc 8c c9 91 14 b5 3b 12 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
// Platform ← Authenticator Response
// 1packet(64byte)
ff ff ff ff 86 00 11 fc 8c c9 91 14 b5 3b 12 00
1d 00 08 02 05 00 02 05 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
###CTAPHID_INITコマンド(0x06)
- CIDの割り当て要求コマンド。
- 最初にこのコマンドCIDをGETし、以降のメッセージのCIDに指定します。
項目 | サイズ | 値 | 説明 |
---|---|---|---|
CID | 4 | 0xffffffff | CIDの割り当て要求という意味 |
CMD | 1 | 0x86 | CTAP_INITという意味※ |
BCNTH | 1 | 0 | データが8byteなのでここは0でよい |
BCNTL | 1 | 8 | データ長 |
DATA | 8 | 8-byte nonce | 8バイトのnonce※ |
- ※CMDについて
- CTAPHID_INITは0x06なんですが、CMDフィールドは 7ビット目を1たてるという謎のルールがある ので 0x80|0x06(CTAPHID_INIT)⇒0x86 を設定します
- ※nonceについて
- 送信側が決めた8byteの値。応答にも含まれるので送信に対する応答かどうかチェックに使いましょう(ということらしい)。
####応答パケット
CTAPHID_INIT (0x06)コマンドに対してこんな応答が返ってきます。
項目 | サイズ | 値 | 説明 |
---|---|---|---|
CID | 4 | 0xffffffff | 送信データと同じ |
CMD | 1 | 0x86 | 送信データと同じ |
BCNTH | 1 | 0 | |
BCNTL | 1 | 17 | データサイズ |
DATA | 8 | 送信で指定したnonce | |
DATA | 4 | 割り当てられたCID | |
DATA | 1 | CTAPHID protocol version identifier | |
DATA | 1 | Major device version number | |
DATA | 1 | Minor device version number | |
DATA | 1 | Build device version number | |
DATA | 1 | Capabilities flags |
####先ほどのCTAPHID_INIT Packetログ をパースすると
Platform → Authenticator 送信データ=CTAPHID_INIT(0x06)コマンド
- CID=0xff ff ff ff (CID要求)
- CMD=0x86 (CTAPHID_INIT(0x06)コマンド)
- BCNTH=0
- BCNTL=8
- DATA=0xfc 8c c9 91 14 b5 3b 12 (nonce)
Response
- CID=0xff ff ff ff
- CMD=0x86
- BCNTH=0
- BCNTL=11
- DATA=0xfc 8c c9 91 14 b5 3b 12 (nonce)
- DATA=00 1d 00 08 (CID)
- DATA=02 (proto)
- DATA=05 (major)
- DATA=00 (minor)
- DATA=02 (build)
- DATA=05 (caps=wink, cbor, msg)
CTAP仕様書 = 8.1.9.1.3. CTAPHID_INIT (0x06)
#4. CTAPHID_CBORコマンドを送信して応答をGET
CTAP仕様書 = 8.1.9.1.2. CTAPHID_CBOR (0x10)
ここでの送受信データは以下の通り
// Platform → Authenticator 送信データ
// 1packet(64byte)
00 1d 00 08 90 00 01 04 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
// Platform ← Authenticator Response
// 2 packet(64byte×2)
// initialization packet
00 1d 00 08 90 00 56 00 a6 01 82 66 55 32 46 5f
56 32 68 46 49 44 4f 5f 32 5f 30 02 81 6b 68 6d
61 63 2d 73 65 63 72 65 74 03 50 f8 a0 11 f3 8c
0a 4d 15 80 06 17 11 1f 9e dc 7d 04 a4 62 72 6b
// continuation packet
00 1d 00 08 00 f5 62 75 70 f5 64 70 6c 61 74 f4
69 63 6c 69 65 6e 74 50 69 6e f4 05 19 04 b0 06
81 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
これがまた、めんどくさいコトになっています。
- CTAPHID_CBORコマンドとはデバイス(Yubikey)にCBORエンコードしたメッセージを送信するコマンドです。
- CBORとはJSONを短くすることに特化したエンコード方式のようで、なかなか独特なルールになっています。
- このCBORエンコードされたメッセージはなにかというと、これまたフォーマットが決まっていて、今回はauthenticatorGetInfoメッセージというものです。
- 以降でCTAPHID_CBORコマンドとauthenticatorGetInfoメッセージのフォーマットを説明します。
###CTAPHID_CBORコマンド(0x10)
項目 | サイズ | 説明1 | 説明2 |
---|---|---|---|
CID | 4 | INITで取得したCIDを指定する | |
CMD | 1 | 0x90 | 0x80|0x10⇒0x90 |
BCNTH | 1 | High part of payload length | 送信データのサイズ |
BCNTL | 1 | Low part of payload length | 送信データのサイズ |
DATA | x | Payload data | CBORエンコードされたauthenticatorGetInfoメッセージ |
####authenticatorGetInfoメッセージ(0x04)
Yubikeyのデバイス情報を取得するコマンド。
パラメータなし、単に 0x04 と1byteデータを送ればよい。
####応答パケット
CTAPHID_CBOR (0x10)コマンドに対してこんな応答が返ってきます。
initialization packet
項目 | サイズ | 値 | 説明 |
---|---|---|---|
CID | 4 | 0x00 1d 00 08 | 送信データと同じ |
CMD | 1 | 0x90 | 送信データと同じ |
BCNTH | 1 | 0 | |
BCNTL | 1 | 86 | データサイズ |
DATA | 57 | CBORエンコードされたauthenticatorGetInfo Response |
continuation packet
項目 | サイズ | 値 | 説明 |
---|---|---|---|
CID | 4 | 0x00 1d 00 08 | 送信データと同じ |
SEQ | 1 | 0x00 | 継続パケットNo |
DATA | 29 | CBORエンコードされたauthenticatorGetInfo Response |
で、CBORエンコードされたauthenticatorGetInfo Responseだけをまとめるとこうなります。
// payload len 86
00 a6 01 82 66 55 32 46 5f 56 32 68 46 49 44 4f
5f 32 5f 30 02 81 6b 68 6d 61 63 2d 73 65 63 72
65 74 03 50 f8 a0 11 f3 8c 0a 4d 15 80 06 17 11
1f 9e dc 7d 04 a4 62 72 6b f5 62 75 70 f5 64 70
6c 61 74 f4 69 63 6c 69 65 6e 74 50 69 6e f4 05
19 04 b0 06 81 01
で、これがすべてCBORエンコードというのは正しくなくて最初の1Byteだけ応答ステータスであり、2byteからCBORです。
さて、このCBORデータですが、CBOR Playgroundというサイトで一発でデコードできます。
A6 # map(6)
01 # unsigned(1)
82 # array(2)
66 # text(6)
5532465F5632 # "U2F_V2"
68 # text(8)
4649444F5F325F30 # "FIDO_2_0"
02 # unsigned(2)
81 # array(1)
6B # text(11)
686D61632D736563726574 # "hmac-secret"
03 # unsigned(3)
50 # bytes(16)
F8A011F38C0A4D15800617111F9EDC7D # "\xF8\xA0\x11\xF3\x8C\nM\x15\x80\x06\x17\x11\x1F\x9E\xDC}"
04 # unsigned(4)
A4 # map(4)
62 # text(2)
726B # "rk"
F5 # primitive(21)
62 # text(2)
7570 # "up"
F5 # primitive(21)
64 # text(4)
706C6174 # "plat"
F4 # primitive(20)
69 # text(9)
636C69656E7450696E # "clientPin"
F4 # primitive(20)
05 # unsigned(5)
19 04B0 # unsigned(1200)
06 # unsigned(6)
81 # array(1)
01 # unsigned(1)
↑・・・なんだかよくわかりませんね。いや、よく見ればわかるのですがわかりずらいですね。
これはデコード結果ではなく、CBORパース最中のログです。
{
1: ["U2F_V2", "FIDO_2_0"],
2: ["hmac-secret"],
3: h'F8A011F38C0A4D15800617111F9EDC7D',
4: {"rk": true, "up": true, "plat": false, "clientPin": false},
5: 1200,
6: [1]
}
↑やっと人間にもわかるようになりました。
ただ、これではまだ完全ではありません。
CTAP仕様書 6.2. Responses にこの意味が書いてあります。
まとめるとこうなるかと
項目 | key | 値 |
---|---|---|
versions | 1 | U2F_V2,FIDO_2_0 |
extensions | 2 | hmac-secret |
aaguid | 3 | F8A011F38C0A4D15800617111F9EDC7D |
options※ | 4 | rk, up, noplat, noclientPin |
maxMsgSize | 5 | 1200 |
pinProtocols | 6 | 1 |
~ ※optionsの詳細 ⇒ CTAP仕様書 5.4. authenticatorGetInfo (0x04) ~
#4. HIDをClose
省略!
#おつかれさまでした
なんとなくわかったきがする・・・
すぐわすれるきがする・・・
#参考