yubikey
FIDO
CTAP

CTAP2 お勉強メモ#2 - 接続(GetInfo)

はじめに

前回「CTAP2 お勉強メモ#1」の続き。
これはCTAP仕様とサンプルプログラムでCTAPのお勉強をしたメモであります。

目次

  1. サンプルプログラム概要
  2. HIDをOpenしてYubikeyと接続
  3. CTAPHID_INITコマンド
  4. CTAPHID_CBORコマンド
  5. HIDをClose

1.サンプルプログラム概要(info.exe)

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の説明

ここでの送受信データは以下の通り

CTAPHID_INIT_Packetログ
  // 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)

ここでの送受信データは以下の通り

CTAPHID_CBOR_Packetログ
  // 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メッセージのフォーマットを説明します。
    • ここではCBORの詳細説明はしません。あまりWebには情報がなく、日本語サイトではココしか見つかりませんでした。
    • CBORのエンコードはココでできるので、それで確認しておしまいにします。

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だけをまとめるとこうなります。

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です。

CTAP仕様書6.2. Responses
キャプチャ7.PNG

さて、このCBORデータですが、CBOR Playgroundというサイトで一発でデコードできます。

CBORデコードログ
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パース最中のログです。


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 にこの意味が書いてあります。
image.png

まとめるとこうなるかと

項目 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

省略!

おつかれさまでした

なんとなくわかったきがする・・・
すぐわすれるきがする・・・

CTAP2 お勉強メモ#3

参考