เช่นเดียวกับซอฟต์แวร์เข้ารหัสดิสก์และไฟล์ส่วนใหญ่ การเข้ารหัสพื้นที่เก็บข้อมูลของ Android มักจะอาศัยคีย์การเข้ารหัสดิบที่มีอยู่ในหน่วยความจำระบบเพื่อให้สามารถดำเนินการเข้ารหัสได้ แม้ว่าการเข้ารหัสจะดำเนินการโดยฮาร์ดแวร์เฉพาะแทนที่จะเป็นซอฟต์แวร์ แต่โดยทั่วไปซอฟต์แวร์ยังคงต้องจัดการคีย์การเข้ารหัสแบบ Raw
โดยปกติแล้วสิ่งนี้จะไม่ถูกมองว่าเป็นปัญหา เนื่องจากคีย์จะไม่ปรากฏในระหว่างการโจมตีแบบออฟไลน์ ซึ่งเป็นการโจมตีประเภทหลักที่การเข้ารหัสพื้นที่เก็บข้อมูลมีจุดประสงค์เพื่อป้องกัน อย่างไรก็ตาม มีความปรารถนาที่จะเพิ่มการป้องกันการโจมตีประเภทอื่นๆ เช่น การโจมตีด้วย Cold Boot และการโจมตีออนไลน์ที่ผู้โจมตีอาจทำให้หน่วยความจำระบบรั่วไหลโดยไม่กระทบต่ออุปกรณ์อย่างเต็มที่
เพื่อแก้ไขปัญหานี้ Android 11 ได้แนะนำการรองรับ คีย์ที่หุ้มด้วยฮาร์ดแวร์ ซึ่งมีการรองรับฮาร์ดแวร์อยู่ คีย์ที่หุ้มด้วยฮาร์ดแวร์คือคีย์การจัดเก็บที่รู้จักในรูปแบบดิบเท่านั้นสำหรับฮาร์ดแวร์เฉพาะ ซอฟต์แวร์จะเห็นและใช้งานได้กับคีย์เหล่านี้ในรูปแบบที่ห่อหุ้ม (เข้ารหัส) เท่านั้น ฮาร์ดแวร์นี้จะต้องสามารถสร้างและนำเข้าคีย์การจัดเก็บข้อมูล การห่อคีย์การจัดเก็บข้อมูลในรูปแบบชั่วคราวและระยะยาว การสืบทอดคีย์ย่อย การตั้งโปรแกรมคีย์ย่อยหนึ่งรายการโดยตรงลงในกลไกการเข้ารหัสลับแบบอินไลน์ และการส่งคืนคีย์ย่อยแยกต่างหากไปยังซอฟต์แวร์
หมายเหตุ : กลไกการเข้ารหัสแบบอินไลน์ (หรือ ฮาร์ดแวร์การเข้ารหัสแบบอินไลน์ ) หมายถึงฮาร์ดแวร์ที่เข้ารหัส/ถอดรหัสข้อมูลในขณะที่กำลังเดินทางไป/จากอุปกรณ์จัดเก็บข้อมูล โดยปกติแล้ว นี่คือตัวควบคุมโฮสต์ UFS หรือ eMMC ที่ใช้ส่วนขยายการเข้ารหัสลับที่กำหนดโดยข้อกำหนด JEDEC ที่เกี่ยวข้อง
ออกแบบ
ส่วนนี้นำเสนอการออกแบบคุณลักษณะคีย์ที่หุ้มด้วยฮาร์ดแวร์ รวมถึงการสนับสนุนฮาร์ดแวร์ที่จำเป็นสำหรับคุณลักษณะดังกล่าว การสนทนานี้มุ่งเน้นไปที่ การเข้ารหัสตามไฟล์ (FBE) แต่โซลูชันนี้ใช้ได้กับ การเข้ารหัสข้อมูลเมตา ด้วย
วิธีหนึ่งในการหลีกเลี่ยงการใช้คีย์เข้ารหัสดิบในหน่วยความจำระบบคือ เก็บไว้ในช่องคีย์ของเอ็นจิ้นการเข้ารหัสลับแบบอินไลน์เท่านั้น อย่างไรก็ตาม วิธีการนี้ประสบปัญหาบางประการ:
- จำนวนคีย์เข้ารหัสอาจเกินจำนวนช่องคีย์
- เอ็นจิ้นการเข้ารหัสลับแบบอินไลน์สามารถใช้เพื่อเข้ารหัส/ถอดรหัสบล็อกข้อมูลบนดิสก์ทั้งหมดเท่านั้น อย่างไรก็ตาม ในกรณีของ FBE ซอฟต์แวร์ยังคงจำเป็นต้องสามารถทำงานด้านการเข้ารหัสอื่นๆ ได้ เช่น การเข้ารหัสชื่อไฟล์ และการรับตัวระบุคีย์ ซอฟต์แวร์ยังคงต้องเข้าถึงคีย์ดิบ FBE เพื่อทำงานอื่นนี้
เพื่อหลีกเลี่ยงปัญหาเหล่านี้ คีย์ที่เก็บข้อมูลจะถูกสร้างเป็น คีย์ที่หุ้มด้วยฮาร์ดแวร์ แทน ซึ่งสามารถแกะและใช้งานได้โดยฮาร์ดแวร์เฉพาะเท่านั้น ซึ่งช่วยให้สามารถรองรับคีย์ได้ไม่จำกัดจำนวน นอกจากนี้ ลำดับชั้นของคีย์ได้รับการแก้ไขและย้ายบางส่วนไปยังฮาร์ดแวร์นี้ ซึ่งอนุญาตให้ส่งคืนคีย์ย่อยไปยังซอฟต์แวร์สำหรับงานที่ไม่สามารถใช้กลไกเข้ารหัสลับแบบอินไลน์ได้
ลำดับชั้นที่สำคัญ
คีย์สามารถได้รับมาจากคีย์อื่นโดยใช้ KDF (ฟังก์ชันการรับคีย์) เช่น HKDF ซึ่งส่งผลให้เกิด ลำดับชั้นของคีย์
แผนภาพต่อไปนี้แสดงลำดับชั้นคีย์ทั่วไปสำหรับ FBE เมื่อ ไม่ ได้ใช้คีย์ที่หุ้มด้วยฮาร์ดแวร์:
คีย์คลาส FBE คือคีย์การเข้ารหัสดิบที่ Android ส่งไปยังเคอร์เนล Linux เพื่อปลดล็อกชุดไดเร็กทอรีที่เข้ารหัสโดยเฉพาะ เช่น พื้นที่เก็บข้อมูลที่เข้ารหัสข้อมูลประจำตัวสำหรับผู้ใช้ Android โดยเฉพาะ (ในเคอร์เนล คีย์นี้เรียกว่า คีย์หลัก fscrypt ) จากคีย์นี้ เคอร์เนลได้รับคีย์ย่อยต่อไปนี้:
- ตัวระบุที่สำคัญ สิ่งนี้ไม่ได้ใช้สำหรับการเข้ารหัส แต่เป็นค่าที่ใช้เพื่อระบุคีย์ที่ใช้ป้องกันไฟล์หรือไดเร็กทอรีเฉพาะ
- คีย์การเข้ารหัสเนื้อหาไฟล์
- คีย์การเข้ารหัสชื่อไฟล์
ในทางตรงกันข้าม แผนภาพต่อไปนี้แสดงลำดับชั้นคีย์สำหรับ 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
สำหรับรายละเอียดเพิ่มเติม โปรดดู เอกสารประกอบการเข้ารหัสข้อมูลเมตา