สตรีมมิง AEAD ของ AES-GCM-HKDF
จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน
บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ
เอกสารนี้ระบุฟังก์ชันทางคณิตศาสตร์อย่างเป็นทางการซึ่งแสดงด้วยคีย์สตรีมมิง AES-GCM-HKDF ซึ่งเข้ารหัสในรูปแบบโปรโตเป็น 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{Salt}\) ความยาว
\(\mathrm{DerivedKeySize}\) และสตริงแบบสุ่มแบบเดียวกัน \(\mathrm{NoncePrefix}\)
ความยาว 7
จากนั้นเราจะตั้งค่า \(\mathrm{Header} := \mathrm{len}(\mathrm{Header}) \| \mathrm{Salt}
\| \mathrm{NoncePrefix}\)โดยเข้ารหัสความยาวของส่วนหัวเป็นไบต์เดี่ยว โปรดทราบว่า \(\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}\) ในการเข้ารหัส Big-endian จะเท่ากับ 4 ไบต์ และไบต์ $b$ จะเท่ากับ 0x00
หาก $i < n-1$ และ 0x01
หากไม่ใช่
จากนั้นเราจะเข้ารหัส \(M_i\) การใช้ AES-GCM4 , กุญแจอยู่ไหน\(\mathrm{DerivedKey}\)เวกเตอร์การเริ่มต้นคือ \(\mathrm{IV}_i\)และข้อมูลที่เกี่ยวข้องเป็นสตริงว่าง \(C_i\) เป็นผลลัพธ์ของการเข้ารหัสนี้ (เช่น การเชื่อมโยงกันของ \(C\) และ \(T\) ในส่วนที่ 5.2.1.2 ของเอกสารอ้างอิง AES-GCM ที่เชื่อมโยง)
เชื่อมต่อกลุ่มที่เข้ารหัส
สุดท้าย กลุ่มทั้งหมดจะต่อกันเป็น \(\mathrm{Header} \| C_0 \| \cdots \|
C_{n-1}\)ซึ่งเป็นข้อความเข้ารหัสสุดท้าย
การถอดรหัส
การถอดรหัสจะสลับการเข้ารหัส เราใช้ส่วนหัวเพื่อรับ\(\mathrm{NoncePrefix}\)และถอดรหัสข้อความเข้ารหัสแต่ละส่วนแยกกัน
API อาจ (และโดยปกติจะ) อนุญาตให้เข้าถึงแบบสุ่มหรือเข้าถึงตอนต้นของไฟล์โดยไม่ต้องตรวจสอบส่วนท้ายของไฟล์ ซึ่งเกิดจากความตั้งใจ เนื่องจากคุณจะถอดรหัส \(M_i\) จาก \(C_i\)ได้โดยไม่ต้องถอดรหัสบล็อกข้อความเข้ารหัสก่อนหน้าและที่เหลืออยู่ทั้งหมด
อย่างไรก็ตาม API ควรระมัดระวังไม่ให้ผู้ใช้สร้างความสับสนให้กับข้อผิดพลาดในส่วนท้ายของไฟล์และข้อผิดพลาดในการถอดรหัส โดยในทั้ง 2 กรณี API จะต้องแสดงข้อผิดพลาด และการเพิกเฉยต่อความแตกต่างอาจทำให้ฝ่ายตรงข้ามสามารถตัดไฟล์ได้อย่างมีประสิทธิภาพ
การทำให้เป็นอนุกรมและการแยกวิเคราะห์คีย์
หากต้องการทำให้คีย์เป็นอนุกรมในรูปแบบ "Tink Proto" ก่อนอื่นเราจะแมปพารามิเตอร์ในลักษณะที่เห็นได้ชัดเข้ากับ Proto ที่ให้ไว้ที่ aes_gcm_hkdf_streaming.proto ช่อง version
ต้องตั้งค่าเป็น 0 จากนั้นเราจะเรียงลำดับพารามิเตอร์นี้โดยใช้การเรียงอันดับโปรโตตามปกติ และฝังสตริงที่ได้ลงในค่าของช่องของโปรโต KeyData เราตั้งค่าช่อง type_url
เป็น type.googleapis.com/google.crypto.tink.AesGcmHkdfStreamingKey
จากนั้นเราจะตั้งค่า 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 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)"]]