สตรีมมิง AEAD ของ AES-GCM-HKDF

เอกสารนี้ระบุฟังก์ชันทางคณิตศาสตร์อย่างเป็นทางการซึ่งแสดงด้วยคีย์สตรีมมิง AES-GCM-HKDF ซึ่งเข้ารหัสในรูปแบบ Proto เป็น 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

ปัญหาที่ทราบแล้ว

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

รายการอ้างอิง


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

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

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

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