# FIDO2認証の調査
FIDO2は公開鍵暗号を利用して従来のパスワード認証より強力な認証を行うための規格である。
## FIDO規格の歴史
FIDO AllianceはU2FとUAFを規格化した。
U2Fは二段階認証手段で、UAFはパスワードレス認証を行う規格である。
これらを統合した結果、FIDO2規格が生まれた。
## 特徴
- 非常に安全
- パスワード不要
- 外部認証器またはTPMを使うので安全
- 外部認証器またはTPMがなければ使えない
- 鍵がウェブサイトごとに異なるものを使う
- 主要ブラウザ最新版はすべて対応している
## 対応認証器
- Windows Hello
- Apple Touch ID/Face ID
- Android画面ロック
- YubiKey
- Trezor
- Ledger
- Google Titan
## API
公開鍵ベースの認証、パスワード認証が利用できる。前者はデバイスのTPMやYubikey, Trezorなどの外部認証器が使える。後者はWIP?
外部の認証器との通信はBluetooth, ISO7816.ISO14443, USB HIDの通信方式における送信方法がCTAPによって定義される
### 新規登録フロー
`navigator.credentials.create(options?: PublicKeyCredentialCreationOptions)`
- 認証先のウェブサイト
- 認証に使うユーザー情報
- 公開鍵暗号方式
- COSEのアルゴつかえる
- RSAまたはECDSAが既定
- チャレンジ
などを送ると、鍵を生成・記憶して、
- rawId
- clientDataJSON
- クライアントデータ
- このJSONが署名文?
- attestationObject
- 電子署名が含まれたCBOR
### 利用フロー
`navigator.credentials.get(options?: PublicKeyCredentialRequestOptions)`
createのときと同様の形式で、createの時と同じ鍵でチャレンジを署名して返す。
### 検証
#### 環境
- Windows 11
- Firefox 97
- AMD Ryzen 9 3950X (AMD PSP TEE内臓)
- 外部TPM非接続
Windows Helloによる認証を行った。認証に使った方法はTPM(Windowsにログインするときに使うPIN)である。
### 結果
https://codesandbox.io/s/cocky-lumiere-gyhrky
```
PublicKeyCredential {getClientExtensionResults: ƒ getClientExtensionResults(), rawId: ArrayBuffer, response: AuthenticatorAttestationResponse, id: "zYnIjuB0EIXW-G1XOo4ZMxho6XDXOxv8D4_zm_98ZwY", type: "public-key"…}
{}
{"challenge":"jAom_w","clientExtensions":{},"hashAlgorithm":"SHA-256","origin":"https://gyhrky.csb.app","type":"webauthn.create"}
Uint8Array {0: 163, 1: 99, 2: 102, 3: 109, 4: 116…}
a363666d74646e6f6e656761747453746d74a0686175746844617461590167eb94a11634db6334bf23ec0d3a8d9858509c5fe6ec6832b71314a3818f677c864500000000000000000000000000000000000000000020cd89c88ee0741085d6f86d573a8e19331868e970d73b1bfc0f8ff39bff7c6706a401030339010020590100dc7b6fb382e1df4918822c0fc1c2241b34a7fa1d15f8cc4087202ba5bdcf55667ac94434a08c1eae945beb050ac9e8cc1777890819fe77031e6be67439b5d3ba8969560f6e40fe96a740f370d8c0565fc3eb594b2a56a4cf68494c448c5a34c40c3d7be46ecacf6eb0193e3cf7c89e3fcf3da3f62748dbb0ddf7989d62ba0cf7d1dd4c54406b8c5a7766b4e5b9b7d2c2a59c6b050dd7b79c80f0b43052005530b11c613c24d066dc621d8611f1e9d163da10c94300abec70867492d081a10811ecb4c0fb8c68fa3a425493c9153ce5d6615e869b18a79c5fb8d9b2e3fde43c465e1eff82adf1107a90b0db66929e72bbb50cc3547ddc1efb593691c2afaa26172143010001
PublicKeyCredential {getClientExtensionResults: ƒ getClientExtensionResults(), rawId: ArrayBuffer, response: AuthenticatorAssertionResponse, id: "zYnIjuB0EIXW-G1XOo4ZMxho6XDXOxv8D4_zm_98ZwY", type: "public-key"…}
{}
{"challenge":"jAom_w","clientExtensions":{},"hashAlgorithm":"SHA-256","origin":"https://gyhrky.csb.app","type":"webauthn.get"}
Uint8Array {0: 235, 1: 148, 2: 161, 3: 22, 4: 52…}
eb94a11634db6334bf23ec0d3a8d9858509c5fe6ec6832b71314a3818f677c860500000001
```
実行を繰り返しても、同じ署名文が返ってきた。
RS256を使っている。pubKeyCredParamsからRS256を除いたら、PIN入力画面が表示されず、セキュリティキーの入力を求められた。
## これを使えるか?
これに使う電子署名アルゴリズム・暗号文フォーマットを受容するスマートコントラクト等があれば不可能ではなさそうである。
しかし、任意の暗号文を設定してSecp256k1署名をできないので、既存のブロックチェーンの電子署名には使えなさそうである。
また、対応する署名アルゴが複数あり、認証器によって対応するものが異なるため(TrezorはES256のみ, Windows HelloはRS256のみ)、アルゴ対応数増やすと大変である。
また、出力された電子署名を秘密鍵として扱いECDSA鍵ペアを導出するのもやめたほうがよい。
Windows 11環境では決定論的署名であるようだが、そのほかの認証器がそうであるという保証はない(もしかしたらCTAPに定義してあるかも)ので、同じ鍵ペアを導出できるかどうかわからないからである。
## 参考文献
https://github.com/trezor/trezor-firmware/blob/319d0f16e33892d0f9c5fc4fd22186bf5e75acb8/core/src/apps/webauthn/fido2.py#L1783
https://www.w3.org/TR/webauthn
https://www.iana.org/assignments/cose/cose.xhtml#algorithms