สตรีมมิง 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}\) มี 4 ไบต์ในการเข้ารหัสแบบ Big-endian และ ไบต์ $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" ก่อนอื่นเราจะแมปพารามิเตอร์ในลักษณะที่ชัดเจนไปยังโปรโตที่ระบุไว้ที่ 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 หรือค่าก่อนหน้า

หากต้องการแยกวิเคราะห์คีย์ เราจะย้อนกระบวนการข้างต้น (ตามปกติเมื่อแยกวิเคราะห์ Proto) ระบบจะละเว้นช่อง key_material_type ระบบอาจละเว้นค่าของ output_prefix_type หรือปฏิเสธคีย์ที่มี output_prefix_type แตกต่างจาก RAW ระบบต้องปฏิเสธคีย์ที่มี version แตกต่างจาก 0

ปัญหาที่ทราบ

การใช้งานฟังก์ชันการเข้ารหัสข้างต้นไม่น่าจะแยกสาขาได้ ดูความปลอดภัยของ Fork

ข้อมูลอ้างอิง


  1. Hoang, Reyhanitabar, Rogaway, Vizar, 2015. การเข้ารหัสที่ตรวจสอบสิทธิ์ทางออนไลน์ และการป้องกันการละเมิดการใช้ Nonce ซ้ำ CRYPTO 2015 https://eprint.iacr.org/2015/189 

  2. Hoang, Shen, 2020. ความปลอดภัยของการเข้ารหัสสตรีมมิงในไลบรารี Tink ของ Google https://eprint.iacr.org/2020/1019 

  3. RFC 5869 ฟังก์ชันการสร้างคีย์แบบแยกและขยาย (HKDF) ที่ใช้ HMAC https://www.rfc-editor.org/rfc/rfc5869 

  4. NIST SP 800-38D คําแนะนําสําหรับโหมดการทํางานของการเข้ารหัสบล็อก: โหมด Galois/Counter (GCM) และ GMAC https://csrc.nist.gov/pubs/sp/800/38/d/final