AES-GCM-HKDF ストリーミング AEAD

このドキュメントでは、AES-GCM-HKDF ストリーミング キーで表される数学関数を正式に定義します。この関数は、proto 形式で type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey としてエンコードされます。

この暗号化は、HRRV151 に基づいています。セキュリティ分析については、HS202 を参照してください。

キーとパラメータ

キーは次の部分で記述されます(このドキュメントのサイズはすべてバイト単位です)。

  • \(\mathrm{KeyValue}\): バイト文字列。
  • \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\)。
  • \(\mathrm{DerivedKeySize} \in \{16, 32\}\)
  • \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256}, \mathrm{SHA512}\}\)。

有効なキーは、さらに次のプロパティを満たしています。

  • \(\mathrm{len}(\mathrm{KeyValue}) \geq \mathrm{DerivedKeySize}\)。
  • \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} + 24\) (後で説明するように、これは \(\mathrm{len}(\mathrm{Header}) + 16\) と同じです)。

これらのプロパティのいずれも満たさない鍵は、鍵が解析されるとき、または対応するプリミティブが作成されるときに、Tink によって拒否されます。

暗号化関数

\(\mathrm{Msg}\) 関連データ\(\mathrm{AssociatedData}\)を含むメッセージを暗号化するには、ヘッダーを作成し、メッセージをセグメントに分割し、各セグメントを暗号化して、暗号化されたセグメントを連結します。

ヘッダーを作成する

長さが\(\mathrm{DerivedKeySize}\) の均一なランダム文字列 \(\mathrm{Salt}\) と、長さが 7 の均一なランダム文字列 \(\mathrm{NoncePrefix}\)を選択します。

次に、 \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt} \| \mathrm{NoncePrefix}\)を設定します。ここで、ヘッダーの長さは 1 バイトとしてエンコードされます。 \(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\)に注意してください。

次に、 \(\mathrm{HkdfHashType}\)で指定されたハッシュ関数と入力 \(\mathrm{ikm} := \mathrm{KeyValue}\)、 \(\mathrm{salt} := \mathrm{Salt}\)、 \(\mathrm{info} := \mathrm{AssociatedData}\)を使用して、出力長 \(\mathrm{DerivedKeySize}\)の HKDF3 を使用します。結果は \(\mathrm{DerivedKey}\)です。

メッセージを分割する

次に、メッセージ \(\mathrm{Msg}\) は部分に分割されます。 \(\mathrm{Msg} = M_0 \| M_1 \| \cdots \| M_{n-1}\)

長さは、次の条件を満たすように選択されます。

  • \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{len}(\mathrm{Header}) - \mathrm{16}\}\)。
  • \(n>1\)の場合は \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1}) \in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{16}\}\)です。
  • \(n>1\)の場合、 \(\mathrm{len}(M_{0}), \ldots, \mathrm{len}(M_{n-2})\) は上記の制約に従って最大長にする必要があります。

\(n\) は \(2^{32}\)文字以下にしてください。そうでないと、暗号化は失敗します。

ブロックを暗号化する

セグメント \(M_i\)を暗号化するには、 \(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b\)を計算します。ここで、 \(\mathrm{i}\) はビッグエンディアン エンコードの 4 バイトであり、バイト $b$ は $i < n-1$ の場合は 0x00、それ以外の場合は 0x01 です。

次に、AES-GCM4 を使用して \(M_i\) を暗号化します。ここで、鍵は\(\mathrm{DerivedKey}\)、初期化ベクトルは \(\mathrm{IV}_i\)、関連データは空の文字列です。 \(C_i\) は、この暗号化の結果です(リンク先の AES-GCM リファレンスのセクション 5.2.1.2 の \(C\) と \(T\) の連結)。

暗号化されたセグメントを連結する

最後に、すべてのセグメントが \(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\)として連結され、これが最終的な暗号文になります。

復号

復号は暗号化を逆にします。ヘッダーを使用して\(\mathrm{NoncePrefix}\)を取得し、暗号テキストの各セグメントを個別に復号します。

API では、ランダム アクセス(通常はファイルの末尾を検査せずにファイルの先頭へのアクセス)が許可される場合があります。これは意図された動作です。以前の暗号テキスト ブロックと残りの暗号テキスト ブロックをすべて復号しなくても、 \(C_i\)から \(M_i\) を復号できるためです。

ただし、API は、ユーザーがファイルの末尾エラーと復号エラーを混同しないように注意する必要があります。どちらの場合も、API はエラーを返す必要があります。この違いを無視すると、攻撃者がファイルを効果的に切り捨てられる可能性があります。

キーのシリアル化と解析

「Tink Proto」形式でキーをシリアル化するには、まず、aes_gcm_hkdf_streaming.proto で指定されているプロトコルにパラメータを明示的にマッピングします。フィールド version は 0 に設定する必要があります。次に、通常のプロト シリアル化を使用してこれをシリアル化し、生成された文字列を KeyData プロトタイプのフィールドの値に埋め込みます。type_url フィールドを type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey に設定します。次に、key_material_typeSYMMETRIC に設定し、これをキーセットに埋め込みます。通常、output_prefix_typeRAW に設定します。例外は、output_prefix_type に異なる値が設定されたキーが解析された場合、Tink が RAW または以前の値を書き込む場合があります。

キーを解析するには、上記のプロセスを逆にします(プロトコルを解析する場合の通常の方法で)。フィールド key_material_type は無視されます。output_prefix_type の値は無視できます。また、output_prefix_typeRAW と異なるキーは拒否できます。version が 0 と異なる鍵は拒否する必要があります。

既知の問題

上記の暗号化関数の実装は、フォークセーフであるとは想定されていません。フォークの安全性をご覧ください。

参照


  1. Hoang, Reyhanitabar, Rogaway, Vizar, 2015. オンライン認証暗号化と、そのノンスの再利用による不正使用に対する耐性。CRYPTO 2015。https://eprint.iacr.org/2015/189 

  2. Hoang、Shen、2020 年。Google の Tink ライブラリでのストリーミング暗号化のセキュリティ。https://eprint.iacr.org/2020/1019 

  3. RFC 5869 の詳細がHMAC ベースの抽出拡張鍵導出関数(HKDF)。https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D。ブロック 暗号の動作モードに関する推奨事項: ガロア/カウンタモード(GCM)と GMAC。https://csrc.nist.gov/pubs/sp/800/38/d/final