AES-CTR HMAC 串流 AEAD
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
本文件正式定義 AES-CTR HMAC Streaming 金鑰 (以 proto 格式編碼為 type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
) 所代表的數學函式。
這項加密機制大致以 [HRRV15]1 為基礎。如要分析安全性,請參閱 [HS20]2。請注意,Tink 跨語言測試包含 aes_ctr_hmac_streaming_key_test.py 測試,其中包含 test_manually_created_test_vector
,並提供完整的操作說明,說明如何取得密文。
鍵和參數
金鑰會以以下部分說明 (本文件中的所有大小皆以位元組為單位):
- \(\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}\),其中標頭長度會編碼為單一位元組。我們注意到\(\mathrm{len}(\mathrm{Header}) \in \{24, 40\}\)。
接下來,我們會使用 HKDF3 搭配雜湊函式 \(\mathrm{HkdfHashType}\),為此訊息計算長度為\(\mathrm{DerivedKeySize} + 32\) 的金鑰素材:\(k := \mathrm{HKDF}(\mathrm{InitialKeyMaterial}, \mathrm{Salt},
\mathrm{AssociatedData})\)。這些輸入內容會用於\(\mathrm{HKDF}\)的對應輸入內容: \(\mathrm{InitialKeyMaterial}\) 是 \(\mathrm{ikm}\)、\(\mathrm{Salt}\) 是鹽,而\(\mathrm{AssociatedData}\) 會用於 \(\mathrm{info}\)。
字串 \(k\) 隨後會分割成兩個部分 \(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 個位元組,如果 $i < n-1$,則將位元組 $b$ 設為 0x00
,否則設為 0x01
。
接著,我們會使用 AES CTR 金鑰 \(k_1\)和初始化向量\(\mathrm{IV}_i\)加密資料。 \(M_i\) 換句話說,AES 叫用的輸入內容為\(\mathrm{IV}_i, \mathrm{IV}_i + 1, \mathrm{IV}_i + 2, \ldots\),其中 \(\mathrm{IV}_i\) 會解讀為大端整數。這會產生 \(C'_i\)。
我們會使用 HMAC 計算標記,並使用 \(\mathrm{HmacHashType}\) 提供的雜湊函式,以及在連接\(\mathrm{IV}_i \| C'_i\)時使用金鑰 \(k_2\) 。
接著,我們將密文與標記連接,取得 \(C_i\)。
連結區段
最後,所有區段會串連為\(\mathrm{Header} \| C_0 \| \cdots \| C_{n-1}\),也就是最終的密文。
解密函式
解密只需將加密內容反轉即可。我們會使用標頭取得 Nonce,並個別解密每個密文區段。
API 可能會 (通常會) 允許隨機存取,或在未檢查檔案結尾的情況下存取檔案開頭。這是有意為之,因為您可以從 \(C_i\)解密 \(M_i\) ,而無須解密所有先前和剩餘的密文區塊。
不過,API 應謹慎處理,避免使用者將檔案結尾和解密錯誤搞混:在兩種情況下,API 都可能會傳回錯誤,而忽略差異可能導致對手有效截斷檔案。
金鑰的序列化和剖析
如要以「Tink Proto」格式序列化金鑰,我們會先以明顯的方式將參數對應至 aes_ctr_hmac_streaming.proto 中提供的原始檔。version
欄位必須設為 0。接著,我們會使用一般 Proto 序列化功能將其序列化,並將產生的字串嵌入 KeyData Proto 欄位的值中。我們將 type_url
欄位設為 type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey
。接著,我們將 key_material_type
設為 SYMMETRIC
,並將其嵌入鍵組中。我們通常會將 output_prefix_type
設為 RAW
。例外狀況是,如果鍵是使用 output_prefix_type
的不同值集進行剖析,Tink 可能會寫入 RAW
或先前的值。
如要剖析鍵,我們會反向執行上述程序 (以剖析 Protobuf 的一般方式)。系統會忽略欄位 key_material_type
。output_prefix_type
的值可以忽略,或是可以拒絕 output_prefix_type
與 RAW
不同的鍵。系統會拒絕 version
與 0 不同的鍵。
參考資料
除非另有註明,否則本頁面中的內容是採用創用 CC 姓名標示 4.0 授權,程式碼範例則為阿帕契 2.0 授權。詳情請參閱《Google Developers 網站政策》。Java 是 Oracle 和/或其關聯企業的註冊商標。
上次更新時間:2025-07-25 (世界標準時間)。
[[["容易理解","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 (世界標準時間)。"],[[["\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)"]]