ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

FIDO2 attestation formatの紹介

こんにちは。Yahoo! JAPANの浜田です。
普段は主にYahoo! JAPANの認証・ログイン関連機能、特にWebAuthn対応機能のサーバーサイドの開発業務を行っています。
今回はWebAuthn仕様に定義されているattestation formatの1つのpackedに関して、主にサーバーサイドの動作についてご紹介します。
packedはWebAuthnにおいて標準的なattestation formatであるため、attestationの処理の基本的なところを知るのに良いformatです。
Attestationそのものの解説はこちらの記事でも書かれていますので、併せてご参照ください。

用語集

初めに、本稿で用いる用語を説明します。

用語 説明
認証器 様々な認証を行う機能。WebAuthn仕様では認証手段は生体認証に限らず、ボタンを押す、といった操作も許可されており、認証器の実装に依存する。公開鍵暗号方式の鍵ペアの生成、秘密鍵の保存なども行う。Authenticator。
サービス事業者 Relying Partyのこと。WebAuthnの公開鍵登録・認証などを行うサーバーサイドのこと。
Authenticator Data 認証器が生成するデータ。サービス事業者に登録する公開鍵、authenticatorの認証レベル、認証回数などの情報を含む。
Attestation Statement 認証器の妥当性の検証、認証器からのリクエストの改ざん検知などを行うためのデータ。
Attestation Format Attestation statementのフォーマット。複数定義されている。

Attestationの概要

Attestationは、認証器、端末の正当性を証明するための記述です。

認証器で、attestation用の秘密鍵・公開鍵のペア、署名作成用のデータ、を元に署名を作成し、attestation formatの仕様に沿って、公開鍵、署名をattestation statementに定義し、サービス事業者に送ります。

サービス事業者では、attestation statement内の公開鍵、署名を取り出し、クライアントからのパラメーターから署名作成用のデータを生成し、それらの値を使って署名検証を行います。

Attestationには執筆時点で6種類のattestation formatがあり、packedは基本的なattestation formatです。

Packedとは

Packedは、WebAuthn仕様に最適化されたattestation formatで、コンパクトながらも拡張性があります。

Packedフォーマットの特徴は、attestationの検証が署名検証のみで行われることです。
そのため、opensslなどが利用できる環境であれば実装できます。
このことから、opensslの新しいアルゴリズムが追加された場合もアルゴリズムへの対応を行えば十分です。

Attestation statement解説

署名作成・検証の前に、packedフォーマットのattestation statementの構造を説明します。packedフォーマットのattestation statementは下記の様な構造です。

全体の構造

$$attStmtType //= (
                      fmt: "packed",
                      attStmt: packedStmtFormat
                  )

packedStmtFormat = {
                       alg: COSEAlgorithmIdentifier,
                       sig: bytes,
                       x5c: [ attestnCert: bytes, * (caCert: bytes) ]
                   } //
                   {
                       alg: COSEAlgorithmIdentifier, (-260 for ED256 / -261 for ED512)
                       sig: bytes,
                       ecdaaKeyId: bytes
                   } //
                   {
                       alg: COSEAlgorithmIdentifier
                       sig: bytes,
                   }

$$attStmtType内のfmtキーがフォーマットの識別子になっており、この場合は文字列packedが指定されています。attStmtキーは後半のpackedStmtFormatを指定しています。packedStmtFormatには3つの構造が記載されていますが、認証器はこの内の1つだけを選択してattestationを送ります。そのため、サービス事業者はどのattestation statementが送られてきているのかを判断する必要があります。

attestation statementの各項目解説

  • alg
    署名作成のアルゴリズムが指定されます。指定には-7のような数字を使い、これはIANA COSE Algorithms registryに定義されているものが利用されます。ただし、WebAuthn仕様ではこの値がIANA COSE Algorithms registryに登録されているべき(SHOULD)となっているため、例外もあり得ます。

  • sig
    作成した署名です。バイナリで指定されます。

  • x5c
    attestnCertとそれ自身の証明書チェーンの配列です。attestnCertは必須で、必ず配列の最初の要素である必要がありますが、証明書チェーンの指定は任意です。

  • attestnCert
    X.509フォーマットのアテステーション証明書です。前述のsigはこの証明書から取得できる公開鍵で署名検証が可能です。

  • ecdaaKeyId
    FIDOで新たに定義されている、ECDAAアルゴリズムの公開鍵です。執筆時点でECDAAアルゴリズムの仕様はドラフト版です。

3パターンの構造

Packedのattestation statementは3つのパターンに分けられます。パターンは署名検証に使う公開鍵の違いに依存します。

  • x5c
    証明書(attestnCert)から取得できる公開鍵を利用するパターン
