Android 4.4 และสูงกว่ารองรับ Verified Boot ผ่านคุณลักษณะเคอร์เนลอุปกรณ์ 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 และใช้เทคนิคที่เรียกว่าการแทรกสลับเพื่อลดพื้นที่ว่างและเพิ่มจำนวนบล็อกที่เสียหายซึ่งสามารถกู้คืนได้ สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ FEC โปรดดู Strictly Verified Boot with Error Correctionการดำเนินการ
สรุป
- สร้างอิมเมจระบบ ext4
- สร้างแผนผังแฮช สำหรับรูปภาพนั้น
- สร้างตาราง dm-verity สำหรับแผนผังแฮชนั้น
- เซ็นชื่อตาราง dm-verity เพื่อสร้างลายเซ็นตาราง
- รวมลายเซ็นของตาราง และตาราง dm-verity ไว้ในข้อมูลเมตาของ verity
- เชื่อมต่ออิมเมจระบบ ข้อมูลเมตาของความจริง และแผนผังแฮช
ดูThe Chromium Projects - Verified Boot สำหรับคำอธิบายโดยละเอียดของแผนผังแฮชและตาราง dm-verity
การสร้างต้นไม้แฮช
ดังที่อธิบายไว้ในบทนำ ต้นไม้แฮชเป็นองค์ประกอบสำคัญของ dm-verity เครื่องมือ cryptsetup จะสร้างแผนผังแฮชให้คุณ อีกวิธีหนึ่งคือกำหนดที่เข้ากันได้ที่นี่:
<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 ที่คาดไว้ ซึ่งช่วยให้คุณทราบได้ว่าต้นไม้แฮชไม่ได้ถูกลบออก และเติมข้อมูลเปล่าแทน
ในการสร้างแผนผังแฮช ให้เชื่อมแฮชเลเยอร์ 2 เข้ากับแฮชสำหรับเลเยอร์ 1 เชื่อมแฮชเลเยอร์ 3 เข้ากับแฮชของเลเยอร์ 2 และอื่น ๆ เขียนทั้งหมดนี้ลงดิสก์ โปรดทราบว่าสิ่งนี้ไม่ได้อ้างอิงเลเยอร์ 0 ของรูทแฮช
สรุป อัลกอริทึมทั่วไปในการสร้างแผนผังแฮชมีดังนี้:
- เลือกเกลือแบบสุ่ม (การเข้ารหัสเลขฐานสิบหก)
- แยกอิมเมจระบบของคุณออกเป็นบล็อก 4k
- สำหรับแต่ละบล็อก รับแฮช SHA256 (เค็ม)
- เชื่อมต่อแฮชเหล่านี้เพื่อสร้างระดับ
- เลื่อนระดับด้วย 0 ถึงขอบเขตบล็อก 4k
- เชื่อมต่อระดับกับแผนผังแฮชของคุณ
- ทำซ้ำขั้นตอนที่ 2-6 โดยใช้ระดับก่อนหน้าเป็นแหล่งที่มาสำหรับถัดไปจนกว่าคุณจะมีแฮชเดียว
ผลลัพธ์คือแฮชเดียวซึ่งเป็นรูทแฮชของคุณ สิ่งนี้และเกลือของคุณถูกใช้ระหว่างการสร้างตารางการแมป dm-verity
สร้างตารางการแมป dm-verity
สร้างตารางการแมป dm-verity ซึ่งระบุอุปกรณ์บล็อก (หรือเป้าหมาย) สำหรับเคอร์เนลและตำแหน่งของแผนผังแฮช (ซึ่งเป็นค่าเดียวกัน) การแมปนี้ใช้สำหรับการสร้างและการบูท fstab
ตารางยังระบุขนาดของบล็อกและ hash_start ซึ่งเป็นตำแหน่งเริ่มต้นของแผนผังแฮช (โดยเฉพาะอย่างยิ่ง หมายเลขบล็อกจากจุดเริ่มต้นของรูปภาพ)
ดู cryptsetup สำหรับคำอธิบายโดยละเอียดของช่องตารางการแมปเป้าหมาย verity
การลงนามในตาราง 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
- ทดลองกับการตั้งค่าแบบอ่านล่วงหน้าและ prefetch_cluster ต่างๆ เพื่อค้นหาการกำหนดค่าที่ดีที่สุดสำหรับอุปกรณ์ของคุณ