คีย์ห่อด้วยฮาร์ดแวร์

เช่นเดียวกับซอฟต์แวร์เข้ารหัสดิสก์และไฟล์ส่วนใหญ่ การเข้ารหัสพื้นที่เก็บข้อมูลของ Android มักจะอาศัยคีย์การเข้ารหัสดิบที่มีอยู่ในหน่วยความจำระบบเพื่อให้สามารถดำเนินการเข้ารหัสได้ แม้ว่าการเข้ารหัสจะดำเนินการโดยฮาร์ดแวร์เฉพาะแทนที่จะเป็นซอฟต์แวร์ แต่โดยทั่วไปซอฟต์แวร์ยังคงต้องจัดการคีย์การเข้ารหัสแบบ Raw

โดยปกติแล้วสิ่งนี้จะไม่ถูกมองว่าเป็นปัญหา เนื่องจากคีย์จะไม่ปรากฏในระหว่างการโจมตีแบบออฟไลน์ ซึ่งเป็นการโจมตีประเภทหลักที่การเข้ารหัสพื้นที่เก็บข้อมูลมีจุดประสงค์เพื่อป้องกัน อย่างไรก็ตาม มีความปรารถนาที่จะเพิ่มการป้องกันการโจมตีประเภทอื่นๆ เช่น การโจมตีด้วย Cold Boot และการโจมตีออนไลน์ที่ผู้โจมตีอาจทำให้หน่วยความจำระบบรั่วไหลโดยไม่กระทบต่ออุปกรณ์อย่างเต็มที่

เพื่อแก้ไขปัญหานี้ Android 11 ได้แนะนำการรองรับ คีย์ที่หุ้มด้วยฮาร์ดแวร์ ซึ่งมีการรองรับฮาร์ดแวร์อยู่ คีย์ที่หุ้มด้วยฮาร์ดแวร์คือคีย์การจัดเก็บที่รู้จักในรูปแบบดิบเท่านั้นสำหรับฮาร์ดแวร์เฉพาะ ซอฟต์แวร์จะเห็นและใช้งานได้กับคีย์เหล่านี้ในรูปแบบที่ห่อหุ้ม (เข้ารหัส) เท่านั้น ฮาร์ดแวร์นี้จะต้องสามารถสร้างและนำเข้าคีย์การจัดเก็บข้อมูล การห่อคีย์การจัดเก็บข้อมูลในรูปแบบชั่วคราวและระยะยาว การสืบทอดคีย์ย่อย การตั้งโปรแกรมคีย์ย่อยหนึ่งรายการโดยตรงลงในกลไกการเข้ารหัสลับแบบอินไลน์ และการส่งคืนคีย์ย่อยแยกต่างหากไปยังซอฟต์แวร์

หมายเหตุ : กลไกการเข้ารหัสแบบอินไลน์ (หรือ ฮาร์ดแวร์การเข้ารหัสแบบอินไลน์ ) หมายถึงฮาร์ดแวร์ที่เข้ารหัส/ถอดรหัสข้อมูลในขณะที่กำลังเดินทางไป/จากอุปกรณ์จัดเก็บข้อมูล โดยปกติแล้ว นี่คือตัวควบคุมโฮสต์ UFS หรือ eMMC ที่ใช้ส่วนขยายการเข้ารหัสลับที่กำหนดโดยข้อกำหนด JEDEC ที่เกี่ยวข้อง

ออกแบบ

ส่วนนี้นำเสนอการออกแบบคุณลักษณะคีย์ที่หุ้มด้วยฮาร์ดแวร์ รวมถึงการสนับสนุนฮาร์ดแวร์ที่จำเป็นสำหรับคุณลักษณะดังกล่าว การสนทนานี้มุ่งเน้นไปที่ การเข้ารหัสตามไฟล์ (FBE) แต่โซลูชันนี้ใช้ได้กับ การเข้ารหัสข้อมูลเมตา ด้วย

