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{ikm} := \mathrm{KeyValue}\)、 \(\mathrm{salt} :=
\mathrm{Salt}\)、 \(\mathrm{info} := \mathrm{AssociatedData}\)で出力長 \(\mathrm{DerivedKeySize}\)を使用します。この結果を \(\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-GCM を使用4ここで鍵となるのは\(\mathrm{DerivedKey}\)初期化ベクトルは \(\mathrm{IV}_i\)関連付けられたデータは空の文字列です。 \(C_i\) この暗号化の結果(つまり、 \(C\) そして \(T\) リンク先の AES-GCM リファレンスのセクション 5.2.1.2 を参照)。
暗号化されたセグメントを連結する
最後に、すべてのセグメントが \(\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_type
を SYMMETRIC
に設定し、これを鍵セットに埋め込みます。通常は、output_prefix_type
を RAW
に設定します。例外として、output_prefix_type
に設定された異なる値でキーが解析された場合、Tink は RAW
またはその前の値を書き込むことができます。
キーを解析するには、上記のプロセスを逆に(proto の解析時に通常の方法で)します。フィールド key_material_type
は無視されます。output_prefix_type
の値は無視することも、output_prefix_type
が RAW
と異なるキーを拒否することもできます。version
が 0 とは異なる鍵は拒否される必要があります。
既知の問題
上記の暗号化関数の実装は、フォークセーフであるとは限りません。フォークの安全性をご覧ください。
参照
特に記載のない限り、このページのコンテンツはクリエイティブ・コモンズの表示 4.0 ライセンスにより使用許諾されます。コードサンプルは Apache 2.0 ライセンスにより使用許諾されます。詳しくは、Google Developers サイトのポリシーをご覧ください。Java は Oracle および関連会社の登録商標です。
最終更新日 2025-07-25 UTC。
[null,null,["最終更新日 2025-07-25 UTC。"],[[["\u003cp\u003eThis document specifies the \u003ccode\u003eAesGcmHkdfStreamingKey\u003c/code\u003e encryption function, based on HRRV15 and analyzed in HS20, for secure streaming data encryption.\u003c/p\u003e\n"],["\u003cp\u003eKeys consist of \u003ccode\u003eKeyValue\u003c/code\u003e, \u003ccode\u003eCiphertextSegmentSize\u003c/code\u003e, \u003ccode\u003eDerivedKeySize\u003c/code\u003e, and \u003ccode\u003eHkdfHashType\u003c/code\u003e, with specific size and validity constraints.\u003c/p\u003e\n"],["\u003cp\u003eEncryption involves creating a header, splitting the message into segments, encrypting each segment using AES-GCM with a derived key, and concatenating the results.\u003c/p\u003e\n"],["\u003cp\u003eDecryption reverses this process, allowing random access to segments for efficient retrieval of specific message parts.\u003c/p\u003e\n"],["\u003cp\u003eSerialization and parsing of keys utilize Tink Proto format with specific requirements for \u003ccode\u003eversion\u003c/code\u003e, \u003ccode\u003etype_url\u003c/code\u003e, and \u003ccode\u003ekey_material_type\u003c/code\u003e fields.\u003c/p\u003e\n"]]],["The document defines AES-GCM-HKDF Streaming key encryption. Encryption involves creating a header with a random salt and nonce prefix, deriving a key using HKDF with associated data, and splitting the message into segments. Each segment is encrypted using AES-GCM with a unique initialization vector. The encrypted segments are concatenated with the header to form the final ciphertext. Decryption reverses this process. Key serialization follows the Tink Proto format, with specific field settings. Keys can be parsed in the same way.\n"],null,["# AES-GCM-HKDF Streaming AEAD\n\nThis document formally defines the mathematical function represented by\nAES-GCM-HKDF Streaming keys, encoded in proto format as\n`type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey`.\n\nThis encryption is loosely based on HRRV15^[1](#fn1)^. For security analysis, we refer\nto HS20^[2](#fn2)^.\n\nKey and parameters\n------------------\n\nKeys are described by the following parts (all sizes in this document are in\nbytes):\n\n- \\\\(\\\\mathrm{KeyValue}\\\\), a byte string.\n- \\\\(\\\\mathrm{CiphertextSegmentSize} \\\\in \\\\{1, 2, \\\\ldots, 2\\^{31}-1\\\\}\\\\).\n- \\\\(\\\\mathrm{DerivedKeySize} \\\\in \\\\{16, 32\\\\}\\\\).\n- \\\\(\\\\mathrm{HkdfHashType} \\\\in \\\\{\\\\mathrm{SHA1}, \\\\mathrm{SHA256}, \\\\mathrm{SHA512}\\\\}\\\\).\n\nValid keys additionally satisfy the following properties:\n\n- \\\\(\\\\mathrm{len}(\\\\mathrm{KeyValue}) \\\\geq \\\\mathrm{DerivedKeySize}\\\\).\n- \\\\(\\\\mathrm{CiphertextSegmentSize} \\\u003e \\\\mathrm{DerivedKeySize} + 24\\\\) (This equals \\\\(\\\\mathrm{len}(\\\\mathrm{Header}) + 16\\\\) as explained later).\n\nKeys that violate any of these properties are rejected by Tink, either\nwhen the key is parsed or when the corresponding primitive is created.\n\nEncryption function\n-------------------\n\nTo encrypt a message \\\\(\\\\mathrm{Msg}\\\\) with associated data\n\\\\(\\\\mathrm{AssociatedData}\\\\), we create a header, split the message into\nsegments, encrypt each segment, and concatenate the encrypted segments.\n\n### Create the header\n\nWe pick a uniform random string \\\\(\\\\mathrm{Salt}\\\\) of length\n\\\\(\\\\mathrm{DerivedKeySize}\\\\) and a uniform random string \\\\(\\\\mathrm{NoncePrefix}\\\\)\nof length 7.\n\nWe then set \\\\(\\\\mathrm{Header} := \\\\mathrm{len}(\\\\mathrm{Header}) \\\\\\| \\\\mathrm{Salt}\n\\\\\\| \\\\mathrm{NoncePrefix}\\\\), where the length of the header is encoded as a single\nbyte. Note that \\\\(\\\\mathrm{len}(\\\\mathrm{Header}) \\\\in \\\\{24, 40\\\\}\\\\).\n\nNext, we use HKDF^[3](#fn3)^ with the hash function given by \\\\(\\\\mathrm{HkdfHashType}\\\\)\nand inputs \\\\(\\\\mathrm{ikm} := \\\\mathrm{KeyValue}\\\\), \\\\(\\\\mathrm{salt} :=\n\\\\mathrm{Salt}\\\\), and \\\\(\\\\mathrm{info} := \\\\mathrm{AssociatedData}\\\\), with output\nlength \\\\(\\\\mathrm{DerivedKeySize}\\\\). We call the result \\\\(\\\\mathrm{DerivedKey}\\\\).\n\n### Split the message\n\nThe message \\\\(\\\\mathrm{Msg}\\\\) is next split into parts: \\\\(\\\\mathrm{Msg} = M_0 \\\\\\|\nM_1 \\\\\\| \\\\cdots \\\\\\| M_{n-1}\\\\).\n\nTheir lengths are chosen to satisfy:\n\n- \\\\(\\\\mathrm{len}(M_0) \\\\in \\\\{0,\\\\ldots, \\\\mathrm{CiphertextSegmentSize} - \\\\mathrm{len}(\\\\mathrm{Header}) - \\\\mathrm{16}\\\\}\\\\).\n- If \\\\(n\\\u003e1\\\\), then \\\\(\\\\mathrm{len}(M_1), \\\\ldots, \\\\mathrm{len}(M_{n-1}) \\\\in \\\\{1,\\\\ldots, \\\\mathrm{CiphertextSegmentSize} - \\\\mathrm{16}\\\\}\\\\).\n- If \\\\(n\\\u003e1\\\\), then \\\\(\\\\mathrm{len}(M_{0}), \\\\ldots, \\\\mathrm{len}(M_{n-2})\\\\) must have maximal length according to the above to constraints.\n\n\\\\(n\\\\) may be at most \\\\(2\\^{32}\\\\). Otherwise, encryption fails.\n\n### Encrypt the blocks\n\nTo encrypt segment \\\\(M_i\\\\), we compute \\\\(\\\\mathrm{IV}_i := \\\\mathrm{NoncePrefix}\n\\\\\\| \\\\mathrm{i} \\\\\\| b\\\\), where \\\\(\\\\mathrm{i}\\\\) is 4 bytes in big-endian encoding and\nbyte $b$ is `0x00` if $i \\\u003c n-1$ and `0x01` otherwise.\n\nWe then encrypt \\\\(M_i\\\\) using AES-GCM^[4](#fn4)^, where the key is\n\\\\(\\\\mathrm{DerivedKey}\\\\), the initialization vector is \\\\(\\\\mathrm{IV}_i\\\\), and the\nassociated data is the empty string. \\\\(C_i\\\\) is the result of this encryption\n(i.e. the concatenation of \\\\(C\\\\) and \\\\(T\\\\) in section 5.2.1.2 of the linked\nAES-GCM reference).\n\n### Concatenate the encrypted segments\n\nFinally, all segments are concatenated as \\\\(\\\\mathrm{Header} \\\\\\| C_0 \\\\\\| \\\\cdots \\\\\\|\nC_{n-1}\\\\), which is the final ciphertext.\n\nDecryption\n----------\n\nDecryption inverts the encryption. We use the header to obtain\n\\\\(\\\\mathrm{NoncePrefix}\\\\), and decrypt each segment of ciphertext individually.\n\nAPIs may (and typically do) allow random access, or access to the beginning of a\nfile without inspecting the end of the file. This is intentional, since it is\npossible to decrypt \\\\(M_i\\\\) from \\\\(C_i\\\\), without decrypting all previous and\nremaining ciphertext blocks.\n\nHowever, APIs should be careful to not allow users to confuse end-of-file and\ndecryption errors: in both cases the API probably has to return an error, and\nignoring the difference can lead to an adversary being able to effectively\ntruncate files.\n\nSerialization and parsing of keys\n---------------------------------\n\nTo serialize a key in the \"Tink Proto\" format, we first map the parameters in\nthe obvious way into the proto given at\n[aes_gcm_hkdf_streaming.proto](https://github.com/tink-crypto/tink-java/blob/main/proto/aes_gcm_hkdf_streaming.proto). The field `version` needs to\nbe set to 0. We then serialize this using normal proto serialization, and embed\nthe resulting string in the value of field of a [KeyData](https://github.com/tink-crypto/tink-java/blob/main/proto/tink.proto) proto. We\nset the `type_url` field to\n`type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey`. We then set\n`key_material_type` to `SYMMETRIC`, and embed this into a keyset. We usually set\nthe `output_prefix_type` to `RAW`. The exception is that if the key was parsed\nwith a different value set for `output_prefix_type`, Tink may either write `RAW`\nor the previous value.\n\nTo parse a key, we reverse the above process (in the usual way when parsing\nprotos). The field `key_material_type` is ignored. The value of\n`output_prefix_type` can either be ignored, or keys which have\n`output_prefix_type` different from `RAW` can be rejected. Keys which have a\n`version` different from 0 must be rejected.\n\nKnown issues\n------------\n\nImplementations of the above encryption function are not expected to be fork\nsafe. See [Fork Safety](/tink/issues/fork-safety).\n\nReferences\n----------\n\n*** ** * ** ***\n\n1. Hoang, Reyhanitabar, Rogaway, Vizar, 2015. Online authenticated-encryption\n and its nonce-reuse misuse-resistance. CRYPTO 2015.\n \u003chttps://eprint.iacr.org/2015/189\u003e [↩](#fnref1)\n\n2. Hoang, Shen, 2020. Security of Streaming Encryption in Google's\n Tink Library. \u003chttps://eprint.iacr.org/2020/1019\u003e [↩](#fnref2)\n\n3. RFC 5869. HMAC-based Extract-and-Expand Key Derivation Function (HKDF).\n \u003chttps://www.rfc-editor.org/rfc/rfc5869\u003e [↩](#fnref3)\n\n4. NIST SP 800-38D. Recommendation for Block Cipher Modes of Operation:\n Galois/Counter Mode (GCM) and GMAC. \u003chttps://csrc.nist.gov/pubs/sp/800/38/d/final\u003e [↩](#fnref4)"]]