LoginSignup
4
2

More than 3 years have passed since last update.

CTAP2 お勉強メモ#9 - CTAP 2.1 PRE authenticatorCredentialManagement

Posted at

はじめに

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

今回はCTAP2.1(Review Draft)の authenticatorCredentialManagement です。

CTAP 2.1 PRE ?

今の CTAP 2.0 に機能拡張を加えた仕様で Review Draft です。なので正式版は仕様が変わる可能性があります。
とはいえ 今世の中に出回っているセキュリティキーで既にこれを実装しているものがありまして authenticatorGetInfoFIDO_2_1_PRE という version が採れるものがそれになります。
このセキュリティキーを使って authenticatorCredentialManagement を検証しました。

サンプルコード

Rustで実装したクレートとサンプルコードがあります。コード見たほうが早いという人はこちら。

authenticatorCredentialManagement

教科書 - 6.8 authenticatorCredentialManagement (0x0A)

authenticatorCredentialManagement は Residentkey で記録されたセキュリティキー内のクレデンシャルデータを管理するコマンドです。

memo : 正式には authenticatorCredentialManagement を表すコマンドは 0x0A ですが、FIDO_2_1_PRE では 0x41です。そのへんの説明は 仕様書 6.13 に書いてあります。

以下のようなサブコマンド形式になっています。

subCommand Name subCommand Number
getCredsMetadata 0x01 クレデンシャル数を取得する
enumerateRPsBegin 0x02 RP情報を取得する
enumerateRPsGetNextRP 0x03 RP情報を取得する
enumerateCredentialsBegin 0x04 クレデンシャルを取得する
enumerateCredentialsGetNextCredential 0x05 クレデンシャルを取得する
deleteCredential 0x06 クレデンシャルを削除する
updateUserInformation 0x07 クレデンシャルのユーザーデータをUpdateする

getCredsMetadata

セキュリキーに記録可能なクレデンシャルの最大数と現在記録されているクレデンシャルの数を取得します。

subCommand (0x01) に getCredsMetadata(0x01) を指定して、pinUvAuthProtocol(0x03)、pinUvAuthParam(0x04)を設定してCBORを投げると応答が返ってきます。

Parameters

authenticatorCredentialManagement の各サブコマンドにはpinUvAuthProtocol、pinUvAuthParamを指定する必要があります。これはPINの情報で、取得する情報がクレデンシャルなので当然といえば当然ですね。この生成シーケンスがめんどくさいです。

pinUvAuthProtocol

PIN/UV Auth プロトコルを指定します。簡単にいうとPINをどんなふうに暗号化しているかです。
仕様では 1 (6.5.6. PIN/UV Auth Protocol One) と 2  (6.5.7. PIN/UV Auth Protocol Two) がありまして。
どっちでもいいというわけではなくて、セキュリティキーに authenticatorGetInfo 投げて 採れる pin_uv_auth_protocols の値です。
私の持っているセキュリティキーの場合は 1(Protocol One) でした。

pinUvAuthParam

仕様では

authenticate(pinUvAuthToken, getCredsMetadata (0x01))

authenticate(key, message) → signature
Computes a MAC of the given message.

だっつうことなのですが意味不明です。
以下の方法で求めます。

  • Protocol One なんで いつもの方法で pinUvAuthToken を求めます。32byteのバイナリデータです。
  • 32byteの pinUvAuthTokenKey , 1byteの 0x01Message として HMAC-SHA256 を求めます、32byteのバイナリデータが求まります。私のコード(Rust)ではhmacクレートを使っています簡単です。
  • 先程求めた32byteのHMAC-SHA256の先頭16Byte(0〜16番目)が pinUvAuthParam です。つまり、16byteのバイナリデータですね。

Response

  • getCredsMetadataでとれる情報
    • existingResidentCredentialsCount (0x01) : セキュリキーに記録可能なクレデンシャルの最大数
    • maxPossibleRemainingResidentCredentialsCount (0x02) : 現在記録されているクレデンシャルの数

私の持ってるキーだと

  • existingResidentCredentialsCount = 2

  • maxPossibleRemainingResidentCredentialsCount = 50

でした。ResidetKeyしてクレデンシャルを登録していくと existingResidentCredentialsCount が増えていく感じです。

enumerateRPsBegin/enumerateRPsGetNextRP

RP情報を取得します。enumerateRPsBeginで最初のRP情報と総RP数を取得し、enumerateRPsGetNextRPで次のRP情報を取得します。

Parameters