วิธีหนึ่งในการหลีกเลี่ยงการใช้คีย์เข้ารหัสดิบในหน่วยความจำระบบคือ เก็บไว้ในช่องคีย์ของเอ็นจิ้นการเข้ารหัสลับแบบอินไลน์เท่านั้น อย่างไรก็ตาม วิธีการนี้ประสบปัญหาบางประการ:

  • จำนวนคีย์เข้ารหัสอาจเกินจำนวนช่องคีย์
  • เอ็นจิ้นการเข้ารหัสลับแบบอินไลน์สามารถใช้เพื่อเข้ารหัส/ถอดรหัสบล็อกข้อมูลบนดิสก์ทั้งหมดเท่านั้น อย่างไรก็ตาม ในกรณีของ FBE ซอฟต์แวร์ยังคงจำเป็นต้องสามารถทำงานด้านการเข้ารหัสอื่นๆ ได้ เช่น การเข้ารหัสชื่อไฟล์ และการรับตัวระบุคีย์ ซอฟต์แวร์ยังคงต้องเข้าถึงคีย์ดิบ FBE เพื่อทำงานอื่นนี้

เพื่อหลีกเลี่ยงปัญหาเหล่านี้ คีย์ที่เก็บข้อมูลจะถูกสร้างเป็น คีย์ที่หุ้มด้วยฮาร์ดแวร์ แทน ซึ่งสามารถแกะและใช้งานได้โดยฮาร์ดแวร์เฉพาะเท่านั้น ซึ่งช่วยให้สามารถรองรับคีย์ได้ไม่จำกัดจำนวน นอกจากนี้ ลำดับชั้นของคีย์ได้รับการแก้ไขและย้ายบางส่วนไปยังฮาร์ดแวร์นี้ ซึ่งอนุญาตให้ส่งคืนคีย์ย่อยไปยังซอฟต์แวร์สำหรับงานที่ไม่สามารถใช้กลไกเข้ารหัสลับแบบอินไลน์ได้

ลำดับชั้นที่สำคัญ

คีย์สามารถได้รับมาจากคีย์อื่นโดยใช้ KDF (ฟังก์ชันการรับคีย์) เช่น HKDF ซึ่งส่งผลให้เกิด ลำดับชั้นของคีย์

แผนภาพต่อไปนี้แสดงลำดับชั้นคีย์ทั่วไปสำหรับ FBE เมื่อ ไม่ ได้ใช้คีย์ที่หุ้มด้วยฮาร์ดแวร์:

ลำดับชั้นคีย์ FBE (มาตรฐาน)
รูปที่ 1. ลำดับชั้นคีย์ FBE (มาตรฐาน)

คีย์คลาส FBE คือคีย์การเข้ารหัสดิบที่ Android ส่งไปยังเคอร์เนล Linux เพื่อปลดล็อกชุดไดเร็กทอรีที่เข้ารหัสโดยเฉพาะ เช่น พื้นที่เก็บข้อมูลที่เข้ารหัสข้อมูลประจำตัวสำหรับผู้ใช้ Android โดยเฉพาะ (ในเคอร์เนล คีย์นี้เรียกว่า คีย์หลัก fscrypt ) จากคีย์นี้ เคอร์เนลได้รับคีย์ย่อยต่อไปนี้:

  • ตัวระบุที่สำคัญ สิ่งนี้ไม่ได้ใช้สำหรับการเข้ารหัส แต่เป็นค่าที่ใช้เพื่อระบุคีย์ที่ใช้ป้องกันไฟล์หรือไดเร็กทอรีเฉพาะ
  • คีย์การเข้ารหัสเนื้อหาไฟล์
  • คีย์การเข้ารหัสชื่อไฟล์

ในทางตรงกันข้าม แผนภาพต่อไปนี้แสดงลำดับชั้นคีย์สำหรับ FBE เมื่อใช้คีย์ที่หุ้มด้วยฮาร์ดแวร์:

ลำดับชั้นคีย์ FBE (พร้อมคีย์ที่หุ้มด้วยฮาร์ดแวร์)
รูปที่ 2. ลำดับชั้นคีย์ FBE (พร้อมคีย์ที่หุ้มด้วยฮาร์ดแวร์)

