CTAP2.2 の Hybrid transports
Digital Identity技術勉強会 #iddance Advent Calendar 2023 21 日目の記事です。
Client to Authenticator Protocol (CTAP) Review Draft, March 21, 2023 の 11.5. Hybrid transports に書いてある内容を整理しました。
ざっくり
この仕様はPC などのクライアントプラットフォームに対してスマートフォンなどの別のデバイスを認証器として使うときの仕様で、それぞれ QR-initiated Transactions と State-assisted Transactions に分かれています。
QR-initiated Transactions
- 接続したことがないクライアントプラットフォームと認証器の間で QR コードを介して接続するフロー
- クライアントプラットフォームに QR コードを表示し、それを認証器側のデバイスでスキャンする
- スマートフォンは BLE アドバタイズを行い、ここまでにやり取りした情報を使って双方のデバイスが tunnel service 経由で WebSocket によってやりとりできるようになる
- この接続を通じて CTAP メッセージのやりとりをすることで、クライアントプラットフォームは認証器にクレデンシャルの作成を命令したり、認証器は作成したアテステーションをクライアントプラットフォームに送信したりすることができる
State-assisted Transactions
- QR-initiated Transactions で接続した際に交換した接続情報を使って QR コードを使わずにやり取りするフロー
- QR-initiated Transactions を一度行ったクライアントプラットフォームと認証器の間で再度やりとりをする際に利用する
- 以前の接続で交換済みの接続情報を使って、クライアントプラットフォームは tunnel service にアクセスする
- tunnel service は認証器にアクセスする
- スマートフォンは BLE アドバタイズを行う
- ここまでに取得した情報を使って双方がハンドシェイクを行い、双方のデバイスが tunnel service 経由で WebSocket によってやりとりできるようになる
- 以降は QR-initiated Transactions と同様
tunnel service とは
クライアントプラットフォームが認証器とやりとりするために利用するサービスで、認証器を提供するプラットフォーマーによって提供されています。例えば認証器が Android であれば Google に、iPhone であれば Apple に "cable.ua5v.com" や "cable.auth.com" といったホストで提供されています。仕様上 tunnel service は認証器の提供元が自分でホストすることもできるようになっているようです。
プラットフォームと tunnel service の接続はこの仕様で定義されていますが、認証器と tunnel service 間の通信は各プラットフォームの実装依存となります。こちらは参考になるものも特になさそうです。
QR-initiated Transactions の流れ
QR コード
クライアントプラットフォームは認証器が読み取るための QR コードを表示します。この QR コード以下のフィールドを持つ CBOR にエンコードされています。
key | value | desc |
---|---|---|
0 | 公開鍵 | トンネル経由でクライアントプラットフォームと認証器がハンドシェイクを行うのに使う |
1 | QR secret | BLE advertise の暗号化、復号、認証に使う |
2 | クライアントプラットフォームが知ってる tunnel service のドメインの数 ※1 | これが 2 だったらこのプラットフォームは cable.ua5v.com と cable.auth.com を知っていることになり、後述の BLE advert で 0 を指定すれば前者に、1 を指定すれば後者を利用することが表現できる |
3 | 現在時刻 | |
4 | プラットフォームが state-assisted transactions に対応しているかどうか | |
5 | 今後行われる操作についてのヒント(認証 or 登録) |
- ※1 なぜこういうフォーマットなのかよくわからない
- ※1
cable.ua5v.com
だけ知ってる、はできるのにcable.auth.com
しか知らないプラットフォームを作れないように見えるのがなんか変な気がする - ※1 wellknown なものは各プラットフォームが全部サポートするのが前提なんだろうか
認証器による QR コードの読み取りと BLE advert
クライアントプラットフォームは認証器からの接続を待機し、認証器は BLE advertise を行います。これは認証器がクライアントプラットフォームの近くにあることを担保するために行われます。
これによって攻撃者が自分のページに、あるサイトに対して認証を行うための QR コードを表示してユーザーがそれをスキャンした場合、攻撃者の PC と被害者のスマートフォンが tunnel service を通じて接続されます。このとき、被害者のスマートフォンが発行した RP 向けのアサーションやアテステーションを攻撃者が取得してしまう、といった状況を回避します。
この BLE advert は暗号化されていて、認証、復号のための値は HKDF を使って QR secret から導出されます。
BLE advert には以下のフィールドがあります。
value | desc |
---|---|
nonce | |
routing ID | tunnel service に接続する際にわたす |
tunnel service identifier | 利用する tunnel service を決定するための値 |
この値によって利用する tunnel service が決定されます。
tunnel service の決定
tunnel service identifier によって利用する tunnel service が決定されます。
この値が 256 未満の場合、well known な tunnel service を利用することを意味します。
つまり、0 であれば cable.ua5v.com
、1 であれば cable.auth.com
が利用されます。
256 以上の場合、この値をあるルール(導出のための Go のコードが仕様に記載されています)でハッシュ化した値がドメインとして利用されます。
例えば 256 であれば cable.qz2ekwmnd332c.info
、257 であれば cable.4a6bwmj6hiyyd.net
のようになります。任意の tunnel service を利用する認証器を実装する場合、適当な数字で空いているドメインを取得してホストすればよいのではないでしょうか。
tunnel service への接続
クライアントプラットフォームが tunnel service へ接続する準備ができたので、接続します。
まず tunnel ID を導出します。これは tunnel service のコネクションを一意に特定する ID のようなものです。この値も QR secret から導出されます。
その後以下のような URL で WebSocket によって tunnel service への接続を行います。
wss://cable.example.com/cable/connect/{routing id}/{tunnel id}
WebSocket の subprotocol identifier は fido.cable
となります。このとき、認証器側も tunnel service に接続しに行きますがこちらの接続については具体的な記述がありません。認証器と tunnel service の提供元が同じなので独自の仕組みで認証したりするんでしょうか。
この時点でトンネルが確立し、クライアントプラットフォームと認証器は WebSocket フレーム でのやり取りができるようになります。
ハンドシェイク
トンネルを通じてクライアントプラットフォームと認証器は Noise KNpsk0 暗号のハンドシェイクを行います。
このとき、クライアントプラットフォームが BLE advert を受信したことを証明するためまず最初にハンドシェイクのメッセージを送信し、認証器はそれにレスポンスを返します。
KNpsk0 は以下の定義になっています。
-> s
...
-> psk, e
<- e, ee, se
- s
- QR コード経由で共有したクライアントプラットフォームの公開鍵
- psk
- QR secret と復号した BLE advert から導出
- e
- 双方が動的に生成する鍵
PSK で認証しながら「e 同士」「クライアントプラットフォームの公開鍵と e」 でそれぞれ鍵交換を行い、これらの鍵から通信に利用する鍵を導出します。
以降のメッセージはハンドシェイクで導出した鍵によって暗号化されます。
認証器側からの最初のメッセージは post handshake
で、これには getInfo のレスポンス、つまり認証器の情報(aaguid とか対応してるアルゴリズムとかトランスポートとか)が含まれています。
これ以降のメッセージにはメッセージの type が含まれるようになり、CTAP のコマンドは CTAP message type のメッセージによって送信されます。
key | type | desc |
---|---|---|
0 | shutdown | クライアントから認証器にのみ送られる クライアントがこれ以上 CTAP メッセージを送らないことを表す |
1 | CTAP | CTAP2 payload |
2 | update | 双方から送られるが、現状認証器からクライアントプラットフォームに向けての値のみ定義されている |
クライアントプラットフォームは CTAP2 コマンドを認証器に送って何らかのアクションを送ります。getInfo はすでに受信しているので例えば登録の場合 authenticatorMakeCredential を送ると認証器がクレデンシャルを作成してアテステーションをレスポンスしてくるのでそれを使ってよしなに登録処理を進めるといった感じになります。
update メッセージ
update メッセージには CBOR で linking information が含まれていて、この linking information は以下となっています。
key | value | desc |
---|---|---|
1 | contact ID | tunnel service が認証器を特定するのに使う State-assisted Transactions で クライアントプラットフォームがこの値を指定して tunnel service にアクセスする Android の場合は FCM Registration token |
2 | link ID | この link を認証器が識別する値 |
3 | link secret | 共通鍵 |
4 | 認証器の公開鍵 | |
5 | 認証器の名前 | ユーザーが hybrid を選択したときに選択する Pixel 6a みたいなやつ |
6 | ハンドシェイクの署名 | linking information に含まれる公開鍵の保持を証明するための署名 |
これらの情報は State-assisted Transactions で利用されます。
State-assisted Transactions の流れ
State-assisted Transactions では認証器との接続に以前 QR-initiated Transactions で交換した情報を使うので QR の読み取りを利用しません。
tunnel service への接続
クライアントプラットフォームは認証器と紐づけてドメインと contact id を持っておき、このエンドポイント経由での認証器への接続を試みます。
wss://cable.example.com/cable/contact/${contact id}
一方認証器はどのクライアントプラットフォームと接続するかを知るための link ID とクライアントプラットフォームによる nonce を必要とします。これらの値は X-caBLE-Client-Payload
ヘッダ経由でクライアントプラットフォームから送信されます。ただしこちらについても認証器側の具体的な接続手順は仕様外です。
トンネルに接続できたら認証器はハンドシェイクメッセージを送信します。
認証器による BLE advert
この後認証器は近接性の検証のため BLE advert を行います。この BLE advert は link secret と tunnel service 経由で認証器に送信した nonce によって復号できます。
ハンドシェイク
クライアントプラットフォームは link secret と BLE advert に含まれる nonce からハンドシェイクに使う PSK を導出します。
PSK および linking information に含まれる公開鍵を使ってハンドシェイクを行います。この際のハンドシェイクは NKpsk0 となります。
<- s
...
-> psk, e, es
<- e, ee
- s
- update message で共有した認証器の公開鍵
- psk
- link secret と復号した BLE advert から導出
- e
- 双方が動的に生成する鍵
ハンドシェイクが完了したらあとは QR-initiated Transactions と同様に CTAP メッセージのやりとりを行ってアテステーションやアサーションの作成を依頼します。
シーケンス
値が色々出てくるのでシーケンスをまとめます。通信方法に関わらず矢印は「データを送る側->データを受け取る側」です。
QR-initiated Transactions
Tunnel Service への接続まで
1. QR コード
Client Platform -> Authenticator
value | 出どころ |
---|---|
クライアントプラットフォームの公開鍵 | クライアントプラットフォームが保持 |
QR secret | クライアントプラットフォームが生成 |
クライアントプラットフォームが知ってる tunnel service のドメインの数 | クライアントプラットフォームが保持 |
2. BLE advert
Client Platform <- Authenticator
BLE advert の復号は QR secret から導出した値を使って行う。
value | 出どころ |
---|---|
routing ID | 認証器が決定 |
tunnel service identifier | 認証器が保持 |
3. wss://cable.example.com/cable/connect/{routing id}/{tunnel id}
Client Platform -> Tunnel Service
value | 出どころ |
---|---|
接続先ホスト | BLE advert で受け取った tunnel service identifier とクライアントプラットフォームが知っている tunnel service のホストの一覧から判定 |
routing ID | BLE advert で受け取った値 |
tunnel id | QR secret から導出 |
Tunnel Service への接続後
1. Handshake message
Client Platform -> (Tunnel Service) -> Authenticator
value | 出どころ |
---|---|
PSK | QR secret と復号した BLE から導出 |
ephemeral key | ランダム生成 |
2. Handshake message with getInfo の結果
Client Platform <- (Tunnel Service) <- Authenticator
value | 出どころ |
---|---|
ephemeral key | ランダム生成 |
getInfo の結果 | 認証器が保持 |
この時点で双方が通信に使う鍵が共有される。
3. update message
Client Platform <- (Tunnel Service) <- Authenticator
このメッセージは仕様上タイミングが定義されていないので CTAP message のあとかも
key | 出どころ |
---|---|
contact ID | 認証器が決定? |
link ID | 認証器が決定? |
link secret | 認証器が生成? |
認証器の公開鍵 | 認証器が保持 |
ハンドシェイクの署名 | 認証器の秘密鍵で署名 |
4. shutdown message
Client Platform -> (Tunnel Service) -> Authenticator
これを受けて認証器は接続を切る。(切らなくてもいい)
State-assisted Transactions
Tunnel Service への接続まで
1. wss://cable.example.com/cable/contact/${contact id}
Client Platform -> Tunnel Service
value | でどころ |
---|---|
contact id | 以前の接続時に受信した update message |
link id | 以前の接続時に受信した update message |
nonce | ランダムに生成 |
2. BLE advert
Authenticator Client Platform
BLE advert の復号は link secret と 1 で送信した nonce から導出した鍵によって行う。
value | でどころ |
---|---|
nonce | ランダムに生成 |
Tunnel Service への接続後
1. Handshake message
Client Platform -> Authenticator
value | でどころ |
---|---|
PSK | link secret と複合した BLE から導出 |
ephemeral key | ランダム生成 |
2. Handshake message with getInfo の結果
Client Platform -> Authenticator
value | 出どころ |
---|---|
ephemeral key | ランダム生成 |
getInfo の結果 | 認証器が保持 |
この時点で双方が通信に使う鍵が共有される。
Discussion