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

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

この暗号化は 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{AssociatedData}\)を含むメッセージを \(\mathrm{Msg}\) 暗号化するには、ヘッダーを作成し、メッセージをセグメントに分割し、各セグメントを暗号化して、暗号化されたセグメントを連結します。

ヘッダーを作成する

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

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

次に、HKDF3 を、 \(\mathrm{HkdfHashType}\)で指定されるハッシュ関数と、出力の長さが \(\mathrm{DerivedKeySize}\)である入力 \(\mathrm{ikm} := \mathrm{KeyValue}\)、 \(\mathrm{salt} := \mathrm{Salt}\)、 \(\mathrm{info} := \mathrm{AssociatedData}\)とともに使用します。この結果を \(\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 バイト、$i < n-1$ の場合はバイト $b$ が 0x00、それ以外の場合は 0x01 になります。

次に、 \(M_i\) AES-GCM4 を使用して暗号化します。ここで、鍵は\(\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 はおそらくエラーを返す必要があり、その違いを無視すると、攻撃者が効果的にファイルを切り捨てられる可能性があります。

キーのシリアル化と解析

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

キーを解析するには、上記のプロセスを逆にします(proto 解析時の通常の方法です)。フィールド 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 年。Security of Streaming Encryption in Google's Tink Library。https://eprint.iacr.org/2020/1019 

  3. RFC 5869HMAC ベースの抽出 / 展開キー導出関数(HKDF)。https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D。オペレーションのブロック暗号モードに関する推奨事項: Galois/Counter Mode(GCM)と GMAC。https://csrc.nist.gov/pubs/sp/800/38/d/final