LoginSignup
7
2

More than 3 years have passed since last update.

CTAP2 お勉強メモ#4 - 認証(Authentication)

Last updated at Posted at 2018-11-03

はじめに

これはCTAPのお勉強をしたメモです。
WebAuthn(ウェブオースン)ではなく、CTAP(シータップ)であります。

随時修正します。

#3ではauthenticatorMakeCredentialコマンドでYubikeyに登録、クレデンシャルID(Credential ID)認証用公開鍵(Credential Public Key)をゲットしました。

今回はこれらの情報を使って認証をするお勉強メモです。

環境

  • OS=Windows10 1803
  • 開発環境=Visual Studio Pro 2017(15.8.5)
    • Microsoft Visual C++ 2017
  • Yubico セキュリティキー(青)

※キーはリセットしてPINが設定されていないものを使います、PINを設定してしまうと、pinAuthの話をしないといけなくて、その話をするとすごく長くなるのでやめています。pinAuthの話は #5 - PIN をみてください。

キャプチャ2.PNG

認証(Authentication)

image.png

※絵はこちらからピックアップさせていただきました。
https://developer.mozilla.org/ja/docs/Web/API/Web_Authentication_API

目次

  1. サンプルプログラム概要
  2. authenticatorGetAssertion(0x02) コマンド
  3. Assertion(Response)
  4. Verify(検証)

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

assert.exeの処理シーケンスです。

  • 認証用公開鍵とYubikeyのHIDパスの指定は必須
  • -aオプションでauthenticatorMakeCredentialコマンドで作成したクレデンシャルIDファイル(creid.dat)を指定
>assert.exe -a c:\work\cred_out\creid.dat c:\work\cred_out\pubkey.pem \\?\hid#vid_1050&pid_0120#6&1b5e4874&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}

GitHubで公開されているソースはこちら

2. authenticatorGetAssertion(0x02) コマンド

Yubikey(Authenticator)内のクレデンシャル情報を取得するコマンドです。
クレデンシャル情報とは、秘密鍵を持っていることを証明する情報で、Assertionと名付けられています。
サンプルプログラムでは 以下のパラメータを指定しています。

  • rpid
  • clientDataHash
  • allowList
  • options
authenticatorGetAssertionコマンドのパラメータを見やすく整形したもの
    // 0x01 rpid
    1: "localhost",

    // 0x02 clientDataHash
    2: h'EC8D8F78424A2BB78234AACA07A1F656421CB6F6B3008652352DA2624ABE8976', 

    // 0x03 allowList
    3: [{
        "type": "public-key",
        "id": h'C52D9FDEBA951F4ACA94C2CB7DCFB47B13D0E4D676E2491F90D081D65E6995C74A77828BF8DFE239FC1D62D93DDB547827C524A2A3C035A27D4041DB8352F87E'
        }],

    // 0x05 options
    5: {"up": false, "uv": false}

ざっくりコマンド解説

ここで書いたように、クレデンシャルはrpidをキーにして保存されています。

なので、rpidを指定さえすればAssertionをGETできるかというと、そうではないんです。
allowListでCredentialIDも指定しないといけません。
⇒rpidだけででAssertionをGETすることはできません。

別の方法として、options.uv=trueにしてさらにpinAuth、pinProtocolを指定すると、CredentialIDの指定無しでAssertionをGETできます。
ただし、これは登録時にrk=trueを指定してuser情報をYubikeyに保存していることが前提条件となります。(複雑!)

上記記載は誤り、正しくは
- allowListでCredentialIDを指定して取得する方法とallowList無しで取得する方法がある。
- allowListでCredentialIDを指定すると、CredentialIDに紐づいたAssertion(1個)をGETすることができる。
- allowList無しの場合は、登録時にrk=trueでuser情報をYubikeyに保存して登録されたAssertion(1個以上)をGETすることができる。
- allowList無しの場合はAssertionが複数個取れる場合があるので注意。詳細は#6を参照。

clientDataHash⇒いわゆるチャレンジです。クレデンシャルを特定するためのパラメータではなく、GETしたクレデンシャルをVerifyするためのパラメータです。

rpid

Relying Party Identifierってことで、要はサーバーのドメインを指定します。
authenticatorMakeCredentialの時に指定したものを指定する必要があります。

試しに、"localhost"を"gebogebo.com"に変えてコマンドを送ってみると。
FIDO_ERR_INVALID_CREDENTIAL (0x22)
となり、エラーとなります。
rpidに問題がある、といった意味合いのエラーは定義されてないのか、ハマりそうだ・・・

clientDataHash

これは #3 と同じ要領で作成するのである、という理解が正解なのだけれども、もっとシンプルに考えて、乱数をSHA256(32byte)したchallenge、ということでも問題ないんだろうと思います。(CTAP的には)。
それで、Assertionにはこのchallengeの署名が返ってくるので、それをVerifyして真正性を確認するのです。