เมื่อเปรียบเทียบกับกรณีก่อนหน้านี้ มีการเพิ่มระดับเพิ่มเติมในลำดับชั้นของคีย์ และคีย์การเข้ารหัสเนื้อหาไฟล์ถูกย้ายตำแหน่งแล้ว โหนดรูทยังคงแสดงถึงคีย์ที่ Android ส่งไปยัง Linux เพื่อปลดล็อกชุดไดเรกทอรีที่เข้ารหัส อย่างไรก็ตาม ขณะนี้คีย์นั้นอยู่ในรูปแบบที่ห่อหุ้มชั่วคราว และจะต้องส่งคีย์นั้นไปยังฮาร์ดแวร์เฉพาะเพื่อใช้งาน ฮาร์ดแวร์นี้ต้องใช้สองอินเทอร์เฟซที่ใช้คีย์ที่ห่อชั่วคราว:

  • อินเทอร์เฟซเดียวเพื่อรับ inline_encryption_key และตั้งโปรแกรมโดยตรงลงในช่องคีย์ของกลไกการเข้ารหัสลับแบบอินไลน์ ซึ่งช่วยให้สามารถเข้ารหัส/ถอดรหัสเนื้อหาไฟล์ได้โดยไม่ต้องมีซอฟต์แวร์เข้าถึงคีย์ดิบ ในเคอร์เนลทั่วไปของ Android อินเทอร์เฟซนี้สอดคล้องกับการดำเนินการ blk_crypto_ll_ops::keyslot_program ซึ่งจะต้องใช้งานโดยไดรเวอร์หน่วยเก็บข้อมูล
  • อินเทอร์เฟซเดียวในการรับและส่งคืน sw_secret ("ความลับของซอฟต์แวร์" - หรือเรียกอีกอย่างว่า "ความลับดิบ" ในบางที่) ซึ่งเป็นคีย์ที่ Linux ใช้ในการรับคีย์ย่อยสำหรับทุกสิ่งนอกเหนือจากการเข้ารหัสเนื้อหาไฟล์ ในเคอร์เนลทั่วไปของ Android อินเทอร์เฟซนี้สอดคล้องกับการดำเนินการ blk_crypto_ll_ops::derive_sw_secret ซึ่งต้องใช้โดยไดรเวอร์การจัดเก็บข้อมูล

หากต้องการรับ inline_encryption_key และ sw_secret จากคีย์หน่วยเก็บข้อมูลดิบ ฮาร์ดแวร์ต้องใช้ KDF ที่มีการเข้ารหัสที่แข็งแกร่ง KDF นี้ต้องเป็นไปตามแนวทางปฏิบัติที่ดีที่สุดในการเข้ารหัส จะต้องมีความแข็งแกร่งด้านความปลอดภัยอย่างน้อย 256 บิต ซึ่งเพียงพอสำหรับอัลกอริทึมใดๆ ที่ใช้ในภายหลัง นอกจากนี้ยังต้องใช้ป้ายกำกับ บริบท และ/หรือสตริงข้อมูลเฉพาะแอปพลิเคชันที่แตกต่างกันเมื่อได้รับคีย์ย่อยแต่ละประเภท เพื่อรับประกันว่าคีย์ย่อยที่เป็นผลลัพธ์จะถูกแยกโดยการเข้ารหัส กล่าวคือ ความรู้เกี่ยวกับหนึ่งในนั้นจะไม่เปิดเผยสิ่งอื่นใด ไม่จำเป็นต้องขยายคีย์ เนื่องจากคีย์ที่เก็บข้อมูลดิบนั้นเป็นคีย์แบบสุ่มที่สม่ำเสมออยู่แล้ว

ในทางเทคนิคแล้ว KDF ใดๆ ที่ตรงตามข้อกำหนดด้านความปลอดภัยสามารถใช้ได้ อย่างไรก็ตาม เพื่อวัตถุประสงค์ในการทดสอบ จำเป็นต้องนำ KDF เดิมไปใช้ใหม่ในโค้ดทดสอบ ปัจจุบัน KDF หนึ่งรายการได้รับการตรวจสอบและนำไปใช้แล้ว สามารถพบได้ใน ซอร์สโค้ดสำหรับ vts_kernel_encryption_test ขอแนะนำให้ฮาร์ดแวร์ใช้ KDF นี้ ซึ่งใช้ NIST SP 800-108 "KDF ในโหมดตัวนับ" โดยมี AES-256-CMAC เป็น PRF โปรดทราบว่าเพื่อให้เข้ากันได้ ทุกส่วนของอัลกอริทึมจะต้องเหมือนกัน รวมถึงตัวเลือกบริบท KDF และป้ายกำกับสำหรับแต่ละคีย์ย่อย

การห่อกุญแจ

