AES-CTR HMAC ストリーミング AEAD
コレクションでコンテンツを整理
必要に応じて、コンテンツの保存と分類を行います。
このドキュメントでは、AES-CTR HMAC ストリーミング キー(type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
としてプロト形式でエンコード)で表される数学関数を正式に定義します。
この暗号化は、[HRRV15]1 をベースにしています。セキュリティの分析については、[HS20]2 を参照してください。また、Tink のクロス言語テストには、test_manually_created_test_vector
を含むテスト aes_ctr_hmac_streaming_key_test.py があり、暗号テキストの取得方法に関する完全なチュートリアルが含まれています。
キーとパラメータ
キーは次の部分で記述されます(このドキュメントのサイズはすべてバイト単位です)。
- \(\mathrm{InitialKeyMaterial}\)、バイト文字列: 初期鍵マテリアル。
- \(\mathrm{CiphertextSegmentSize} \in \{1, 2, \ldots, 2^{31}-1\}\)。
- \(\mathrm{DerivedKeySize} \in \{16, 32\}\)
- \(\mathrm{HkdfHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256},
\mathrm{SHA512}\}\)
- \(\mathrm{HmacHashType} \in \{\mathrm{SHA1}, \mathrm{SHA256},
\mathrm{SHA512}\}\)
- \(\mathrm{HmacTagSize} \in \mathbb{N}\)。
有効なキーは、さらに次のプロパティを満たしています。
- \(\mathrm{len}(\mathrm{InitialKeyMaterial}) \geq \mathrm{DerivedKeySize}\)。
- \(\mathrm{HmacHashType} = \mathrm{SHA1}\) の場合は \(\mathrm{HmacTagSize}
\in \{10, \ldots, 20\}\)です。
- \(\mathrm{HmacHashType} = \mathrm{SHA256}\) の場合は \(\mathrm{HmacTagSize}
\in \{10, \ldots, 32\}\)です。
- \(\mathrm{HmacHashType} = \mathrm{SHA512}\) の場合は \(\mathrm{HmacTagSize}
\in \{10, \ldots, 64\}\)です。
- \(\mathrm{CiphertextSegmentSize} > \mathrm{DerivedKeySize} +
\mathrm{HmacTagSize} + 8\) (後で説明するように、これは\(\mathrm{len}(\mathrm{Header}) + \mathrm{HmacTagSize}\) と同じです)。
これらのプロパティのいずれも満たさない鍵は、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}\)を使用して HKDF3 を使用し、このメッセージ\(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt},
\mathrm{AssociatedData})\)の長さ\(\mathrm{DerivedKeySize} + 32\) の鍵マテリアルを計算します。入力は、\(\mathrm{HKDF}\)の対応する入力で使用されます。 \(\mathrm{InitialKeyMaterial}\) は \(\mathrm{ikm}\)、\(\mathrm{Salt}\) は塩、\(\mathrm{AssociatedData}\) は \(\mathrm{info}\)として使用されます。
文字列 \(k\) は 2 つの部分 \(k_1 \| k_2 := k\)(\(\mathrm{len}(k_1) = \mathrm{DerivedKeySize}\) と \(\mathrm{len}(k_2) = 32\))に分割されます。
メッセージの分割
次に、メッセージ \(M\) は部分に分割されます。 \(M = M_0 \| M_1 \| \cdots
\| M_{n-1}\)
長さは、次の条件を満たすように選択されます。
- \(\mathrm{len}(M_0) \in \{0,\ldots, \mathrm{CiphertextSegmentSize} -
\mathrm{len}(\mathrm{Header}) - \mathrm{HmacTagSize}\}\)。
- \(n > 1\)の場合は \(\mathrm{len}(M_1), \ldots, \mathrm{len}(M_{n-1})
\in \{1,\ldots, \mathrm{CiphertextSegmentSize} - \mathrm{HmacTagSize}\}\)です。
- \(n > 1\)の場合、 \(M_{0}, \ldots, M_{n-2}\) は上記の制約に従って最大長にする必要があります。
この分割では、 \(n\) は最大で \(2^{32}\)になります。そうでないと、暗号化は失敗します。
ブロックの暗号化
セグメント \(M_i\)を暗号化するには、まず\(\mathrm{IV}_i := \mathrm{NoncePrefix} \| \mathrm{i} \| b \| 0x00000000\)を計算します。ここで、バイトエンディアン エンコードを使用して \(\mathrm{i}\) を 4 バイトにエンコードし、バイト $b$ を $i < n-1$ の場合は 0x00
に、それ以外の場合は 0x01
に設定します。
次に、 \(M_i\) AES CTR キー \(k_1\)と初期化ベクトル\(\mathrm{IV}_i\)を使用して暗号化します。つまり、AES の呼び出しへの入力は\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\)です。ここで、 \(\mathrm{IV}_i\) はビッグエンディアン整数として解釈されます。これにより、 \(C'_i\)が生成されます。
タグは、 \(\mathrm{HmacHashType}\) で指定されたハッシュ関数と、\(\mathrm{IV}_i \| C'_i\)の連結に対するキー \(k_2\) を使用して HMAC で計算します。
次に、暗号文にタグを連結して \(C_i\)を取得します。
セグメントを連結する
最後に、すべてのセグメントが\(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\)として連結され、これが最終的な暗号文になります。
復号関数
復号は、暗号化を逆にするだけです。ヘッダーを使用してノンスを取得し、暗号テキストの各セグメントを個別に復号します。
API では、ランダム アクセス(通常はファイルの末尾を検査せずにファイルの先頭にアクセス)が許可される場合があります。これは意図的な動作です。以前の暗号テキスト ブロックと残りの暗号テキスト ブロックをすべて復号しなくても、 \(C_i\)から \(M_i\) を復号できるためです。
ただし、API は、ユーザーがファイルの末尾エラーと復号エラーを混同しないように注意する必要があります。どちらの場合も、API はエラーを返す必要があります。この違いを無視すると、攻撃者がファイルを効果的に切り捨てられる可能性があります。
キーのシリアル化と解析
キーを「Tink Proto」形式でシリアル化するには、まず、aes_ctr_hmac_streaming.proto で指定されているプロトコルにパラメータを明示的にマッピングします。フィールド version
は 0 に設定する必要があります。次に、通常のプロト シリアル化を使用してこれをシリアル化し、生成された文字列を KeyData プロトタイプのフィールドの値に埋め込みます。type_url
フィールドを type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
に設定します。次に、key_material_type
を SYMMETRIC
に設定し、これをキーセットに埋め込みます。通常、output_prefix_type
は RAW
に設定します。ただし、output_prefix_type
に別の値が設定された状態でキーが解析された場合、Tink は RAW
または以前の値を書き込むことがあります。
キーを解析するには、上記のプロセスを逆にします(プロトコルを解析する場合の通常の方法で)。フィールド 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。
[[["わかりやすい","easyToUnderstand","thumb-up"],["問題の解決に役立った","solvedMyProblem","thumb-up"],["その他","otherUp","thumb-up"]],[["必要な情報がない","missingTheInformationINeed","thumb-down"],["複雑すぎる / 手順が多すぎる","tooComplicatedTooManySteps","thumb-down"],["最新ではない","outOfDate","thumb-down"],["翻訳に関する問題","translationIssue","thumb-down"],["サンプル / コードに問題がある","samplesCodeIssue","thumb-down"],["その他","otherDown","thumb-down"]],["最終更新日 2025-07-25 UTC。"],[[["\u003cp\u003eThis document defines the AES-CTR HMAC Streaming key and its mathematical function for encryption and decryption, loosely based on the HRRV15 and HS20 security analyses.\u003c/p\u003e\n"],["\u003cp\u003eThe encryption process involves creating a header, splitting the message into segments, encrypting each segment using AES CTR with a derived key, and concatenating the segments along with the header.\u003c/p\u003e\n"],["\u003cp\u003eKeys are described by parameters including InitialKeyMaterial, CiphertextSegmentSize, DerivedKeySize, HkdfHashType, HmacHashType, and HmacTagSize, with specific validity constraints.\u003c/p\u003e\n"],["\u003cp\u003eDecryption reverses the encryption process, allowing for potential random access to segments but requiring careful handling of end-of-file and decryption errors.\u003c/p\u003e\n"],["\u003cp\u003eSerialization and parsing of keys involve mapping parameters to a proto format, embedding them in a KeyData proto, and setting specific fields like type_url, key_material_type, and output_prefix_type.\u003c/p\u003e\n"]]],["AES-CTR HMAC Streaming keys define an encryption method involving key parameters like `InitialKeyMaterial`, `CiphertextSegmentSize`, `DerivedKeySize`, `HkdfHashType`, `HmacHashType`, and `HmacTagSize`. Encryption creates a header with a random salt and nonce prefix, derives key material using HKDF, and splits the message into segments. Each segment is encrypted using AES-CTR with a unique IV and HMAC tag, then concatenated to form the ciphertext. Decryption reverses this process. Keys are serialized into a specific proto format.\n"],null,["# AES-CTR HMAC Streaming AEAD\n\nThis document formally defines the mathematical function represented by\nAES-CTR HMAC Streaming keys (encoded in proto format as\n`type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey`).\n\nThis encryption is loosely based on \\[HRRV15\\]^[1](#fn1)^. For an analysis of the\nsecurity we refer to \\[HS20\\]^[2](#fn2)^. Note also that the Tink cross language tests\nhave a test\n[aes_ctr_hmac_streaming_key_test.py](https://github.com/google/tink/blob/master/testing/cross_language/streaming_aead/aes_ctr_hmac_streaming_key_test.py) which\ncontains `test_manually_created_test_vector` with a complete walkthrough on how\nto get a ciphertext.\n\nKey and parameters\n------------------\n\nKeys are described by the following parts (all sizes in this document are in\nbytes):\n\n- \\\\(\\\\mathrm{InitialKeyMaterial}\\\\), a byte string: the initial key material.\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- \\\\(\\\\mathrm{HmacHashType} \\\\in \\\\{\\\\mathrm{SHA1}, \\\\mathrm{SHA256}, \\\\mathrm{SHA512}\\\\}\\\\).\n- \\\\(\\\\mathrm{HmacTagSize} \\\\in \\\\mathbb{N}\\\\).\n\nValid keys additionally satisfy the following properties:\n\n- \\\\(\\\\mathrm{len}(\\\\mathrm{InitialKeyMaterial}) \\\\geq \\\\mathrm{DerivedKeySize}\\\\).\n- If \\\\(\\\\mathrm{HmacHashType} = \\\\mathrm{SHA1}\\\\) then \\\\(\\\\mathrm{HmacTagSize} \\\\in \\\\{10, \\\\ldots, 20\\\\}\\\\).\n- If \\\\(\\\\mathrm{HmacHashType} = \\\\mathrm{SHA256}\\\\) then \\\\(\\\\mathrm{HmacTagSize} \\\\in \\\\{10, \\\\ldots, 32\\\\}\\\\).\n- If \\\\(\\\\mathrm{HmacHashType} = \\\\mathrm{SHA512}\\\\) then \\\\(\\\\mathrm{HmacTagSize} \\\\in \\\\{10, \\\\ldots, 64\\\\}\\\\).\n- \\\\(\\\\mathrm{CiphertextSegmentSize} \\\u003e \\\\mathrm{DerivedKeySize} + \\\\mathrm{HmacTagSize} + 8\\\\) (This equals \\\\(\\\\mathrm{len}(\\\\mathrm{Header}) + \\\\mathrm{HmacTagSize}\\\\) as explained later).\n\nKeys which do not satisfy 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,\nsplit the message into segments, encrypt each segment, and concatenate\nthe segments. We explain these steps in the following.\n\n### Creating the header\n\nTo create the header, we first pick a uniform random string \\\\(\\\\mathrm{Salt}\\\\)\nof length \\\\(\\\\mathrm{DerivedKeySize}\\\\). We next pick a uniform random string\n\\\\(\\\\mathrm{NoncePrefix}\\\\) of length 7.\n\nWe then set\n\\\\(\\\\mathrm{Header} := \\\\mathrm{len}(\\\\mathrm{Header}) \\\\\\| \\\\mathrm{Salt} \\\\\\|\n\\\\mathrm{NoncePrefix}\\\\),\nwhere the length of the header is encoded as a single byte. We note that\n\\\\(\\\\mathrm{len}(\\\\mathrm{Header}) \\\\in \\\\{24, 40\\\\}\\\\).\n\nNext, we use HKDF^[3](#fn3)^ with hash-function \\\\(\\\\mathrm{HkdfHashType}\\\\)\nto compute key material of length\n\\\\(\\\\mathrm{DerivedKeySize} + 32\\\\) for this message:\n\\\\(k := \\\\mathrm{HKDF}(\\\\mathrm{InitialKeyMaterial}, \\\\mathrm{Salt},\n\\\\mathrm{AssociatedData})\\\\).\nThe inputs are used in the corresponding respective inputs of\n\\\\(\\\\mathrm{HKDF}\\\\): \\\\(\\\\mathrm{InitialKeyMaterial}\\\\) is \\\\(\\\\mathrm{ikm}\\\\),\n\\\\(\\\\mathrm{Salt}\\\\) is the salt, and\n\\\\(\\\\mathrm{AssociatedData}\\\\) is used as \\\\(\\\\mathrm{info}\\\\).\n\nThe string \\\\(k\\\\) is then split into two parts \\\\(k_1 \\\\\\| k_2 := k\\\\),\nsuch that\n\\\\(\\\\mathrm{len}(k_1) = \\\\mathrm{DerivedKeySize}\\\\) and \\\\(\\\\mathrm{len}(k_2) = 32\\\\).\n\n### Splitting the message\n\nThe message \\\\(M\\\\) is next split into parts: \\\\(M = M_0 \\\\\\| M_1 \\\\\\| \\\\cdots\n\\\\\\| M_{n-1}\\\\).\n\nTheir lengths are chosen so that they satisfy:\n\n- \\\\(\\\\mathrm{len}(M_0) \\\\in \\\\{0,\\\\ldots, \\\\mathrm{CiphertextSegmentSize} - \\\\mathrm{len}(\\\\mathrm{Header}) - \\\\mathrm{HmacTagSize}\\\\}\\\\).\n- If \\\\(n \\\u003e 1\\\\), then \\\\(\\\\mathrm{len}(M_1), \\\\ldots, \\\\mathrm{len}(M_{n-1}) \\\\in \\\\{1,\\\\ldots, \\\\mathrm{CiphertextSegmentSize} - \\\\mathrm{HmacTagSize}\\\\}\\\\).\n- If \\\\(n \\\u003e 1\\\\), then \\\\(M_{0}, \\\\ldots, M_{n-2}\\\\) must have maximal length according to the above to constraints.\n\nIn this splitting, \\\\(n\\\\) may at most be \\\\(2\\^{32}\\\\). Otherwise, encryption fails.\n\n### Encrypting the blocks\n\nTo encrypt segment \\\\(M_i\\\\), we first compute\n\\\\(\\\\mathrm{IV}_i := \\\\mathrm{NoncePrefix} \\\\\\| \\\\mathrm{i} \\\\\\| b \\\\\\| 0x00000000\\\\),\nwhere we encode \\\\(\\\\mathrm{i}\\\\) in 4\nbytes using big-endian encoding, and set the byte $b$ to be `0x00` if $i \\\u003c n-1$\nand `0x01` otherwise.\n\nWe then encrypt \\\\(M_i\\\\) using AES CTR key \\\\(k_1\\\\), and initialization vector\n\\\\(\\\\mathrm{IV}_i\\\\). In other words, the inputs to the invocations of AES are\n\\\\(\\\\mathrm{IV}_i, \\\\mathrm{IV}_i + 1, \\\\mathrm{IV}_i + 2, \\\\ldots\\\\)\nwhere \\\\(\\\\mathrm{IV}_i\\\\) is interpreted as big-endian integer.\nThis yields \\\\(C'_i\\\\).\n\nWe compute the tag using HMAC with the hash function given\nby \\\\(\\\\mathrm{HmacHashType}\\\\) and with key \\\\(k_2\\\\) over the concatenation\n\\\\(\\\\mathrm{IV}_i \\\\\\| C'_i\\\\).\n\nWe then concatenate the ciphertext followed by the tag to get \\\\(C_i\\\\).\n\n### Concatenate the segments\n\nFinally, all segments are concatenated as\n\\\\(\\\\mathrm{Header} \\\\\\| C_0 \\\\\\| \\\\cdots \\\\\\| C_{n-1}\\\\), which is the final ciphertext.\n\nDecryption function\n-------------------\n\nDecryption simply inverts the encryption. We use the header to obtain the nonce,\nand decrypt each segment of ciphertext individually.\n\nAPIs may (and typically do) allow random access, or access to the\nbeginning of a file without inspecting the end of the file. This is\nintentional, since it is possible to decrypt \\\\(M_i\\\\) from \\\\(C_i\\\\),\nwithout decrypting all previous and remaining 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\nin the obvious way into the proto given at\n[aes_ctr_hmac_streaming.proto](https://github.com/tink-crypto/tink-java/blob/main/proto/aes_ctr_hmac_streaming.proto).\nThe field `version` needs to be set to 0.\nWe then serialize this using normal proto serialization, and embed the resulting\nstring in the value of field of a\n[KeyData](https://github.com/tink-crypto/tink-java/blob/main/proto/tink.proto) proto. We set the `type_url` field\nto `type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey`.\nWe then set `key_material_type` to `SYMMETRIC`, and embed this into a keyset. We\nusually set the `output_prefix_type` to `RAW`. The exception is that if the key\nwas parsed with a different value set for `output_prefix_type`,\nTink may either write `RAW` or 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\nhave `output_prefix_type` different from `RAW` can be rejected.\nKeys which have a `version` different from 0 are be rejected.\n\nReferences\n----------\n\n*** ** * ** ***\n\n1. \\[HRRV15\\] Hoang, Reyhanitabar, Rogaway, Vizar. Online\n authenticated-encryption and its nonce-reuse misuse-resistance. CRYPTO 2015.\n \u003chttps://eprint.iacr.org/2015/189\u003e [↩](#fnref1)\n\n2. \\[HS20\\] Security of Streaming Encryption in Google's Tink Library.\n Hoang, Shen, 2020.\n \u003chttps://eprint.iacr.org/2020/1019\u003e [↩](#fnref2)\n\n3. \\[HKDF\\] HMAC-based Extract-and-Expand Key Derivation Function (HKDF),\n RFC 5869. \u003chttps://www.rfc-editor.org/rfc/rfc5869\u003e [↩](#fnref3)"]]