Android 4.4 และสูงกว่ารองรับ Verified Boot ผ่านฟีเจอร์เคอร์เนล device-mapper-verity (dm-verity) ที่เป็นตัวเลือก ซึ่งให้การตรวจสอบความสมบูรณ์ของอุปกรณ์บล็อกอย่างโปร่งใส dm-verity ช่วยป้องกันรูทคิทแบบถาวรที่สามารถยึดสิทธิ์ของรูทและอุปกรณ์ประนีประนอม คุณลักษณะนี้ช่วยให้ผู้ใช้ Android มั่นใจได้ว่าเมื่อบูตอุปกรณ์จะอยู่ในสถานะเดียวกับเมื่อใช้งานครั้งล่าสุด
แอปพลิเคชันที่อาจเป็นอันตราย (PHA) ที่มีสิทธิ์ของรูทสามารถซ่อนจากโปรแกรมตรวจจับและปิดบังตัวเองได้ ซอฟต์แวร์การรูทสามารถทำได้เพราะมักจะได้รับสิทธิพิเศษมากกว่าตัวตรวจจับ ทำให้ซอฟต์แวร์สามารถ "โกหก" กับโปรแกรมตรวจจับได้
คุณลักษณะ dm-verity ช่วยให้คุณดูอุปกรณ์บล็อก เลเยอร์หน่วยเก็บข้อมูลพื้นฐานของระบบไฟล์ และพิจารณาว่าตรงกับการกำหนดค่าที่คาดไว้หรือไม่ มันทำสิ่งนี้โดยใช้ต้นไม้แฮชเข้ารหัส สำหรับทุกบล็อก (โดยทั่วไปคือ 4k) จะมีแฮช SHA256
เนื่องจากค่าแฮชถูกเก็บไว้ในแผนผังของหน้า เฉพาะแฮช "รูท" ระดับบนสุดเท่านั้นที่น่าเชื่อถือเพื่อตรวจสอบส่วนที่เหลือของทรี ความสามารถในการแก้ไขบล็อกใด ๆ จะเทียบเท่ากับการทำลายแฮชการเข้ารหัส ดูแผนภาพต่อไปนี้สำหรับการพรรณนาถึงโครงสร้างนี้