เพื่อให้บรรลุเป้าหมายด้านความปลอดภัยของคีย์ที่หุ้มด้วยฮาร์ดแวร์ จึงมีการกำหนดการตัดคีย์สองประเภท:

  • การปิดชั่วคราว : ฮาร์ดแวร์เข้ารหัสคีย์ดิบโดยใช้คีย์ที่สร้างขึ้นแบบสุ่มทุกครั้งที่บูตและไม่ได้เปิดเผยภายนอกฮาร์ดแวร์โดยตรง
  • การห่อระยะยาว : ฮาร์ดแวร์เข้ารหัสคีย์ดิบโดยใช้คีย์ถาวรที่ไม่ซ้ำใครซึ่งสร้างไว้ในฮาร์ดแวร์ซึ่งไม่ได้เปิดเผยโดยตรงภายนอกฮาร์ดแวร์

คีย์ทั้งหมดที่ส่งผ่านไปยังเคอร์เนล Linux เพื่อปลดล็อกที่เก็บข้อมูลจะถูกห่อหุ้มชั่วคราว สิ่งนี้ทำให้แน่ใจได้ว่าหากผู้โจมตีสามารถแยกคีย์ที่ใช้งานออกจากหน่วยความจำระบบได้ คีย์นั้นจะใช้งานไม่ได้ไม่เพียงแต่นอกอุปกรณ์เท่านั้น แต่ยังรวมไปถึงในอุปกรณ์หลังจากรีบูตด้วย

ในเวลาเดียวกัน Android ยังคงต้องสามารถจัดเก็บคีย์เวอร์ชันที่เข้ารหัสไว้บนดิสก์เพื่อให้สามารถปลดล็อคได้ตั้งแต่แรก คีย์ดิบจะทำงานเพื่อจุดประสงค์นี้ อย่างไรก็ตาม ไม่ควรจะมีคีย์ดิบอยู่ในหน่วยความจำระบบเลย เพื่อไม่ให้มีการแตกคีย์ออกมาเพื่อใช้นอกอุปกรณ์ แม้ว่าจะแตกออกมาตอนบูตเครื่องก็ตาม ด้วยเหตุนี้ แนวคิดของการห่อระยะยาวจึงถูกกำหนดไว้

เพื่อรองรับการจัดการคีย์ที่ห่อหุ้มด้วยสองวิธีที่แตกต่างกันนี้ ฮาร์ดแวร์จะต้องใช้อินเทอร์เฟซต่อไปนี้:

  • อินเทอร์เฟซสำหรับสร้างและนำเข้าคีย์การจัดเก็บข้อมูล โดยส่งคืนในรูปแบบการห่อระยะยาว อินเทอร์เฟซเหล่านี้เข้าถึงได้ทางอ้อมผ่าน KeyMint และสอดคล้องกับแท็ก TAG_STORAGE_KEY KeyMint ความสามารถ "สร้าง" ถูกใช้โดย vold เพื่อสร้างคีย์การจัดเก็บใหม่สำหรับใช้งานโดย Android ในขณะที่ vts_kernel_encryption_test ใช้ความสามารถ "นำเข้า" เพื่อนำเข้าคีย์ทดสอบ
  • อินเทอร์เฟซสำหรับแปลงคีย์หน่วยเก็บข้อมูลแบบรวมระยะยาวเป็นคีย์หน่วยเก็บข้อมูลแบบรวมชั่วคราว ซึ่งสอดคล้องกับวิธีการ convertStorageKeyToEphemeral KeyMint วิธีการนี้ใช้ทั้ง vold และ vts_kernel_encryption_test เพื่อปลดล็อคที่เก็บข้อมูล

อัลกอริธึมการห่อคีย์เป็นรายละเอียดการใช้งาน แต่ควรใช้ AEAD ที่แข็งแกร่ง เช่น AES-256-GCM พร้อม IV แบบสุ่ม

จำเป็นต้องเปลี่ยนแปลงซอฟต์แวร์

AOSP มีเฟรมเวิร์กพื้นฐานสำหรับรองรับคีย์ที่หุ้มด้วยฮาร์ดแวร์อยู่แล้ว ซึ่งรวมถึงการสนับสนุนในส่วนประกอบพื้นที่ผู้ใช้ เช่น vold เช่นเดียวกับการสนับสนุนเคอร์เนล Linux ใน blk-crypto , fscrypt และ dm-default-key

อย่างไรก็ตาม จำเป็นต้องมีการเปลี่ยนแปลงเฉพาะการใช้งานบางอย่าง

คีย์มินต์มีการเปลี่ยนแปลง