allowList

AllowListクレデンシャルを選択するためのパラメータです。
仕様書にはallowCredentialDescriptorListと書いてあるのですが、どんな形式で何をしていするのか、明確な記載が見つかりません…

とりあえず仕様書のEXAMPLEによるとこんな感じ
image.png
で、サンプルプログラムでも
"type":"public-key"⇒固定値
"id"⇒CredentialIDのバイナリデータをHEX変換した文字列
をつっこんでいます。
配列で複数個指定可能なのはallowListという名前で分かるのですが、クレデンシャルIDを指定するのに、typeにpublic-keyを指定するのが理解不能…

options

動作オプションです。
仕様書よりピックアップ
image.png

  • サンプルasser.exeでは-pを指定するとup=trueで動作します。
  • up=trueのときは、Yubikeyがピカピカ光ってタッチするとAssertionが返ってくるようになります。(ユーザーの存在確認)
  • サンプルasser.exeでは-vを指定するとuv=trueで動作します。
  • uvをtrueにしても、特に動作に変更はないのですが、後述のAssertionのFlagでuv結果を受け取ります。uvの指定値と結果値を見て、本当にuvされたかをチェックします。結果値のuvをtureにするためには、PIN認証が必要になり、芋づる式にpinAuth、pinProtocolの指定も必要になります。(ここはもしかしたらCTAPというよりもYubikeyの仕様なのかもしれません)

3.Assertion(Response)

コマンドのResponseのことです。仕様書等を見るとAssertionと名付けられています。

Assertionは以下のデータから構成されます。

  • credential
  • authData
  • signature

CBORデコードしたAssertion

Assertionを見やすく整形したもの
{
    // 0x01:credential
    1: {
        "id": h'C52D9FDEBA951F4ACA94C2CB7DCFB47B13D0E4D676E2491F90D081D65E6995C74A77828BF8DFE239FC1D62D93DDB547827C524A2A3C035A27D4041DB8352F87E', 
        "type": "public-key"
        }, 

    // 0x02:authData
    2: h'49960DE5880E8C687434170F6476605B8FE4AEB9A28632C7995CF3BA831D97630000000068', 

    // 0x03:signature
    3: h'3044022065E5E681837AEDE320D601BE7F82533976F437A5A1066B12EB4D1CAE870E7C32022035E160160AE4B014B81EDA0C264F1F8863A5D5C600EE8B482AB10E52DAC690CB'
}

Assertionについて

#3のAttestationに比べたらだいぶマシです。
取得したクレデンシャル情報です。

credential

Credential ID。GetしたクレデンシャルのIDですね。
送信したコマンドのAllowListで指定したものと同じです。希望したものがとれたということで。

authData

#3 とフォーマットは同じですが、Attested credential data部が無く、以下の情報のみです。

  • rpIdHash
  • flags
  • signCount

signature

署名です。Verifyに使います。

4.Verify(検証)

さて、Verifyです。
Assertionが正しいものかどうか、Verifyします。
基本的には#3と同じです。安心しました(?)

  • Verify-1.Assertionに含まれるflagsを検証
  • Verify-2.送信したRPIDとAssertionに含まれるRPIDハッシュを検証
  • Verify-3.署名検証

Verify-1とVerify-2は #3 と同じなので省略

Verify-3.署名検証

※大まかには#3と同じです。

SigVerifyに必要なデータは

  • Sig
  • Verify用Public key
  • 署名対象データ

この3つがそろえばVerifyできます。
今回もコマンドでVerifyをやってみてお勉強したいと思います。

以下の図はWebauthn仕様書。(こうやって署名されているよ、ってことです)

◆Sig

Attestationのsignatureです。

  • ダンプログからsignatureを取り出してassert_sig.hexというファイルで保存する
  • certutilでassert_sig.hexをバイナリ assert_sig.bin に変換しておく
c:\work\assert>certutil -f -decodehex assert_sig.hex assert_sig.bin 4
入力長 = 140
出力長 = 70
CertUtil: -decodehex コマンドは正常に完了しました。

◆Verify用Public key

  • 認証用公開鍵です。
  • #3で作成したものです。
  • cred_pubkey.pem というファイル名で持っています。

◆署名対象データ

authenticatorData + clientDataHash ⇒ 署名対象データ
ですね。
#3と同じ要領でassert_dgst.binを作成します。

c:\work\assert>certutil -f -decodehex assert_dgst.hex assert_dgst.bin 4
入力長 = 138
出力長 = 69
CertUtil: -decodehex コマンドは正常に完了しました。

◆Verify

材料がそろったらopensslのコマンド一発です。

c:\work\assert>openssl dgst -sha256 -verify cred_pubkey.pem -signature assert_sig.bin assert_dgst.bin
Verified OK

おつかれさまでした

基本的なところはなんとなくひととおりわかったきがする。

7
2
4

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
  3. You can use dark theme
What you can do with signing up
7
2