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

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

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

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

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

ออกแบบ

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

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

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

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

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

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

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

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

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

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

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

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

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

  • อินเทอร์เฟซเดียวในการรับ inline_encryption_key และตั้งโปรแกรมโดยตรงลงในคีย์ล็อตของเอ็นจิ้นการเข้ารหัสแบบอินไลน์ ซึ่งช่วยให้สามารถเข้ารหัส/ถอดรหัสเนื้อหาไฟล์ได้โดยไม่ต้องมีซอฟต์แวร์เข้าถึงคีย์ดิบ ในเคอร์เนลทั่วไปของ Android อินเทอร์เฟซนี้สอดคล้องกับการดำเนินการ blk_ksm_ll_ops::keyslot_program ซึ่งต้องใช้งานโดยไดรเวอร์การจัดเก็บข้อมูล
  • อินเทอร์เฟซเดียวในการรับและส่งคืน sw_secret ("ความลับของซอฟต์แวร์" หรือเรียกอีกอย่างว่า "ความลับดิบ" ในบางสถานที่) ซึ่งเป็นคีย์ที่ Linux ใช้ในการรับคีย์ย่อยสำหรับทุกอย่างนอกเหนือจากการเข้ารหัสเนื้อหาไฟล์ ในเคอร์เนลทั่วไปของ Android อินเทอร์เฟซนี้สอดคล้องกับการดำเนินการ blk_ksm_ll_ops::derive_raw_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 มีเฟรมเวิร์กพื้นฐานสำหรับรองรับคีย์ที่หุ้มด้วยฮาร์ดแวร์อยู่แล้ว ซึ่งรวมถึงการสนับสนุนในส่วนประกอบ userspace เช่น vold เช่นเดียวกับการสนับสนุนเคอร์เนล Linux ใน blk-crypto , fscrypt และ dm-default-key

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

การเปลี่ยนแปลงของ KeyMint

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

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

การเปลี่ยนแปลงเคอร์เนลลินุกซ์

ไดรเวอร์เคอร์เนล Linux สำหรับเอ็นจิ้นการเข้ารหัสแบบอินไลน์ของอุปกรณ์จะต้องถูกแก้ไขเพื่อตั้งค่า BLK_CRYPTO_FEATURE_WRAPPED_KEYS ทำให้การทำงาน keyslot_program() และ keyslot_evict() รองรับการเขียนโปรแกรม/ขับไล่คีย์ที่หุ้มด้วยฮาร์ดแวร์ และใช้การดำเนินการ 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 สำหรับรายละเอียดเพิ่มเติม โปรดดู เอกสารการเข้ารหัสข้อมูลเมตา