pinUvAuthProtocol/pinUvAuthParam

getCredsMetadataとほぼ同じです、pinUvAuthParamの求め方が少し違います。

  • enumerateRPsBeginのとき → authenticate(pinUvAuthToken, 0x02)
  • enumerateRPsGetNextRPのとき → authenticate(pinUvAuthToken, 0x03)

Response

  • enumerateRPsBeginでとれる情報

    • rp (0x03) : RP名、PublicKeyCredentialRpEntity型、例 "webauthn.io"
    • rpIDHash (0x04) : RPIDハッシュ、32byteのバイナリデータ
    • totalRPs (0x05) : 登録されている総RP数、数値
  • enumerateRPsGetNextRPでとれる情報

    • rp (0x03) : RP名
    • rpIDHash (0x04) : RPIDハッシュ

enumerateCredentialsBegin/enumerateCredentialsGetNextCredential

パラメータで指定されたRPのクレデンシャルを取得します。enumerateCredentialsBeginで最初のクレデンシャルと総クレデンシャル数を取得し、enumerateCredentialsGetNextCredentialで次のクレデンシャルを取得します。

Note : RP(Relying Party)があってその下にクレデンシャルというツリー構造ということがわかります。

Parameters

subCommandParams

サブパラメータで rpIDHash を指定します。enumerateRPsBegin/enumerateRPsGetNextRPで取れた値ですね。

pinUvAuthProtocol/pinUvAuthParam

pinUvAuthProtocolは他のコマンドと一緒です。
pinUvAuthParamがちょっとめんどくさいです。

pinUvAuthParam = authenticate(pinUvAuthToken, enumerateCredentialsBegin(0x04) || subCommandParams)

この 0x04 || subCommandParams が何なのかというと0x04(1byte)とsubCommandParamsを連結したたデータです。subCommandParamsがRPIDだらRPIDってことではなくCBOR Map型で指定する必要があります。具体的には コード を見たほうが良いかもしれません。

Response

  • enumerateCredentialsBeginでとれる情報

    • user (0x06) : ユーザーデータ、PublicKeyCredentialUserEntity型
    • credentialID (0x07) : クレデンシャルID、PublicKeyCredentialDescriptor型
    • publicKey (0x08) : 公開鍵、COSE_Key型
    • totalCredentials (0x09) : 総クレデンシャル数
  • enumerateCredentialsGetNextCredentialでとれる情報

    • user (0x06) : ユーザーデータ、PublicKeyCredentialUserEntity型
    • credentialID (0x07) : クレデンシャルID、PublicKeyCredentialDescriptor型
    • publicKey (0x08) : 公開鍵、COSE_Key型

deleteCredential

クレデンシャルを削除します。これが欲しかった!

Parameters

subCommandParams

仕様書には

credentialId (0x02): PublicKeyCredentialDescriptor of the credential to be deleted.

って書いてあります、せっかちな私はここでしばらくハマりました。
credentialId*バイト配列ではなくPublicKeyCredentialDescriptor型で指定します。*CBOR Mapです。サンプルコードでは fn create_public_key_credential_descriptor() のあたりです。
(バイト配列のcredentialIdだけ渡せば特定できると思うけどめんどくさいですね)

pinUvAuthProtocol/pinUvAuthParam
pinUvAuthProtocol = 1
pinUvAuthParam = authenticate(pinUvAuthToken, deleteCredential (0x06) || subCommandParams)

ってことで。

Response

削除完了したらCTAP2_OK(0x00)が返ってきます。

updateUserInformation

登録されているユーザー情報を変更します。

このコマンドは動作確認できていません。確認できたら更新します。

Parameters

subCommandParams
- subCommandParams (0x02): Map containing the parameters that need to be updated.
    - credentialId (0x02): PublicKeyCredentialDescriptor of the credential to be updated.
    - user (0x03): a PublicKeyCredentialUserEntity with the updated information.

この通りにCBOR作って試しましたが なぜか 0x11 CTAP2_ERR_CBOR_UNEXPECTED_TYPE  エラーになってしまいます。なんでや(泣)。

pinUvAuthProtocol/pinUvAuthParam
pinUvAuthProtocol = 1
pinUvAuthParam = authenticate(pinUvAuthToken, updateUserInformation (0x07) || subCommandParams)

Response

更新したらCTAP2_OK(0x00)が返ってくるはずです。

おつかれさまでした

WebAuthn Level2 が W3C Recommendation になりました!

4
2
0

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
4
2