LoginSignup
6

More than 1 year has passed since last update.

posted at

updated at

FIDOのPIN認証のフローを理解する

本記事は Digital Identity技術勉強会 #iddance Advent Calendar 2020 の3日目の記事です。

なんの話ですか?

FIDOの話です。

FIDOの話なんですが、WebAuthnっぽい入り口で、中身はCTAPの話です。
WebサイトでFIDOキーでログインする時のPINの検証プロトコルがどうなっているのかという話です。

PINの検証プロトコルはCTAPで定義されていて、これがやたらめんどくさくてすぐ迷い込んでしまうので少し整理してみました。

01.png

雰囲気理解で書いていますので 間違いなどあれば ご指摘、コメントいただければ嬉しいです

0.人 - ログイン

画面はWebAuthnのデモサイトです。

  • ログインします

06.png

  • ブラウザ『FIDOキーを挿入してください』

03_Insert.png

  • FIDOキーをPCのUSBに刺します

04.png





ここからはじまります。




1.FIDOキー - パワーON!

FIDOキーが刺さりました。FIDOキーのパワーONです。

Authenticator KeyAgreementキー生成

  • FIDOキーの中でキーペアが生成されます
  • ECDHという形式の秘密鍵(a)と公開鍵(aG)です
  • このキーをAuthenticator KeyAgreementキーといいます

PINトークン生成

  • FIDOキーの中でランダムな16byteのデータが生成されます
  • このデータをPINトークンといいます

05.png




このPINトークンをブラウザに引き渡すことがPIN認証となり、今回の話のゴールです。




2.ブラウザ - 共有鍵を作る

ブラウザはFIDOキーが刺されたことを検知しました。

Platform KeyAgreementキー生成

  • FIDOキーと同じECDHのキーペアを生成します
    • このキーをPlatform KeyAgreementキーといいます
    • 秘密鍵(b)と公開鍵(bG)と表記します

07.png

共有鍵生成

08.png

  • baGを使ってECDH鍵共有プロトコルというおまじないで計算します
  • 計算=SHA-256((baG).x)
    • このデータをSharedSecret(共有鍵)といいます

09.png

3.人 - PINを入力する

  • ブラウザ『PINを入力してください』
  • PINを入力します

10.png

4.ブラウザ - PINをバラして暗号化する

PINが入力されました

  • PIN文字列をbyte配列にしてからハッシュを取ります
  • 先頭の16byteを取り出します

    • LEFT(SHA-256(PIN),16)
  • 取り出した16byteのデータを暗号化します

    • 暗号化方式はAESです
    • 一言でいうと AES-256-CBC
    • 暗号化キーはさっき作ったSharedSecretです
  • このデータをpinHashEncといいます

    • pinHashEncはPINをバラしたものなので元のPIN文字列にモドすことはできません

11.png

12.png

5.FIDOキー - 検証

FIDOキーは送り付けられたデータを検証します

共有鍵生成

  • ブラウザから送り付けられた Platform KeyAgreementキーの公開鍵(bG) を取り出します

  • abGを使ってECDH鍵共有プロトコルというおまじないで計算します

    • 計算=SHA-256((abG).x)
    • この計算結果が、なんと!ブラウザが作ったSharedSecretと同じになります

13.png

pinHashEnc生成

  • ブラウザと同じ方法でFIDOキーに記録されているPINと先ほど生成したSharedSecretを使ってpinHashEncを生成します

14.png

検証

  • ブラウザから送られてきたpinHashEnc = FIDOキーが生成したpinHashEnc ならば検証OK!

15.png

PINトークン引き渡し

  • 一番最初に生成したPINトークンを暗号化します
    • 暗号化方式は一言でいうとAES256-CBC
    • 暗号化キーはさっき作ったSharedSecretです
  • このデータをpinTokenEnc(暗号化されたPINトークン)といいます

16.png

17.png

6.ブラウザ - PINトークンをGET

ブラウザはpinTokenEnc(暗号化されたPINトークン)をGETしました。

これをブラウザ自身が持つSharedSecretで復号してついにPINトークンをGETすることができました。PINトークンは PIN認証OK という証明書です。

18.png

  • FIDOキーにPINトークンを提示して、認証の証明書(Assertion)の要求をします
    • 単にPINトークンを提示するのではなくpinAuthという形式に変換して利用します
    • pinAuth= LEFT(HMAC-SHA-256(pinToken, clientDataHash), 16)
    • CTAPの authenticatorGetAssertion というAPIです

20.png

7.人 - ログイン

  • ブラウザ『キーをタッチしてください』
  • キーをタッチします

19.png

  • FIDOキーから本人認証の証明書(Assertion)をGETし、Assertionをサーバーで検証してOKであればログインします

21.png

22.png





おしまい。




まとめ

  • PINを検証するやりとりでサーバーは関与しない
  • ブラウザとFIDOキーの間でPINそのものの送信は行わない
  • ブラウザとFIDOキーの間ではPINを使って暗号化したデータを送るが、暗号化したデータからPINを復元することはできない
  • 通信には共通の暗号化キー(SharedSecret)を使うが、SharedSecretを通信で交換しない
  • SharedSecretはECDH鍵共有プロトコルを使うことでブラウザ/FIDOキーで同じ値を生成する
  • いったんPIN認証したら引き渡されるPINトークンを使う
  • PINトークンはFIDOを刺した時に毎回生成される

おつかれさまでした

このプロトコルを実装したライブラリはちゃんと動いているので 言葉よりソースコードで という方はこちらをご覧ください。

参考

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
6