本記事は Digital Identity技術勉強会 #iddance Advent Calendar 2020 の3日目の記事です。
なんの話ですか?
FIDOの話です。
FIDOの話なんですが、WebAuthnっぽい入り口で、中身はCTAPの話です。
WebサイトでFIDOキーでログインする時のPINの検証プロトコルがどうなっているのかという話です。
PINの検証プロトコルはCTAPで定義されていて、これがやたらめんどくさくてすぐ迷い込んでしまうので少し整理してみました。
雰囲気理解で書いていますので 間違いなどあれば ご指摘、コメントいただければ嬉しいです
0.人 - ログイン
画面はWebAuthnのデモサイトです。
- ログインします
- ブラウザ『FIDOキーを挿入してください』
- FIDOキーをPCのUSBに刺します
ここからはじまります。
1.FIDOキー - パワーON!
FIDOキーが刺さりました。FIDOキーのパワーONです。
Authenticator KeyAgreementキー生成
- FIDOキーの中でキーペアが生成されます
- ECDHという形式の秘密鍵(a)と公開鍵(aG)です
- このキーをAuthenticator KeyAgreementキーといいます
PINトークン生成
- FIDOキーの中でランダムな16byteのデータが生成されます
- このデータをPINトークンといいます
このPINトークンをブラウザに引き渡すことがPIN認証となり、今回の話のゴールです。
2.ブラウザ - 共有鍵を作る
ブラウザはFIDOキーが刺されたことを検知しました。
Platform KeyAgreementキー生成
- FIDOキーと同じECDHのキーペアを生成します
- このキーをPlatform KeyAgreementキーといいます
- 秘密鍵(b)と公開鍵(bG)と表記します
共有鍵生成
- FIDOキーから KeyAgreementキーの公開鍵(aG) を取り出します
- CTAPの authenticatorClientPIN - getKeyAgreement というAPIです
- bとaGを使ってECDH鍵共有プロトコルというおまじないで計算します
- 計算=
SHA-256((baG).x)
- このデータをSharedSecret(共有鍵)といいます
3.人 - PINを入力する
- ブラウザ『PINを入力してください』
- PINを入力します
4.ブラウザ - PINをバラして暗号化する
PINが入力されました
-
PIN文字列をbyte配列にしてからハッシュを取ります
-
先頭の16byteを取り出します
LEFT(SHA-256(PIN),16)
-
取り出した16byteのデータを暗号化します
- 暗号化方式はAESです
- 一言でいうと
AES-256-CBC
- 一言でいうと
- 暗号化キーはさっき作ったSharedSecretです
- 暗号化方式はAESです
-
このデータをpinHashEncといいます
- pinHashEncはPINをバラしたものなので元のPIN文字列にモドすことはできません
-
ブラウザはPlatform KeyAgreementキーの公開鍵(bG)とpinHashEncをFIDOキーに送りつけます
- CTAPの authenticatorClientPIN - getPINToken というAPIです
5.FIDOキー - 検証
FIDOキーは送り付けられたデータを検証します
共有鍵生成
-
ブラウザから送り付けられた Platform KeyAgreementキーの公開鍵(bG) を取り出します
-
aとbGを使ってECDH鍵共有プロトコルというおまじないで計算します
- 計算=
SHA-256((abG).x)
- この計算結果が、なんと!ブラウザが作ったSharedSecretと同じになります
- 計算=
pinHashEnc生成
- ブラウザと同じ方法でFIDOキーに記録されているPINと先ほど生成したSharedSecretを使ってpinHashEncを生成します
検証
-
ブラウザから送られてきたpinHashEnc
=FIDOキーが生成したpinHashEnc
ならば検証OK!
PINトークン引き渡し
- 一番最初に生成したPINトークンを暗号化します
- 暗号化方式は一言でいうと
AES256-CBC
- 暗号化キーはさっき作ったSharedSecretです
- 暗号化方式は一言でいうと
- このデータをpinTokenEnc(暗号化されたPINトークン)といいます
-
pinTokenEncをブラウザに送り返します
- CTAPの authenticatorClientPIN - getPINToken の戻り値です
6.ブラウザ - PINトークンをGET
ブラウザはpinTokenEnc(暗号化されたPINトークン)をGETしました。
これをブラウザ自身が持つSharedSecretで復号してついにPINトークンをGETすることができました。PINトークンは PIN認証OK という証明書です。
- FIDOキーにPINトークンを提示して、認証の証明書(Assertion)の要求をします
- 単にPINトークンを提示するのではなくpinAuthという形式に変換して利用します
- pinAuth=
LEFT(HMAC-SHA-256(pinToken, clientDataHash), 16)
- CTAPの authenticatorGetAssertion というAPIです
7.人 - ログイン
- ブラウザ『キーをタッチしてください』
- キーをタッチします
- FIDOキーから本人認証の証明書(Assertion)をGETし、Assertionをサーバーで検証してOKであればログインします
- AssertionはCTAPの authenticatorGetAssertion の戻り値です
おしまい。
まとめ
- PINを検証するやりとりでサーバーは関与しない
- ブラウザとFIDOキーの間でPINそのものの送信は行わない
- ブラウザとFIDOキーの間ではPINを使って暗号化したデータを送るが、暗号化したデータからPINを復元することはできない
- 通信には共通の暗号化キー(SharedSecret)を使うが、SharedSecretを通信で交換しない
- SharedSecretはECDH鍵共有プロトコルを使うことでブラウザ/FIDOキーで同じ値を生成する
- いったんPIN認証したら引き渡されるPINトークンを使う
- PINトークンはFIDOを刺した時に毎回生成される
おつかれさまでした
このプロトコルを実装したライブラリはちゃんと動いているので 言葉よりソースコードで という方はこちらをご覧ください。
- ctap-hid-fid2(Rust)
- CTAPcs(C#)
参考
-
FIDO
- FIDOの仕組み
- https://fidoalliance.org/fido%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF/?lang=ja - FIDO2: WebAuthn & CTAP
- https://fidoalliance.org/fido2 - Client to Authenticator Protocol (CTAP)
Proposed Standard, January 30, 2019
- https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html
- FIDOの仕組み
-
SHA-256,AES,HMAC,ECDH(楕円曲線ディフィー・ヘルマン鍵共有)などの暗号について
- 暗号技術入門 第3版 秘密の国のアリス
- https://www.hyuki.com/cr
- 暗号技術入門 第3版 秘密の国のアリス