การใช้งาน KeyMint ของอุปกรณ์ต้องได้รับการแก้ไขเพื่อรองรับ TAG_STORAGE_KEY และใช้เมธอด convertStorageKeyToEphemeral

ใน Keymaster มีการใช้ exportKey แทน convertStorageKeyToEphemeral

การเปลี่ยนแปลงเคอร์เนล Linux

ต้องแก้ไขไดรเวอร์เคอร์เนล Linux สำหรับเอ็นจิ้นการเข้ารหัสแบบอินไลน์ของอุปกรณ์เพื่อรองรับคีย์ที่หุ้มด้วยฮาร์ดแวร์

สำหรับ android14 และเคอร์เนลที่สูงกว่า ให้ตั้งค่า BLK_CRYPTO_KEY_TYPE_HW_WRAPPED ใน blk_crypto_profile::key_types_supported สร้าง blk_crypto_ll_ops::keyslot_program และ blk_crypto_ll_ops::keyslot_evict รองรับการเขียนโปรแกรม/ขับไล่คีย์ที่หุ้มด้วยฮาร์ดแวร์ และใช้ blk_crypto_ll_ops::derive_sw_secret

สำหรับเคอร์เนล android12 และ android13 ให้ตั้งค่า BLK_CRYPTO_FEATURE_WRAPPED_KEYS ใน blk_keyslot_manager::features สร้าง blk_ksm_ll_ops::keyslot_program และ blk_ksm_ll_ops::keyslot_evict รองรับการเขียนโปรแกรม/ขับไล่คีย์ที่หุ้มด้วยฮาร์ดแวร์ และใช้ blk_ksm_ll_ops::derive_raw_secret

สำหรับเคอร์เนล android11 ​​ให้ตั้งค่า BLK_CRYPTO_FEATURE_WRAPPED_KEYS ใน keyslot_manager::features สร้าง keyslot_mgmt_ll_ops::keyslot_program และ keyslot_mgmt_ll_ops::keyslot_evict รองรับการเขียนโปรแกรม/กำจัดคีย์ที่หุ้มด้วยฮาร์ดแวร์ และใช้ keyslot_mgmt_ll_ops::derive_raw_secret

การทดสอบ

แม้ว่าการเข้ารหัสด้วยคีย์ที่หุ้มด้วยฮาร์ดแวร์จะทดสอบได้ยากกว่าการเข้ารหัสด้วยคีย์มาตรฐาน แต่ก็ยังสามารถทดสอบได้โดยการนำเข้าคีย์ทดสอบและปรับใช้การรับคีย์ที่ฮาร์ดแวร์ทำอีกครั้ง สิ่งนี้ถูกนำไปใช้ใน vts_kernel_encryption_test หากต้องการรันการทดสอบนี้ ให้รัน:

atest -v vts_kernel_encryption_test

อ่านบันทึกการทดสอบและตรวจสอบว่ากรณีทดสอบคีย์ที่หุ้มด้วยฮาร์ดแวร์ (เช่น FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy และ DmDefaultKeyTest.TestHwWrappedKey ) ไม่ได้ถูกข้ามเนื่องจากไม่พบการรองรับคีย์ที่หุ้มด้วยฮาร์ดแวร์ เนื่องจากผลการทดสอบจะยังคง "ส่งผ่าน" ใน กรณีนั้น

กำลังเปิดใช้งาน

เมื่อการสนับสนุนคีย์ที่หุ้มด้วยฮาร์ดแวร์ของอุปกรณ์ทำงานอย่างถูกต้อง คุณสามารถทำการเปลี่ยนแปลงต่อไปนี้ในไฟล์ fstab ของอุปกรณ์เพื่อให้ Android ใช้สำหรับ FBE และการเข้ารหัสข้อมูลเมตา:

  • FBE: เพิ่มแฟล็ก wrappedkey_v0 ให้กับพารามิเตอร์ fileencryption ตัวอย่างเช่น ใช้ fileencryption=::inlinecrypt_optimized+wrappedkey_v0 สำหรับรายละเอียดเพิ่มเติม โปรดดู เอกสาร FBE
  • การเข้ารหัสข้อมูลเมตา: เพิ่มแฟล็ก wrappedkey_v0 ให้กับพารามิเตอร์ metadata_encryption ตัวอย่างเช่น ใช้ metadata_encryption=:wrappedkey_v0 สำหรับรายละเอียดเพิ่มเติม โปรดดู เอกสารประกอบการเข้ารหัสข้อมูลเมตา