คีย์ที่รวมไว้ในฮาร์ดแวร์

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

โดยปกติแล้วจะไม่ถือว่าเป็นปัญหาเนื่องจากคีย์ไม่ได้อยู่ในระบบ ระหว่างการโจมตีแบบออฟไลน์ ซึ่งเป็นการโจมตีประเภทหลักที่การเข้ารหัส พื้นที่เก็บข้อมูลมีไว้เพื่อป้องกัน อย่างไรก็ตาม เราต้องการเพิ่มการป้องกันการโจมตีประเภทอื่นๆ เช่น การโจมตีแบบ 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 เพื่อปลดล็อกชุด ไดเรกทอรีที่เข้ารหัส อย่างไรก็ตาม ตอนนี้คีย์ดังกล่าวอยู่ในรูปแบบที่ห่อหุ้มชั่วคราว และ หากต้องการใช้คีย์ดังกล่าว จะต้องส่งไปยังฮาร์ดแวร์เฉพาะ ฮาร์ดแวร์นี้ต้อง ใช้ 2 อินเทอร์เฟซที่ใช้คีย์ที่ห่อหุ้มชั่วคราว

  • อินเทอร์เฟซเดียวสำหรับสร้าง 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 ใดก็ได้ที่ตรงตามข้อกำหนดด้านความปลอดภัย อย่างไรก็ตาม เพื่อวัตถุประสงค์ในการทดสอบ vts_kernel_encryption_test จะใช้ KDF เดียวกันในซอฟต์แวร์เพื่อสร้างข้อความที่เข้ารหัสบนดิสก์ซ้ำ และยืนยันว่าถูกต้อง เพื่อความสะดวกในการทดสอบและเพื่อให้แน่ใจว่ามีการใช้ KDF ที่ปลอดภัย และได้รับการตรวจสอบแล้ว เราขอแนะนำให้ฮาร์ดแวร์ใช้ KDF เริ่มต้นที่การทดสอบตรวจสอบ สำหรับฮาร์ดแวร์ที่ใช้ KDF อื่น โปรดดูทดสอบคีย์ที่ห่อหุ้มเพื่อดูวิธีกำหนดค่าการทดสอบ ตามนั้น

การรวมคีย์

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

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

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

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

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

  • อินเทอร์เฟซสำหรับสร้างและนำเข้าคีย์พื้นที่เก็บข้อมูล โดยจะส่งคืนคีย์ในรูปแบบที่ห่อหุ้มระยะยาว อินเทอร์เฟซเหล่านี้เข้าถึงได้โดยอ้อมผ่าน 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

ต้องแก้ไขการติดตั้งใช้งาน 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) เนื่องจากตรวจไม่พบการรองรับ คีย์ที่ห่อหุ้มด้วยฮาร์ดแวร์ เนื่องจากผลการทดสอบยังคงเป็น "ผ่าน" ในกรณีดังกล่าว

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

อย่างไรก็ตาม vts_kernel_encryption_test ยังใช้ KDF เพิ่มเติม kdf2 ผ่าน kdf4 ด้วย ซึ่งมีความปลอดภัยเท่ากับ kdf1 และแตกต่างกันเพียงในตัวเลือกบริบท ป้ายกำกับ และ การจัดรูปแบบสตริงอินพุตที่แก้ไขแล้ว โดยมีไว้เพื่อรองรับฮาร์ดแวร์ที่แตกต่างกันเท่านั้น

สำหรับอุปกรณ์ที่ใช้ KDF อื่น ให้ตั้งค่า ro.crypto.hw_wrapped_keys.kdfพร็อพเพอร์ตี้ของระบบใน PRODUCT_VENDOR_PROPERTIESเป็นชื่อของ KDF ตามที่กำหนดไว้ใน ซอร์สโค้ดทดสอบ ซึ่งจะทำให้ vts_kernel_encryption_test ตรวจสอบ KDF นั้นแทน kdf1 เช่น หากต้องการเลือก kdf2 ให้ใช้

PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2

สำหรับอุปกรณ์ที่ใช้ KDF ที่การทดสอบไม่รองรับ ให้เพิ่มการ ติดตั้งใช้งาน KDF นั้นลงในการทดสอบและตั้งชื่อที่ไม่ซ้ำกัน

เปิดใช้คีย์ที่รวม

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