1 คะแนน โดย GN⁺ 2024-06-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • XAES-256-GCM คือสเปก AEAD ใหม่ที่ใช้คีย์ 256 บิตและ nonce 192 บิต เพื่อทำให้การจัดการ nonce ใน API การเข้ารหัสระดับสูงมีความเสี่ยงน้อยลง
  • nonce ที่ใหญ่ขึ้นทำให้สามารถใช้วิธีสร้างค่าใหม่อัตโนมัติจาก CSPRNG ของระบบปฏิบัติการสำหรับทุกข้อความได้ โดยตั้งเป้าความเสี่ยงการชนกันไว้ที่ระดับ 2⁻³² สำหรับข้อความ 2⁸⁰ รายการ
  • ภายในเป็นโครงสร้าง nonce ขยายที่สร้างคีย์อนุพันธ์และ nonce 96 บิตจากคีย์อินพุตและ nonce ขนาดใหญ่ แล้วใช้ AES-256-GCM มาตรฐานตามเดิม
  • อิมพลีเมนต์อ้างอิงของ Go ใช้เพียง crypto/cipher และ crypto/aes และมีขนาดไม่ถึง 100 บรรทัด โดยการเรียกใช้ AES-256 ต่อข้อความ 3 ครั้งนั้นบางส่วนสามารถคำนวณล่วงหน้าได้
  • ด้วยการให้ความสำคัญกับการปฏิบัติตาม FIPS 140 และความเข้ากันได้กับไลบรารี จึงสามารถใช้เป็นตัวเลือกสำหรับ API AEAD แบบไม่ต้องจัดการ nonce ร่วมกับ XChaCha20Poly1305 และ AES-GCM-SIV ได้

AEAD ที่มี nonce ขนาดใหญ่สำหรับ API ระดับสูง

  • XAES-256-GCM เป็นอัลกอริทึม authenticated encryption with associated data (AEAD) ที่ใช้คีย์ 256 บิตและ nonce 192 บิต
  • เป้าหมายของการออกแบบสรุปได้เป็น 3 ข้อ
    • รองรับ nonce ขนาดใหญ่ ที่การสุ่มสร้างยังปลอดภัย แม้จำนวนข้อความจะมากจนแทบไม่จำกัด
    • ปฏิบัติตาม FIPS 140 ได้อย่างสมบูรณ์และตรงไปตรงมา
    • ติดตั้งใช้งานได้ง่ายบนไลบรารีเข้ารหัสทั่วไป
  • ด้วย nonce ขนาดใหญ่ จึงสามารถสร้าง API ที่อ่าน nonce ใหม่จาก CSPRNG ของระบบปฏิบัติการสำหรับทุกข้อความได้ โดยไม่ต้องให้ผู้ใช้คำนวณ birthday bound เอง
  • โดยให้ความสำคัญกับการปฏิบัติตามข้อกำหนดและความเข้ากันได้ก่อน จึงนำไปใช้กับงานที่ต้องการ AEAD ได้แม้ในสภาพแวดล้อมที่ใช้ AEAD แบบ nonce ใหญ่ชนิดอื่นได้ยาก

โครงสร้าง nonce ขยายที่ใช้ AES-256-GCM เดิมโดยตรง

  • XAES-256-GCM เป็น โครงสร้าง nonce ขยาย ที่วางอยู่บน AEAD เดิม เช่นเดียวกับ XChaCha20Poly1305
  • คำนวณคีย์อนุพันธ์และ nonce อนุพันธ์สำหรับ AES-256-GCM ภายในจากคีย์อินพุตและ nonce 192 บิต
    • คีย์อินพุตและ nonce คือ K, N
    • คีย์และ nonce อนุพันธ์สำหรับ AES-256-GCM คือ Kₓ, Nₓ
  • สร้าง Kₓ ด้วยการเรียก AES-256ₖ และส่วนหน้าของ nonce และใช้ 96 บิตท้ายของ nonce อินพุตเป็น Nₓ
  • ต้องเรียก AES-256ₖ 3 ครั้งต่อข้อความ
    • หนึ่งในนั้นสามารถ คำนวณล่วงหน้า ได้สำหรับคีย์ที่กำหนด
    • อีกสองครั้งสามารถใช้ key schedule เดียวกันซ้ำได้