$$attStmtType //= (
                      fmt: "packed",
                      attStmt: packedStmtFormat
                  )

packedStmtFormat = {
                       alg: COSEAlgorithmIdentifier,
                       sig: bytes,
                       x5c: [ attestnCert: bytes, * (caCert: bytes) ]
                   }
  • ecdaaKeyId
    ECDAAアルゴリズムの公開鍵を利用するパターン
$$attStmtType //= (
                      fmt: "packed",
                      attStmt: packedStmtFormat
                  )

packedStmtFormat = {
                       alg: alg: COSEAlgorithmIdentifier, (-260 for ED256 / -261 for ED512)
                       sig: bytes,
                       ecdaaKeyId: bytes
                   }
  • self attestation
    上記2パターン以外authenticator data内の公開鍵を利用するパターン
$$attStmtType //= (
                      fmt: "packed",
                      attStmt: packedStmtFormat
                  )

packedStmtFormat = {
                       alg: COSEAlgorithmIdentifier
                       sig: bytes,
                   }

x5c、ecdaaKeyIdはattestation statementから公開鍵を取得できますが、self attestationの場合はauthenticator data内の公開鍵を利用する必要があります。

署名作成・検証について

認証器は前述の3パターンの内のどのattestation statementを利用するかを選択し、適切に値を整形します。サービス事業者はx5c、ecdaaKeyId、self attestationのどのパターンに当てはまるattestation statementなのかを適切にハンドリングし、署名検証を行います。ハンドリングの例は下記です。

  • Attestation statementにx5cが含まれるかつ、ecdaaKeyIdが含まれない場合、x5cのattestation statement

    1. x5cの最初の値の証明書から公開鍵を取得
    2. 取得した公開鍵、alg、sig、署名作成用データで署名検証
    3. 署名検証が成功した場合は処理続行、失敗の場合は処理終了

  • Attestation statementにecdaaKeyIdが含まれるかつ、x5cが含まれない場合、ecdaaKeyIdのattestation statement

    1. ecdaaKeyIdから公開鍵を取得
    2. 取得した公開鍵、alg、sig、署名作成用データで署名検証
    3. 署名検証が成功した場合は処理続行、失敗の場合は処理終了

  • Attestation statementにx5cecdaaKeyIdも含まれない場合、self attestationのattestation statement

    1. authenticator dataから公開鍵を取得
    2. 取得した公開鍵、alg、sig、署名作成用データで署名検証
    3. 署名検証が成功した場合は処理続行、失敗の場合は処理終了

x5cecdaaKeyIdのどちらも含まれる場合はattestation statementとして正しくないので、サービス事業者はエラーとして処理を中断すべきです。

署名作成用データ

署名作成用データは3パターン共通して同じものが利用されます。署名作成用データは下記の要領で生成します。

  1. Authenticator dataを取得。これを以後authenticatorDataとする。
  2. ClientDataのSHA-256のハッシュ値を取得。これを以後clientDataHashとする。
  3. authenticatorDataclientDataHashを結合。

authenticatorDataにはRPIDのハッシュ、clientDataJSONにはチャレンジ、originを含んでいます。そのため、サービス事業者ごと、サービス事業者とのやりとりごとにユニークな値になり、署名作成用データとして適した値になります。

注意点としては、clientDataHashの元になるClientDataはJSON形式のため、区切り文字の,(カンマ)ごとに改行や半角スペースが含まれている可能性があります。例として、サービス事業者の以下のような処理を考えます。

  1. 改行が含まれているClientDataをJSONから配列にデコードし、変数$clientDataとして保持
  2. 署名作成用データ生成のために、変数$clientDataを配列からJSONにエンコードし、$clientDataJSONとして保持
  3. $clientDataJSONのSHA-256のハッシュ値を$clientDataHashとして保持
  4. authenticator dataの変数$authenticatorData$clientDataHashを結合

この場合、開発言語にもよりますが、2の処理にて改行が消えてしまい、3のハッシュ値は期待している値になりません。ブラウザーの実装によってはClientDataに改行があったりなかったりしますので、サービス事業者ではこうした注意点を包含する署名検証を実装する必要があります。

まとめ

今回はattestation formatの1つであるpackedについてご紹介しました。
Packedは公開鍵暗号方式によるセキュリティーを保ちつつ、比較的コンパクトにまとまっている、WebAuthnにおいて標準的なattestation formatです。
ただし、署名作成のデータにJSONを含んでいるため、正しく署名検証を行っているつもりでも失敗してしまう、ということがある点にも注意が必要です。
本稿をWebAuthnの実装に役立てていただければ幸いです。

関連記事

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました

このページの先頭へ