FIDO2に重要なWeb Authenticationの仕様の7章Relying Partyについて書かれている部分を邦訳した。
(表記ゆれは残っているし、誤訳もあるかも。。。)
この内容をもとにYahoo! JAPANでの生体認証の取り組み(FIDO2サーバーの仕組みについて) - Yahoo! JAPAN Tech Blogでは実装されているはずなので、参考にしてFIDOについて勉強していく予定。
FIDO2やWen Authenticationの基本的な内容は、以下の記事などを参照。
7. WebAuthn Relying Party処理
登録処理または認証処理は、WebAuthn Relying PartyがそれぞれPublicKeyCredentialCreationOptions
オブジェクトまたはPublicKeyCredentialRequestOptions
オブジェクトという処理に使用するパラメータをエンコードしたオブジェクトを作成することから始まります。Relying Partyはこの段階で機密情報を漏らさないように注意しなければなりません(SHOULD)。詳細は§ 14.6.2 Username Enumerationを参照してください。
create()
またはget()
の実行に成功した場合、Relying Partyのスクリプトは、クライアントからそれぞれAuthenticatorAttestationResponse
構造またはAuthenticatorAssertionResponse
構造を含んだPublicKeyCredential
を受け取ります。本仕様外のメソッドによって、この構造のコンテンツはRelying Partyのサーバに届けられなければなりません。このセクションではRelying Partyが受け取ったこれらの構造のオブジェクトに対しておこなう処理について記述しています。
7.1 新しいクレデンシャルの登録
登録処理を実行するため、Relying Partyは以下のことを実施しなければなりません(MUST):
- options を、処理のためにRelying Partyが必要とする新規の
PublicKeyCredentialCreationOptions
構造に設定する。 navigator.credentials.create()
を呼び出し、publicKey
オプションとして option を受け渡す。Promiseが正常にresolveされた結果を credintial とする。Promiseがrejectされた場合、ユーザが見ることができるエラーによって処理を中止する、もしくはrejectされたPromiseから取得できる情報から判定できるユーザ操作に導く。例えば、「InvalidStateError
」と同等なエラーコードによってPromiseがrejectされた場合、別の認証器を利用するようにユーザに指示する。その他エラーおよび環境での情報は、§ 6.3.2 The authenticatorMakeCredential Operationを参照。- response を credential.response とする。 response がAuthenticatorAttestationResponseのインスタンスでなかった場合、ユーザが見ることができエラーによって処理を中止する。
- clientExtensionResults に credential.getClientExtensionResults() の呼び出し結果を設定します。
- JSONtext に response.clientDataJSON の値にUTF-8デコードを実行した結果を設定します。
注意:UTF-8デコードの他の実装を利用することは、UTF-8デコードアルゴリズムと同じ結果を出力するかぎり認められています。特に先頭のバイト順マーク(BOM)は取り除かなければなりません(MUST)。
注意: C はこのアルゴリズムが必要なときに C のコンポーネントを参照可能なかぎり、実装特有なデータ構造表現にして構わない。
- C.type の値が
webauthn.create
と一致することを検証する。 - C.challenge の値が options.challenge のBase64URLエンコーディング結果と一致することを検証する。
- C.origin の結果がRelying Partyのoriginと一致することを検証する。
- C.tokenBinding.status の値が認証を取得するTLS接続のためのトークンバインディングの状態と一致することを検証する。また、TLS接続においてトークンバインディングが使われた場合、 C.tokenBinding.id がその接続のトークンバインディングIDのBase64URLエンコーディングに一致することを検証する。
- hash にSHA-256を利用して response.clientDataJSON のハッシュ値を計算した結果を設定する。
- attestation statement format fmt 、authenticator data authData およびattestation statement attStmt を取得するため、 AuthenticatorAttestationResponse構造のattestationObjectフィールドに対してCBORデコーディングを実行する。
- authData 内のrpIdHashがRelying Partyによって期待されるRP IDのSHA-256ハッシュ値であることを検証する。
- authData 内の
flags
のUser Presentビットが設定されていることを検証する。 - この登録にユーザ認証を必須とする場合、 authData 内の
flags
のUser Verifiedビットが設定されていることを検証する。 - authData 内の公開鍵の「alg」パラメータが options.pubKeyCredParams 内の要素のうちの1つの
alg
属性と一致することを検証する。 - clientExtensionResults 内のclient extension outputsおよび authData 内の
extensions
のauthenticator extension outputsの値が期待通りであることを検証する。これは options.extensions で渡されたclient extension inputの値および未承認、つまり options.extensions 部分に記載されていないextensionsに関するRelying Party独自のポリシーについて考慮したものである。一般的に、「期待通りであること」という意味はRelying Partyとどのようなextensionsが利用されているかによって変わる。
注意:クライアントプラットフォームは、追加的なauthenticator extensionsまたはclient extensionsを設定するためのローカルポリシーを規定するかもしれない(MAY)。つまり、これらは options.extensions 部分で元来定められていなかったauthenticator extension outputsまたはclient extension outputsを表現する値を必要とする。Relying Partyは未承認のextensionsを無視するかattestationを拒絶するかに関わらず、このような状態に対応できるようにしなければならない(MUST)。Relying Partyはローカルポリシーと利用されているextensionsに基づいてこの決定をおこなうことができる。
注意:クライアントと認証器の両方において全てのextensionsはOPTIONALであるため、Relying Partyは必須となるextensionsが全て実行されないケースおよび一部が実行されないケースに対応できるようにしなければならない(MUST)。
- サポートされているWebAuthn Attestation Statement Format Identifierに対し、 fmt がUSASCIIケースセンシティブに一致するかを確認してattestation statement formatを決定する。登録されているWebAuthn Attestation Statement Format Identifierの最新のリストは、[RFC8809]によって設立されたIANA "WebAuthn Attestation Statement Format Identifiers" registry [IANA-WebAuthn-Registries]にて管理されている。
- attStmt が適切なattestation signatureを有した正しいattestation statementであることを、与えられた attStmt 、 authData および hash を用いてattestation statement format fmt ごとの検証手順によって検証する。
注意:それぞれの attestation statement formatは独自の検証手順を有する。初期に定義されたフォーマットについては§ 8 Defined Attestation Statement Formatsを参照し、最新のリストは[IANA-WebAuthn-Registries]を参照する。
- 検証に成功した場合、信頼されたソースおよびポリシーからattestation typeおよびattestation statement format fmt に関するアクセス可能なトラストアンカー(つまり、attestation root certificates)の一覧を取得できる。例えば、FIDO Metadata Service [FIDOMetadataService]は、 authData 内の
attestedCredentialData
のaaguid
を利用してそれらの情報を取得する方法の1つである。 - ステップ19の検証手順の結果を利用し、以下のようにattestationの信用性を評価する:
- no attestationの場合、None attestationが受け入れ可能かRelying Partyのポリシーによって検証する。
- self attestationの場合、self attestationが受け入れ可能かRelying Partyのポリシーによって検証する。
- 上記以外の場合、検証手順からattestation trust pathとして返却されたX.509証明書を利用し、attestation public keyが正しくアクセス可能なルート証明書につながっていること、もしくはそれ自体がアクセス可能な証明書であること(つまり、その証明書とステップ20で取得したルート証明書が同一であること)を検証する。
credentialId
がどのユーザに対して登録されていないことを確認する。既に他のユーザに登録されているクレデンシャルの登録が要求された場合、Relying Partyはこの登録処理を失敗させるべきである(SHOULD)、または例えば古い登録を削除して登録を受け入れると決めることができる(MAY)。- attestation statement attStmt の検証が成功し、信用性が確認され、 options.user で示されているアカウントに対して新しいクレデンシャルとして登録された場合:
- Relying Partyのシステムの必要性に応じ、ユーザのアカウントと authData.attestedCredentialData 内の
credentialId
およびcredentialPublicKey
を関連付ける。 credentialId
と authData.signCount の値で初期化して新規保存したsignature counterの値を関連付ける。 また、以下のことを推奨する(RECOMMENDED):
- Relying Partyのシステムの必要性に応じ、ユーザのアカウントと authData.attestedCredentialData 内の
-
credentialId
と credential.response.getTransports() を呼び出した戻り値であるtransport hintsを関連付ける。この値は保存前後で変更すべきではない(SHOULD NOT)。後述のget()
の呼び出しにおいてClientが適切な認証器を見つける方法を知る手助けとなるようにallowCredentials
オプションのtransports
に情報を入れるため、この値を使うことを推奨する(RECOMMENDED)。 - attestation statement attStmt の検証に成功したが上述のステップ21で信用性が確認できなかった場合、Relying Partyは登録処理を失敗させるべきである(SHOULD)。
注意:ただし、ポリシーによって許可された場合、Relying Partyはcredential IDおよび公開鍵を登録することができる(MAY)が、self attestation(§ 6.5.3 Attestation Types参照)と関連付いていることとなり、クレデンシャルを脅かすことになる。これを実施した場合、Relying Partyは特定の認証器モデルによって生成された公開鍵は暗号証明がないことを検証する。更に詳細な議論は[FIDOSecRef]および[UAFProtocol]を参照。
attestation objectsの検証は、Relying Partyが上述のステップ20においてアクセス可能なトラストアンカーを決定するための信用できる方法を有していることが必須である。また、証明書が使用された場合、Relying Partyは中間CA証明書に証明書ステータス情報にアクセスしなくてはならない(MUST)。さらに、Clientがattestation情報でattestation certificate chainについて知らせなかった場合、Relying Partyはそのチェーンを構成することができなければならない。
7.2 Authenticaiton Assertionの検証
認証処理を実行するため、Relying Partyは以下の内容を実行する必要がある(MUST):
- options に、Relying Partyが処理に必要とする項目を設定した新規生成した
PublicKeyCredentialRequestOptions
構造を設定する。 options.allowCredentials が存在する場合、それぞれのアイテムのtransports
要素は対応するクレデンシャルが登録されたときの credential.response.getTransports() の戻り値を設定するべきである(SHOULD)。 navigator.credentials.get()
を呼び出し、publicKey
オプションとして options を受け渡す。 credential にPromiseのresolveが成功した結果を設定する。Promiseがrejectされた場合、ユーザが見ることができるエラーによって処理を中止する、もしくはrejectされたPromiseから取得できる情報から判定できるユーザ操作に導く。その他エラーおよび環境での情報は、§ 6.3.3 The authenticatorGetAssertion Operationを参照。- response に credential.response を設定する。 response が
AuthenticatorAssertionResponse
のインスタンスではない場合、ユーザが見ることができるエラーによって処理を中止する。 - clientExtensionResults に credential.getClientExtensionResults() の呼び出し結果を設定する。
- options.allowCredentials が空ではない場合、 credential.id が options.allowCredentials に挙げられている公開鍵の1つを指定していることを検証する。
- 認証されたユーザであることを特定し、そのユーザが credential.id で指定された公開鍵認証ソース credentialSource のオーナーであることを検証する:
- 認証処理を開始する前に例えばユーザ名やcookieによりユーザが特定されていた場合、
- 特定されたユーザが credentialSource のオーナーであることを検証する。 response.userHandle が存在する場合、 userHandle にその値を設定する。また、 userHandle が同じユーザに紐付いていることを検証する。
- 認証処理を開始する前にユーザが特定されていなかった場合、
- response.userHandle が存在することおよびその値によって特定されたユーザが credentialSource のオーナーであることを検証する。
- credential.id (または、base64urlエンコーディングが自身のユースケースに不適切な場合にはcredential.rawId)を利用して対応する公開鍵を探し出し、 credentialPublicKey に公開鍵を設定する。
- cData 、 authData および sig に対して、それぞれ response の
clientDataJSON
、authenticatorData
およびsignature
の値を割り当てる。 - JSONtext に response.clientDataJSON の値にUTF-8デコードを実行した結果を設定します。
注意:UTF-8デコードの他の実装を利用することは、UTF-8デコードアルゴリズムと同じ結果を出力するかぎり認められています。特に先頭のバイト順マーク(BOM)は取り除かなければなりません(MUST)。
注意: C はこのアルゴリズムが必要なときに C のコンポーネントを参照可能なかぎり、実装特有なデータ構造表現にして構わない。
- c.type の値が
webauthn.get
という文字列であることを検証する。 - C.challenge の値が options.challenge のBase64URLエンコーディング結果と一致することを検証する。
- C.origin の結果がRelying Partyのoriginと一致することを検証する。
- C.tokenBinding.status の値がattestationを取得したTLS接続のためのトークンバインディングの状態と一致することを検証する。また、TLS接続においてトークンバインディングが使われた場合、 C.tokenBinding.id がその接続のトークンバインディングIDのBase64URLエンコーディングに一致することを検証する。
- authData 内のrpIdHashがRelying Partyによって期待されるRP IDのSHA-256ハッシュ値であることを検証する。
注意:appid extensionを利用する場合、このステップで特別なロジックが必要となります。詳細は§ 10.1 FIDO AppID Extension (appid)を参照。
- authData 内の
flags
のUser Presentビットが設定されていることを検証する。 - この認証にユーザ認証を必須とする場合、 authData 内の
flags
のUser Verifiedビットが設定されていることを検証する。 - clientExtensionResults 内のclient extension outputsおよび authData 内の
extensions
のauthenticator extension outputsの値が期待通りであることを検証する。これは options.extensions で渡されたclient extension inputの値および未承認、つまり options.extensions 部分に記載されていないextensionsに関するRelying Party独自のポリシーについて考慮したものである。一般的に、「期待通りであること」という意味はRelying Partyとどのようなextensionsが利用されているかによって変わる。
注意:クライアントプラットフォームは、追加的なauthenticator extensionsまたはclient extensionsを設定するためのローカルポリシーを規定するかもしれない(MAY)。つまり、これらは options.extensions 部分で元来定められていなかったauthenticator extension outputsまたはclient extension outputsを表現する値を必要とする。Relying Partyは未承認のextensionsを無視するかattestationを拒絶するかに関わらず、このような状態に対応できるようにしなければならない(MUST)。Relying Partyはローカルポリシーと利用されているextensionsに基づいてこの決定をおこなうことができる。
注意:クライアントと認証器の両方において全てのextensionsはOPTIONALであるため、Relying Partyは必須となるextensionsが全て実行されないケースおよび一部が実行されないケースに対応できるようにしなければならない(MUST)。
- hash にSHA-256を利用して cData のハッシュ値を計算した結果を設定する。
- credentialPublicKey を使用し、 sig がauthData と hash のバイナリ連結に対する適切な署名であることを検証する。
注意:この検証ステップはFIDO U2F認証器によって生成された署名に互換性がある。§ 6.1.2 FIDO U2F Signature Format Compatibility参照。
- storedSignCount に credential.id の関連付けられて保存されたsignature counterの値を設定する。authData.signCount がゼロではない場合、もしくは storedSignCount がゼロではない場合、以下の追加ステップを実行する:
- authData.signCount が、
- storedSignCount より大きい場合、
- authData.signCount の値に storedSignCount を更新する。
- storedSignCount より小さいもしくは同じ場合、
- 認証器が複製されている可能性がある、つまり秘密鍵の複製が最低でも2つ存在している可能性があり、同時に利用されていることを示唆する。Relying Partyはこの情報をリスクスコアリングに組み入れるべきである。このような場合、Relying Partyは storedSignCont を更新するか更新しないか、もしくは認証処理を失敗させるか失敗させないかについてはRelying Partyに依存する。
- authData.signCount が、
- 上述の全てのステップが成功した場合、必要に応じて認証処理を続けます。そうでなければ、認証処理を失敗させる。