รูปที่ 1. ตารางแฮช dm-verity
คีย์สาธารณะรวมอยู่ในพาร์ติชันสำหรับเริ่มระบบ ซึ่งต้องได้รับการตรวจสอบจากภายนอกโดยผู้ผลิตอุปกรณ์ คีย์นั้นใช้เพื่อตรวจสอบลายเซ็นสำหรับแฮชนั้นและยืนยันว่าพาร์ติชั่นระบบของอุปกรณ์ได้รับการป้องกันและไม่เปลี่ยนแปลง
การดำเนินการ
การป้องกัน dm-verity อาศัยอยู่ในเคอร์เนล ดังนั้นหากซอฟต์แวร์การรูททำให้ระบบประนีประนอมก่อนที่เคอร์เนลจะขึ้นมา มันจะคงการเข้าถึงนั้นไว้ เพื่อลดความเสี่ยงนี้ ผู้ผลิตส่วนใหญ่ตรวจสอบเคอร์เนลโดยใช้คีย์ที่เขียนลงในอุปกรณ์ คีย์นั้นไม่สามารถเปลี่ยนแปลงได้เมื่ออุปกรณ์ออกจากโรงงาน
ผู้ผลิตใช้คีย์นั้นเพื่อตรวจสอบลายเซ็นบน bootloader ระดับแรก ซึ่งจะตรวจสอบลายเซ็นในระดับต่อมา โปรแกรม bootloader และเคอร์เนลในท้ายที่สุด ผู้ผลิตแต่ละรายที่ต้องการใช้ประโยชน์จากการ บูตที่ตรวจสอบแล้ว ควรมีวิธีการตรวจสอบความสมบูรณ์ของเคอร์เนล สมมติว่าเคอร์เนลได้รับการตรวจสอบแล้ว เคอร์เนลสามารถดูอุปกรณ์บล็อกและยืนยันได้ขณะติดตั้ง
วิธีหนึ่งในการตรวจสอบอุปกรณ์บล็อกคือการแฮชเนื้อหาโดยตรงและเปรียบเทียบกับค่าที่เก็บไว้ อย่างไรก็ตาม การพยายามตรวจสอบอุปกรณ์บล็อกทั้งหมดอาจใช้เวลานานขึ้นและใช้พลังงานของอุปกรณ์มาก อุปกรณ์จะใช้เวลานานในการบู๊ตและจากนั้นจะระบายออกอย่างมากก่อนใช้งาน
แต่ dm-verity จะตรวจสอบบล็อกทีละรายการและเฉพาะเมื่อเข้าถึงแต่ละบล็อกเท่านั้น เมื่ออ่านในหน่วยความจำ บล็อกจะถูกแฮชแบบขนาน จากนั้นแฮชจะถูกตรวจสอบต้นไม้ และเนื่องจากการอ่านบล็อกเป็นการดำเนินการที่มีราคาแพง ดังนั้นเวลาแฝงที่แนะนำโดยการตรวจสอบระดับบล็อกนี้จึงถือว่าค่อนข้างน้อย
หากการตรวจสอบล้มเหลว อุปกรณ์จะสร้างข้อผิดพลาด I/O โดยระบุว่าไม่สามารถอ่านบล็อกได้ จะปรากฏขึ้นราวกับว่าระบบไฟล์เสียหายตามที่คาดไว้
แอปพลิเคชันอาจเลือกที่จะดำเนินการต่อโดยไม่มีข้อมูลผลลัพธ์ เช่น เมื่อไม่จำเป็นต้องใช้ผลลัพธ์เหล่านั้นในฟังก์ชันหลักของแอปพลิเคชัน อย่างไรก็ตาม หากแอปพลิเคชันไม่สามารถดำเนินการต่อได้โดยไม่มีข้อมูล แอปพลิเคชันจะล้มเหลว
ส่งต่อการแก้ไขข้อผิดพลาด
Android 7.0 ขึ้นไปปรับปรุงความทนทาน dm-verity ด้วยการแก้ไขข้อผิดพลาดไปข้างหน้า (FEC) การใช้งาน AOSP เริ่มต้นด้วยโค้ดแก้ไขข้อผิดพลาดทั่วไปของ Reed-Solomon และใช้เทคนิคที่เรียกว่า interleaving เพื่อลดโอเวอร์เฮดของพื้นที่และเพิ่มจำนวนบล็อกที่เสียหายซึ่งสามารถกู้คืนได้ สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ FEC โปรดดูที่ Strictly Enforced Verified Boot with Error Correctionการดำเนินการ
สรุป
- สร้างอิมเมจระบบ ext4
- สร้าง hash tree สำหรับรูปภาพนั้น
- สร้างตาราง dm-verity สำหรับแฮชทรีนั้น
- ลงชื่อในตาราง dm-verity นั้นเพื่อสร้างลายเซ็นตาราง
- รวมตารางลายเซ็น และตาราง dm-verity ลงในข้อมูลเมตาของ verity
- เชื่อมต่ออิมเมจระบบ ข้อมูลเมตา verity และแผนผังแฮช
ดูโครงการ Chromium - Verified Boot สำหรับคำอธิบายโดยละเอียดของแผนผังแฮชและตาราง dm-verity
กำลังสร้างต้นไม้แฮช
ตามที่อธิบายไว้ในบทนำ แผนผังแฮชเป็นส่วนสำคัญของ dm-verity เครื่องมือ cryptsetup จะสร้าง hash tree ให้กับคุณ อีกวิธีหนึ่งคือการกำหนดค่าที่เข้ากันได้ที่นี่:
<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>
ในการสร้างแฮช อิมเมจระบบจะถูกแบ่งที่เลเยอร์ 0 เป็นบล็อก 4k โดยแต่ละบล็อกจะกำหนดแฮช SHA256 เลเยอร์ 1 เกิดขึ้นจากการรวมแฮช SHA256 เหล่านั้นลงในบล็อก 4k เท่านั้น ส่งผลให้รูปภาพมีขนาดเล็กลงมาก เลเยอร์ 2 ถูกสร้างขึ้นเหมือนกันด้วยแฮช SHA256 ของเลเยอร์ 1
การดำเนินการนี้จะทำจนกว่าแฮช SHA256 ของเลเยอร์ก่อนหน้าจะใส่ลงในบล็อกเดียวได้ เมื่อได้รับ SHA256 ของบล็อกนั้น คุณจะมีรูทแฮชของต้นไม้
ขนาดของแผนผังแฮช (และการใช้พื้นที่ดิสก์ที่เกี่ยวข้อง) จะแตกต่างกันไปตามขนาดของพาร์ติชันที่ตรวจสอบแล้ว ในทางปฏิบัติ ขนาดของ hash tree มักจะมีขนาดเล็ก ซึ่งมักจะน้อยกว่า 30 MB
หากคุณมีบล็อกในเลเยอร์ที่ไม่มีการเติมเต็มตามธรรมชาติโดยแฮชของเลเยอร์ก่อนหน้า คุณควรเพิ่มศูนย์ด้วยศูนย์เพื่อให้ได้ 4k ที่คาดหวัง วิธีนี้ช่วยให้คุณทราบได้ว่าแฮชทรีไม่ได้ถูกลบออก และจะเติมข้อมูลว่างให้สมบูรณ์แทน
ในการสร้าง hash tree ให้เชื่อมแฮชของเลเยอร์ 2 เข้ากับเลเยอร์ที่ 1, เลเยอร์ 3 แฮชเข้ากับเลเยอร์ 2 และอื่นๆ เขียนทั้งหมดนี้ลงในดิสก์ โปรดทราบว่าสิ่งนี้ไม่ได้อ้างอิงเลเยอร์ 0 ของแฮชรูท
ในการสรุป อัลกอริทึมทั่วไปในการสร้าง hash tree มีดังนี้:
- เลือกเกลือแบบสุ่ม (การเข้ารหัสฐานสิบหก)
- แยกอิมเมจระบบของคุณออกเป็นบล็อก 4k
- สำหรับแต่ละบล็อก รับแฮช SHA256 (เค็ม)
- เชื่อมแฮชเหล่านี้เพื่อสร้างระดับ
- แพดระดับด้วย 0s ถึงขอบบล็อก 4k
- เชื่อมต่อระดับกับต้นไม้แฮชของคุณ
- ทำซ้ำขั้นตอนที่ 2-6 โดยใช้ระดับก่อนหน้าเป็นแหล่งที่มาของระดับถัดไป จนกว่าคุณจะมีแฮชเพียงอันเดียว
ผลลัพธ์ที่ได้คือแฮชเดียว ซึ่งเป็นแฮชรูทของคุณ สิ่งนี้และเกลือของคุณถูกใช้ระหว่างการสร้างตารางการแมป dm-verity ของคุณ
การสร้างตารางการแมป dm-verity
สร้างตารางการแมป dm-verity ซึ่งระบุอุปกรณ์บล็อก (หรือเป้าหมาย) สำหรับเคอร์เนลและตำแหน่งของแผนผังแฮช (ซึ่งเป็นค่าเดียวกัน) การแมปนี้ใช้สำหรับการสร้างและบูต fstab
ตารางยังระบุขนาดของบล็อกและ hash_start ซึ่งเป็นตำแหน่งเริ่มต้นของแผนผังแฮช (โดยเฉพาะหมายเลขบล็อกจากจุดเริ่มต้นของรูปภาพ)
ดู cryptsetup สำหรับคำอธิบายโดยละเอียดของฟิลด์ตารางการแมปเป้าหมายตามความเป็นจริง
ลงนามในตาราง dm-verity
ลงนามในตาราง dm-verity เพื่อสร้างลายเซ็นตาราง เมื่อตรวจสอบพาร์ติชัน ลายเซ็นตารางจะได้รับการตรวจสอบก่อน สิ่งนี้ทำกับคีย์บนอิมเมจสำหรับบูตของคุณในตำแหน่งคงที่ โดยทั่วไปแล้ว คีย์จะรวมอยู่ในระบบการสร้างของผู้ผลิตเพื่อรวมไว้ในอุปกรณ์ในตำแหน่งที่แน่นอนโดยอัตโนมัติ
ในการตรวจสอบพาร์ติชันด้วยลายเซ็นและคีย์ผสมนี้:
- เพิ่มคีย์ RSA-2048 ในรูปแบบที่เข้ากันได้กับ libmincrypt ให้กับพาร์ติชัน
/boot
ที่/verity_key
ระบุตำแหน่งของคีย์ที่ใช้ตรวจสอบแผนผังแฮช - ใน fstab สำหรับรายการที่เกี่ยวข้อง ให้เพิ่มการ
verify
ไปที่แฟล็fs_mgr
การรวมลายเซ็นตารางเข้ากับข้อมูลเมตา
รวมตารางลายเซ็นและตาราง dm-verity ลงในข้อมูลเมตาของ verity บล็อกข้อมูลเมตาทั้งหมดมีการกำหนดเวอร์ชันเพื่อให้สามารถขยายได้ เช่น การเพิ่มลายเซ็นประเภทที่สองหรือเปลี่ยนลำดับบางอย่าง
เพื่อตรวจสอบสุขภาพจิต หมายเลขวิเศษจะเชื่อมโยงกับชุดข้อมูลเมตาของตารางแต่ละชุดที่ช่วยระบุตาราง เนื่องจากความยาวรวมอยู่ในส่วนหัวของอิมเมจระบบ ext4 จึงให้วิธีการค้นหาข้อมูลเมตาโดยไม่ทราบเนื้อหาของข้อมูลเอง
เพื่อให้แน่ใจว่าคุณไม่ได้เลือกตรวจสอบพาร์ติชันที่ไม่ผ่านการตรวจสอบ หากไม่มีหมายเลขวิเศษนี้ จะหยุดกระบวนการตรวจสอบ ตัวเลขนี้คล้ายกับ:
0xb001b001
ค่าไบต์ในฐานสิบหกคือ:
- ไบต์แรก = b0
- ไบต์ที่สอง = 01
- ไบต์ที่สาม = b0
- ไบต์ที่สี่ = 01
ไดอะแกรมต่อไปนี้แสดงรายละเอียดของข้อมูลเมตาที่เป็นจริง:
<magic number>|<version>|<signature>|<table length>|<table>|<padding> \-------------------------------------------------------------------/ \----------------------------------------------------------/ | | | | 32K block content
และตารางนี้อธิบายฟิลด์ข้อมูลเมตาเหล่านั้น
ตารางที่ 1. ฟิลด์ข้อมูลเมตาของ Verity
สนาม | วัตถุประสงค์ | ขนาด | ค่า |
---|---|---|---|
เลขวิเศษ | ใช้โดย fs_mgr เพื่อตรวจสุขภาพจิต | 4 ไบต์ | 0xb001b001 |
รุ่น | ใช้เพื่อกำหนดเวอร์ชันบล็อกข้อมูลเมตา | 4 ไบต์ | ปัจจุบัน0 |
ลายเซ็น | ลายเซ็นของตารางในรูปแบบเบาะ PKCS1.5 | 256 ไบต์ | |
ความยาวของโต๊ะ | ความยาวของตาราง dm-verity เป็นไบต์ | 4 ไบต์ | |
โต๊ะ | ตาราง dm-verity ที่อธิบายไว้ก่อนหน้านี้ | ไบต์ความยาวตาราง | |
การขยายความ | โครงสร้างนี้มีความยาวเบาะ 0 ถึง 32k | 0 |
เพิ่มประสิทธิภาพ dm-verity
เพื่อให้ได้ประสิทธิภาพที่ดีที่สุดจาก dm-verity คุณควร:
- ในเคอร์เนล เปิด NEON SHA-2 สำหรับ ARMv7 และส่วนขยาย SHA-2 สำหรับ ARMv8
- ทดลองกับการตั้งค่า read-ahead และ prefetch_cluster แบบต่างๆ เพื่อค้นหาการกำหนดค่าที่ดีที่สุดสำหรับอุปกรณ์ของคุณ