การติดตั้งใช้งานสั้นและอธิบายได้ด้วยองค์ประกอบมาตรฐาน

  • อิมพลีเมนต์อ้างอิงของ Go มีทั้งการปรับให้เหมาะสมด้วยการคำนวณล่วงหน้าและ boilerplate ส่วนใหญ่ โดยมีขนาดไม่ถึง 100 บรรทัด
  • อิมพลีเมนต์ของ Go ใช้เพียง crypto/cipher และ crypto/aes จากไลบรารีมาตรฐาน
  • XAES-256-GCM ยังสามารถอธิบายได้ด้วย KDF มาตรฐาน NIST SP 800-108r1 และ NIST AES-256-GCM AEAD มาตรฐาน
    • KDF เป็น counter-based KDF
    • PRF คือ CMAC-AES256
    • คีย์อินพุตคือ Kin
    • label คืออักขระ ASCII X หรือ 0x58
    • context คือ 96 บิตแรกของ nonce อินพุต
    • ขนาด counter คือ 16 บิต
    • ละเว้นฟิลด์ L แบบเลือกได้
    • เอาต์พุตคือคีย์อนุพันธ์ 256 บิต
  • คีย์อนุพันธ์และ 96 บิตสุดท้ายของ nonce อินพุตจะถูกป้อนเข้า AES-256-GCM
  • จากการเลือกพารามิเตอร์นี้ เมื่อตัดชั้นนามธรรมของ KDF และ CMAC ออก ก็จะช้ากว่าและซับซ้อนกว่าวิธีเรียก AES-256 กับ counter โดยตรงเพียงเล็กน้อย
  • พารามิเตอร์ชุดเดียวกันนี้ยังรองรับใน API ระดับสูงของ OpenSSL ด้วย

อิมพลีเมนต์ของบุคคลที่สามและ test vectors

  • ณ การแก้ไขวันที่ 2024-06-29 มีการเพิ่มอิมพลีเมนต์ของบุคคลที่สามแล้ว
  • อิมพลีเมนต์ของ Web Cryptography API ใช้ CryptoKey แบบ AES-CBC 256 บิต
  • ในสเปกมี test vectors สำหรับสอง code path หลัก
    • MSB₁(L) = 0
    • MSB₁(L) = 1
  • ยังมี accumulated randomized tests ที่สรุปการสุ่มทำซ้ำ 10,000 ครั้งหรือ 1,000,000 ครั้งด้วย

การยกเลิก /11 และทางเลือกอื่น

  • แนวคิดก่อนหน้านี้เคยใช้ชื่อ XAES-256-GCM/11 แต่ในสเปกสุดท้ายได้ยกเลิก /11
  • /11 เป็นการปรับประสิทธิภาพ และเนื่องจากหนึ่งในเหตุผลที่ใช้ AES-GCM คือ การปฏิบัติตาม FIPS 140 การเปลี่ยนจำนวนรอบจึงทำให้สูญเสียความสอดคล้องตามข้อกำหนด
  • หากไม่ได้มีเป้าหมายเรื่องการปฏิบัติตาม FIPS 140 ก็มีทางเลือกหลายแบบ
    • AES-GCM-SIV
    • โครงสร้าง AEAD สมัยใหม่ที่อิงกับ AES core
  • ส่วน Alternatives ในสเปกเปรียบเทียบแต่ละทางเลือกกับ XAES-256-GCM

ตำแหน่งใน Go และ API AEAD แบบไม่ต้องจัดการ nonce

  • XAES-256-GCM มุ่งเป็น AEAD ที่ปลอดภัย เรียบง่าย ปฏิบัติตามข้อกำหนดได้ และทำงานร่วมกันได้
  • กรณีใช้งานหลักคือ API ระดับสูง แบบที่ต้องการเพิ่มเข้าไปใน Go
  • XAES-256-GCM ถูกออกแบบมาให้เสริม XChaCha20Poly1305 และ AES-GCM-SIV และเป็นผู้สมัครสำหรับการติดตั้งใช้งาน API AEAD แบบไม่ต้องจัดการ nonce ตามสมมุติฐาน
  • เนื่องจากไม่ต้องการเพิ่มโครงสร้างเฉพาะของ Go เข้าไปในไลบรารีมาตรฐานของ Go จึงต้องการความคิดเห็นจากผู้ดูแลไลบรารีเข้ารหัสรายอื่น

