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

รูปที่ 1 ตารางแฮช dm-verity
คีย์สาธารณะจะรวมอยู่ในพาร์ติชันสำหรับบูต ซึ่งผู้ผลิตอุปกรณ์ต้องยืนยันจากภายนอก ระบบจะใช้คีย์ดังกล่าวเพื่อยืนยันลายเซ็นสำหรับแฮชนั้น และยืนยันว่าพาร์ติชันระบบของอุปกรณ์ได้รับการปกป้องและไม่มีการเปลี่ยนแปลง
การดำเนินการ
การปกป้อง dm-verity อยู่ในเคอร์เนล ดังนั้นหากซอฟต์แวร์การรูททำให้ระบบเสียหายก่อนที่เคอร์เนลจะทำงาน ซอฟต์แวร์ดังกล่าวจะยังคงมีสิทธิ์เข้าถึงนั้น ผู้ผลิตส่วนใหญ่จะยืนยันเคอร์เนลโดยใช้คีย์ที่ฝังไว้ในอุปกรณ์เพื่อลดความเสี่ยงนี้ คีย์ดังกล่าวจะเปลี่ยนแปลงไม่ได้เมื่ออุปกรณ์ออกจากโรงงาน
ผู้ผลิตใช้คีย์ดังกล่าวเพื่อยืนยันลายเซ็นในบูตโหลดเดอร์ระดับแรก ซึ่งจะยืนยันลายเซ็นในลำดับถัดไป ได้แก่ บูตโหลดเดอร์ของแอป และสุดท้ายคือเคอร์เนล ผู้ผลิตแต่ละรายที่ต้องการใช้ประโยชน์จากการบูตที่ได้รับการยืนยันควรมีวิธีการยืนยันความสมบูรณ์ของเคอร์เนล สมมติว่าเคอร์เนลได้รับการยืนยันแล้ว เคอร์เนลจะดูอุปกรณ์บล็อกและยืนยันได้เมื่อมีการต่อเชื่อม
วิธีหนึ่งในการยืนยันอุปกรณ์บล็อกคือการแฮชเนื้อหาโดยตรงและเปรียบเทียบกับค่าที่เก็บไว้ อย่างไรก็ตาม การพยายามยืนยันอุปกรณ์ทั้งบล็อกอาจใช้เวลานานและทำให้แบตเตอรี่ของอุปกรณ์หมดเร็ว อุปกรณ์จะใช้เวลานานในการบูตและแบตเตอรี่จะหมดลงอย่างรวดเร็วก่อนใช้งาน
แต่ dm-verity จะยืนยันแต่ละบล็อกแยกกันเฉพาะเมื่อมีการเข้าชมแต่ละบล็อกเท่านั้น เมื่ออ่านลงในหน่วยความจํา ระบบจะแฮชบล็อกแบบขนาน จากนั้นระบบจะยืนยันแฮชในลําดับชั้น และเนื่องจากการอ่านบล็อกเป็นการดำเนินการที่มีค่าใช้จ่ายสูงมาก เวลาในการตอบสนองที่เกิดจากการยืนยันระดับบล็อกนี้จึงถือว่าน้อยมากเมื่อเทียบกับการดำเนินการอื่นๆ
หากยืนยันไม่สำเร็จ อุปกรณ์จะสร้างข้อผิดพลาด I/O ที่ระบุว่าอ่านบล็อกไม่ได้ ดูเหมือนว่าระบบไฟล์จะเสียหายตามที่คาดไว้
แอปอาจเลือกที่จะดำเนินการต่อโดยไม่มีข้อมูลผลลัพธ์ เช่น เมื่อผลลัพธ์เหล่านั้นไม่จำเป็นต่อฟังก์ชันหลักของแอป อย่างไรก็ตาม หากแอปดำเนินการต่อไม่ได้หากไม่มีข้อมูล การดำเนินการก็จะไม่สำเร็จ
การแก้ไขข้อผิดพลาดแบบส่งต่อ
Android 7.0 ขึ้นไปจะปรับปรุงความเสถียรของ dm-verity ด้วยข้อผิดพลาดในการแก้ไขแบบส่งต่อ (FEC) การติดตั้งใช้งาน AOSP เริ่มต้นด้วยรหัสแก้ไขข้อผิดพลาด Reed-Solomon ทั่วไป และใช้เทคนิคที่เรียกว่าการแทรกคั่นเพื่อลดพื้นที่ทำงานส่วนเกินและเพิ่มจำนวนบล็อกที่เสียหายซึ่งกู้คืนได้ ดูรายละเอียดเพิ่มเติมเกี่ยวกับ FEC ได้ที่การบังคับใช้การบูตที่ผ่านการยืนยันพร้อมการแก้ไขข้อผิดพลาดอย่างเข้มงวดการใช้งาน
สรุป
- สร้างอิมเมจระบบ ext4
- สร้าง Hash Tree สำหรับรูปภาพนั้น
- สร้างตาราง dm-verity สำหรับต้นไม้แฮชนั้น
- ลงนามในตาราง dm-verity เพื่อสร้างลายเซ็นตาราง
- รวมลายเซ็นตารางและตาราง dm-verity ไว้ในข้อมูลเมตา Verity
- ต่อเชื่อมภาพระบบ ข้อมูลเมตาของ Verity และต้นไม้แฮช
ดูคำอธิบายโดยละเอียดของ Hash Tree และตาราง dm-verity ได้ที่หัวข้อโครงการ Chromium - การบูตที่ตรวจสอบแล้ว
สร้าง Hash Tree
ดังที่อธิบายไว้ในบทนำ ทรีแฮชเป็นส่วนสำคัญของ 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
หากคุณมีบล็อกในเลเยอร์ที่ไม่ได้เติมแฮชของเลเยอร์ก่อนหน้าให้เต็มตามปกติ คุณควรเติมด้วย 0 เพื่อให้ได้ความละเอียด 4K ตามที่ต้องการ ซึ่งจะช่วยให้คุณทราบว่าระบบไม่ได้นําต้นไม้แฮชออก แต่มีการเติมข้อมูลว่างแทน
หากต้องการสร้าง Hash Tree ให้ต่อเชื่อมแฮชของเลเยอร์ 2 เข้ากับแฮชของเลเยอร์ 1, แฮชของเลเยอร์ 3 เข้ากับแฮชของเลเยอร์ 2 และอื่นๆ เขียนข้อมูลทั้งหมดนี้ลงดิสก์ โปรดทราบว่าการดำเนินการนี้ไม่ได้อ้างอิงเลเยอร์ 0 ของแฮชรูท
โดยสรุปแล้ว อัลกอริทึมทั่วไปในการสร้างแฮชทรีมีดังนี้
- เลือก Salt แบบสุ่ม (การเข้ารหัสฐาน 16)
- เลิกจัดเก็บภาพระบบเป็นบล็อก 4K
- รับแฮช SHA256 (ที่มีเกลือ) ของแต่ละบล็อก
- รวมแฮชเหล่านี้เข้าด้วยกันเพื่อสร้างระดับ
- เพิ่มค่า 0 ลงท้ายระดับให้ถึงขอบเขตบล็อก 4K
- ต่อระดับกับต้นไม้แฮช
- ทำซ้ำขั้นตอนที่ 2-6 โดยใช้ระดับก่อนหน้าเป็นแหล่งที่มาของระดับถัดไปจนกว่าคุณจะมีแฮชเพียงรายการเดียว
ผลลัพธ์ที่ได้คือแฮชเดียว ซึ่งเป็นแฮชรูท ระบบจะใช้รหัสผ่านนี้และ Salt ของคุณในระหว่างการสร้างตารางการแมป dm-verity
สร้างตารางการแมป dm-verity
สร้างตารางการแมป dm-verity ซึ่งระบุอุปกรณ์บล็อก (หรือเป้าหมาย) สำหรับเคอร์เนลและตำแหน่งของต้นไม้แฮช (ซึ่งเป็นค่าเดียวกัน) การแมปนี้ใช้สำหรับการสร้างและบูต fstab
ตารางจะระบุขนาดของบล็อกและ hash_start ซึ่งเป็นตำแหน่งเริ่มต้นของต้นไม้แฮช (โดยเฉพาะหมายเลขบล็อกจากจุดเริ่มต้นของรูปภาพ) ด้วย
ดูคำอธิบายโดยละเอียดของช่องตารางการแมปเป้าหมายของ Verity ได้ที่ cryptsetup
ลงนามในตาราง dm-verity
ลงนามในตาราง dm-verity เพื่อสร้างลายเซ็นตาราง เมื่อยืนยันพาร์ติชัน ระบบจะตรวจสอบลายเซ็นตารางก่อน ซึ่งจะดำเนินการกับคีย์ในอิมเมจบูตของคุณในตำแหน่งที่แน่นอน โดยปกติแล้ว คีย์จะรวมอยู่ในระบบการสร้างของผู้ผลิตสำหรับการรวมโดยอัตโนมัติในอุปกรณ์ในตำแหน่งที่ตายตัว
วิธียืนยันพาร์ติชันด้วยลายเซ็นและชุดค่าผสมคีย์นี้
- เพิ่มคีย์ RSA-2048 ในรูปแบบที่เข้ากันได้กับ libmincrypt ลงในพาร์ติชัน
/boot
ที่/verity_key
ระบุตำแหน่งของคีย์ที่ใช้ยืนยันต้นไม้แฮช - ใน fstab สำหรับรายการที่เกี่ยวข้อง ให้เพิ่ม
verify
ลงใน Flagfs_mgr
รวมลายเซ็นตารางไว้ในข้อมูลเมตา
รวมลายเซ็นตารางและตาราง dm-verity ไว้ในข้อมูลเมตา Verity บล็อกข้อมูลเมตาทั้งหมดจะมีเวอร์ชันเพื่อให้ขยายได้ เช่น เพิ่มลายเซ็นประเภทที่ 2 หรือเปลี่ยนลําดับบางอย่าง
ระบบจะตรวจสอบความถูกต้องโดยเชื่อมโยงตัวเลขที่ไม่น่าจะมีอยู่จริงกับชุดข้อมูลเมตาของตารางแต่ละชุดเพื่อช่วยระบุตาราง เนื่องจากความยาวรวมอยู่ในส่วนหัวของรูปภาพระบบ ext4 วิธีนี้จึงเป็นวิธีค้นหาข้อมูลเมตาโดยไม่ต้องทราบเนื้อหาของข้อมูล
วิธีนี้ช่วยให้มั่นใจว่าคุณไม่ได้เลือกยืนยันพาร์ติชันที่ยังไม่ได้รับการยืนยัน หากเป็นเช่นนั้น การไม่มีหมายเลขวิเศษนี้จะทำให้กระบวนการยืนยันหยุดลง หมายเลขนี้คล้ายกับ 0xb001b001
ค่าไบต์ในรูปแบบเลขฐาน 16 มีดังนี้
- ไบต์แรก = b0
- ไบต์ที่ 2 = 01
- ไบต์ที่ 3 = b0
- ไบต์ที่ 4 = 01
แผนภาพต่อไปนี้แสดงรายละเอียดของข้อมูลเมตาการยืนยัน
<magic number>|<version>|<signature>|<table length>|<table>|<padding> \-------------------------------------------------------------------/ \----------------------------------------------------------/ | | | | 32K block content
และตารางนี้จะอธิบายฟิลด์ข้อมูลเมตาเหล่านั้น
ตารางที่ 1 ตรวจสอบช่องข้อมูลเมตา
ช่อง | วัตถุประสงค์ | ขนาด | ค่า |
---|---|---|---|
หมายเลขมหัศจรรย์ | ใช้โดย fs_mgr เป็นการตรวจสอบความถูกต้อง | 4 ไบต์ | 0xb001b001 |
เวอร์ชัน | ใช้เพื่อกำหนดเวอร์ชันของบล็อกข้อมูลเมตา | 4 ไบต์ | ปัจจุบัน 0 |
ลายเซ็น | ลายเซ็นของตารางในรูปแบบ PKCS1.5 แบบเพิ่มค่าให้เต็ม | 256 ไบต์ | |
ความยาวของตาราง | ความยาวของตาราง dm-verity เป็นไบต์ | 4 ไบต์ | |
โต๊ะ | ตาราง dm-verity ที่อธิบายไว้ก่อนหน้านี้ | ไบต์ความยาวตาราง | |
padding | โครงสร้างนี้มีการเพิ่ม 0 ต่อท้ายให้มีความยาว 32,000 | 0 |
เพิ่มประสิทธิภาพ dm-verity
คุณควรทำดังนี้เพื่อให้ dm-verity ทำงานได้อย่างมีประสิทธิภาพสูงสุด
- ในเคอร์เนล ให้เปิด NEON SHA-2 สำหรับ ARMv7 และส่วนขยาย SHA-2 สำหรับ ARMv8
- ลองใช้การตั้งค่าการอ่านล่วงหน้าและ prefetch_cluster แบบต่างๆ เพื่อหาการกำหนดค่าที่ดีที่สุดสำหรับอุปกรณ์