1 ความคิดเห็น

 
GN⁺ 2024-06-30
ความคิดเห็นบน Hacker News
  • การออกแบบฉลาดมาก: เพราะเป็นแบบ อิง CMAC จึงสามารถ derive คีย์ด้วย AES-CBC ได้แม้ไม่มี primitive ระดับล่าง
    หากมองจากมุมของ AES-CBC จะเริ่มด้วย L = AES-CBC-256ₖ(iv = 0¹²⁸, plaintext = 0¹²⁸)[:16] เพื่อสร้าง K1 จากนั้นเข้ารหัส M1, M2 เพื่อได้ Kₓ แล้วใช้ Nₓ = N[12:] ประมาณนี้
    มองได้ว่า AES-CBC-256 คืนเฉพาะบล็อก 128 บิตแรกของ ciphertext แล้วทิ้งบล็อก padding ไป และแม้จะปิด padding ไม่ได้ ก็แค่ต้องเรียก AES ด้วยคีย์เดียวกันเพิ่มอีก 3 ครั้งเมื่อเทียบกับการใช้งานระดับล่าง ซึ่งก็ไม่ได้แย่นัก
    มี implementation บน JS ที่ใช้ WebCrypto API โดยอาศัยคุณสมบัตินี้อยู่ที่ https://github.com/dchest/xaes และยังรองรับคุณสมบัติของ CryptoKey เช่น รับ CryptoKey สำหรับ AES-CBC มาใช้ได้ตรง ๆ และบันทึกลง IndexedDB ด้วย extractable=false

    • ในวงการนี้มันคงดูเหมือน notation มาตรฐาน แต่พูดตรง ๆ ผมไม่ชอบ สัญลักษณ์ทางวิทยาการเข้ารหัส เลย
      ใน pseudocode นั้น ดูเหมือนว่าครึ่งหนึ่งของตัวเลขนับเป็นจำนวนไบต์ ส่วนอีกครึ่งนับเป็นจำนวนบิต และถ้าไม่รู้ algorithm อยู่แล้วแทบจะบอกไม่ได้ว่าอันไหนเป็นอันไหน
      เช่น N[:12] ดูเหมือน 12 ไบต์ แต่ 0¹²⁸ คือ 16 ไบต์ และ X คืออักขระจริง ๆ 'X' หรือบิตสตริง 01011000 แต่ L เป็นตัวแปร ไม่ใช่บิตสตริง 01001100
      ดูชัดเจนว่านักคณิตศาสตร์ไม่ได้ชอบ notation ที่ไม่กำกวม เท่าคนสายวิทยาการคอมพิวเตอร์
    • ค่าคงที่ที่ดูน่าขนลุกอย่าง 0¹²⁰10000111 คือบิตสตริงที่แทนสัมประสิทธิ์ของพหุนามตัวแรกตามลำดับพจนานุกรม ในบรรดาพหุนามลดทอนไม่ได้ดีกรี b ที่มีจำนวนเทอมที่ไม่เป็นศูนย์น้อยที่สุด สำหรับ ขนาดบล็อกของ block cipher ระดับล่าง b ที่ CMAC ใช้
      ไม่มีเจตนาแอบแฝงอะไร
    • ไม่ใช่ผู้เชี่ยวชาญด้านคริปโต แต่สรุปแล้วเข้าใจว่า AES-GCM AEAD มาตรฐานจะพังอย่างร้ายแรงถ้าใช้ nonce เดียวกันซ้ำสองครั้งกับคนละข้อความ[1] และขนาด nonce ในหลายกรณีก็เล็กเกินไปสำหรับการใช้ nonce แบบสุ่มอย่างปลอดภัย
      งานนี้ช่วยหลีกเลี่ยงปัญหานั้นได้ง่าย ๆ ด้วยการเปลี่ยนทั้งคีย์และ nonce ในการเรียก AES-GCM แต่ละครั้ง
      อีกทั้งถ้ามี AES-GCM ก็ใช้เพียง AES “ธรรมดา” ที่โดยทั่วไปหาได้อยู่แล้ว และเลี่ยงโครงสร้างซับซ้อนใหม่ ๆ ที่อาจยังมีจุดอ่อน
      overhead ต่อข้อความคือบัฟเฟอร์เล็ก ๆ 2 ชุดที่ต้องเข้ารหัส/ถอดรหัสด้วย AES “ธรรมดา” และ nonce ที่ยาวขึ้นเป็น 192 บิต
      [1]: https://frereit.de/aes_gcm/
  • ดูเหมือนจะขจัดกับดักของ AES-GCM แบบดั้งเดิมที่เมื่อใช้ nonce แบบสุ่มแล้วต้อง เปลี่ยนคีย์ทุก ๆ ประมาณ 2^32 ข้อความ
    ใน AES-GCM การชนกันของ nonce เป็นเรื่องร้ายแรง และอย่างน้อยก็ทำให้ผู้โจมตีสามารถเซ็นข้อความใด ๆ ได้
    ไม่ได้จำเป็นต้องใช้ nonce แบบสุ่มเสมอไป แต่โดยทั่วไปมักแนะนำ และการทำให้ สอดคล้องกับ FIPS ด้วย primitive สองอย่างคือ counter-based key derivation function กับ GCM ดั้งเดิม ถือว่าค่อนข้างฉลาด

    • พูดให้แม่นคือ วิธีนี้ทำให้ nonce แบบสุ่ม ปลอดภัยตั้งแต่แรก
      ใน AES-GCM มาตรฐาน 96 บิตไม่เพียงพอสำหรับหลีกเลี่ยงการชนกันแบบสุ่ม จึงต้องใช้การสร้าง nonce แบบกำหนดแน่นอน
      อีกทั้งไม่ว่าจะสร้าง nonce อย่างไร เมื่อ counter rollover บล็อกถัดไปก็จะใช้ nonce+counter เหมือนกับบล็อกแรก ดังนั้นหลังจาก 2^32 บล็อกจึงต้องเปลี่ยน nonce หรือคีย์
  • ยอดเยี่ยมจริง ๆ ถ้ามีสิ่งนี้ตอนที่ผมทำ encrypted filesystem ครั้งสุดท้ายเมื่อไม่กี่ปีก่อนก็คงดี
    ในการ deploy filesystem ขนาดใหญ่ การชนกันของ nonce เป็นเรื่องน่ากังวลมาก
    2^32 ดูเหมือนเยอะ แต่ถ้าเป็น array ระดับ PB ที่เขียนด้วย 100k IOPS ต่อวินาทีและพึ่งพาความสุ่มของ pseudo-random generator โอกาสชนกันแทบจะรับประกันได้เลย

    • การแข่งขัน CAESAR[1] จบไปในปี 2019 และผลลัพธ์ก็มี AEAD หลายตัวที่มีพื้นที่ nonce เพียงพอ
      [1] https://en.m.wikipedia.org/wiki/CAESAR_Competition
    • แต่สงสัยว่าทำไม การชนกันของ nonce ถึงเป็นปัญหา
      มันก็แค่หมายความว่าสองบล็อกใช้คีย์เข้ารหัสเดียวกันร่วมกันไม่ใช่หรือ?
      ถ้าไม่รู้ plaintext ของบล็อกใดเลย ก็ไม่เห็นว่ามันจะทำให้ความปลอดภัยของระบบอ่อนลงอย่างไร
  • อยากให้สิ่งนี้ถูกใช้กับ variant ของ age ที่สอดคล้องกับ FIPS สำหรับการเข้ารหัสไฟล์เพื่อเก็บถาวร
    ในการ audit ของภาคธนาคาร age ถูกปฏิเสธสำหรับ use case นี้เพราะใช้ ChaCha และพวกเขาเห็นว่าส่วน public key X25519 ของ age ใช้ได้ ผมเข้าใจว่า X25519 เพิ่งได้รับการอนุมัติจาก NIST เมื่อไม่นานมานี้
    ไม่มีประสบการณ์กับ Go แต่ดูจากสเปกของ age แล้วน่าจะเสียบเข้าไปได้ทันที และถ้ามีเวลาอาจลองทำดู
    ชื่อน่าจะเรียกว่า “cage” ในความหมายของ “compliant actually good encryption”
    1: https://github.com/FiloSottile/age

    • “compliant actually good encryption” อาจเป็น ปฏิพจน์ในตัวเอง ก็ได้
    • ตรวจดูแล้วเหมือนว่า Ed25519 ได้รับอนุมัติแล้ว (FIPS 186-5) แต่ X25519 ดูเหมือนว่ายังไม่ใช่
    • สำหรับอ้างอิง ตอนนี้ไลบรารีมาตรฐานของ Go ยังไม่มี implementation ของ XAES มีแค่ reference implementation ของ C2SP
    • bge, bureaucratic good encryption
  • ในฐานะคนที่ไม่ใช่นักคริปโต ผมสงสัยว่าทำไมใช้ nonce 192 บิต ไม่ใช้ 256 บิต
    ในแอปพลิเคชันจริง ไม่น่าจะมองว่าบิตเพิ่มเติมเหล่านั้นเป็นต้นทุน

    • ไม่มีพื้นที่ให้ใส่ 256 บิต 192 บิตประกอบด้วย 96 บิตจากพื้นที่ nonce ส่วนล่าง และอีก 96 บิตที่ใส่ลงใน บล็อก CMAC 128 บิตพร้อมกับ prefix ที่จำเป็น
      อาจทำให้อินพุต CMAC ยาวกว่านี้ได้ แต่จะต้องรันฟังก์ชันบล็อก AES-256 หลายครั้งขึ้น และจะเจอปัญหายุ่งยากเรื่องการควบคุมคีย์ใน CMAC key derivation function ด้วย
      เหตุผลก็คล้ายกับที่ XChaCha20Poly1305 ใช้ nonce 192 บิต และการสอดคล้องกับ extended-nonce AEAD หลัก ๆ ตัวอื่นก็เป็นข้อดีเล็กน้อยด้วย
  • บอกว่า “ความเสี่ยงชนกัน 2⁻³² เมื่อมีข้อความ 2⁸⁰ ข้อความ” แต่จะไม่เกิดปัญหาก่อนหน้านั้นหรือ จากข้อเท็จจริงที่ว่า ขนาดบล็อกของ AES มีแค่ 128 บิต?

    • ถ้าหมายถึง birthday bound ของบล็อก (https://sweet32.info) นั่นเป็นข้อจำกัดต่อจำนวนบล็อกที่เข้ารหัสด้วยคีย์เดียว
      XAES derive คีย์ขนาดใหญ่ต่อข้อความ จึงให้สิ่งที่มักเรียกว่า การรับประกันที่ดีกว่า birthday bound
    • ไม่
      ถ้าไม่มีบริบทละเอียดกว่านี้ว่าทำไมถึงคิดว่าจะเป็นปัญหา ก็คงตอบได้ละเอียดกว่านี้ไม่ได้
  • “ปลอดภัย น่าเบื่อ สอดคล้องข้อกำหนดได้ และทำงานร่วมกันได้”
    นี่คือเทคโนโลยีประเภทที่ผมชอบที่สุด

  • ดีเลย ดีใจที่มี โครงสร้างที่อิง NIST จริง ๆ
    แต่ก็น่าเสียดายที่ต้องยอมเสียฟีเจอร์หลายอย่างที่เป็นข้อดีของ NIST key derivation function เช่น label และ context
    เข้าใจว่าทำไปเพื่อลดจำนวนการเรียก AES ให้น้อยที่สุด แต่โดยเฉพาะถ้าข้อความยาวกว่าสองสามร้อยไบต์ ผมน่าจะให้ความสำคัญกับการแยกทางคริปโตที่แข็งแรง มากกว่าการประหยัดการเรียก AES ไม่กี่ครั้ง
    สุดท้าย nonce GCM แบบสุ่ม ที่ยาวกว่า 96 บิตยังถูกเข้าใจผิดกันมาก และให้การรับประกันที่ดีกว่า nonce 96 บิตจริง ๆ[1]
    แน่นอนว่าถ้า derive คีย์ใหม่ได้ทุกข้อความ ทางนั้นย่อมดีกว่าแน่นอน
    [1] https://neilmadden.blog/2024/05/23/galois-counter-mode-and-r...