پیاده سازی dm-verity

Android نسخه 4.4 و بالاتر از Verified Boot از طریق ویژگی اختیاری device-mapper-verity (dm-verity) کرنل پشتیبانی می‌کند که بررسی شفافیت یکپارچگی دستگاه‌های بلوک را فراهم می‌کند. dm-verity به جلوگیری از روت‌کیت‌های دائمی که می‌توانند امتیازات ریشه را نگه دارند و دستگاه‌ها را به خطر بیندازند، کمک می‌کند. این ویژگی به کاربران اندرویدی کمک می کند هنگام بوت کردن دستگاه مطمئن شوند که در همان حالتی است که آخرین بار استفاده شده است.

برنامه‌های بالقوه مضر (PHA) با امتیازات ریشه می‌توانند از برنامه‌های شناسایی پنهان شوند و در غیر این صورت خود را پنهان کنند. نرم افزار روت کردن می تواند این کار را انجام دهد زیرا اغلب از آشکارسازها امتیاز بیشتری دارد و به نرم افزار امکان می دهد به برنامه های تشخیص دروغ بگوید.

ویژگی dm-verity به شما امکان می‌دهد به یک دستگاه بلوک، لایه ذخیره‌سازی زیرین سیستم فایل نگاه کنید و تعیین کنید که آیا با پیکربندی مورد انتظار آن مطابقت دارد یا خیر. این کار را با استفاده از درخت هش رمزنگاری انجام می دهد. برای هر بلوک (معمولاً 4k)، یک هش SHA256 وجود دارد.

از آنجایی که مقادیر هش در درختی از صفحات ذخیره می شوند، برای تأیید بقیه درخت فقط باید به هش «ریشه» سطح بالا اعتماد کرد. توانایی تغییر هر یک از بلوک ها معادل شکستن هش رمزنگاری است. برای تصویری از این ساختار به نمودار زیر مراجعه کنید.

dm-verity-hash-table

شکل 1. جدول هش dm-verity

یک کلید عمومی روی پارتیشن بوت وجود دارد که باید توسط سازنده دستگاه به صورت خارجی تأیید شود. این کلید برای تأیید امضای آن هش و تأیید محافظت پارتیشن سیستم دستگاه و بدون تغییر استفاده می شود.

عملیات

حفاظت dm-verity در هسته وجود دارد. بنابراین اگر نرم افزار روت کردن سیستم را قبل از بالا آمدن کرنل به خطر بیندازد، این دسترسی را حفظ می کند. برای کاهش این خطر، اکثر سازندگان هسته را با استفاده از کلید سوزانده شده در دستگاه تأیید می کنند. پس از خروج دستگاه از کارخانه، آن کلید قابل تغییر نیست.

سازندگان از این کلید برای تأیید امضا در بوت لودر سطح اول استفاده می کنند، که به نوبه خود امضای سطوح بعدی، بوت لودر برنامه و در نهایت هسته را تأیید می کند. هر سازنده ای که مایل به استفاده از بوت تایید شده است باید روشی برای تایید یکپارچگی هسته داشته باشد. با فرض اینکه هسته تأیید شده است، هسته می تواند به یک دستگاه بلوک نگاه کند و آن را در حین نصب تأیید کند.

یکی از راه های تأیید یک دستگاه بلوک، هش مستقیم محتویات آن و مقایسه آنها با یک مقدار ذخیره شده است. با این حال، تلاش برای تأیید کل یک دستگاه بلوک می تواند مدت زمان زیادی طول بکشد و مقدار زیادی از توان دستگاه را مصرف کند. دستگاه‌ها زمان زیادی طول می‌کشد تا بوت شوند و سپس قبل از استفاده به میزان قابل توجهی تخلیه می‌شوند.

در عوض، dm-verity بلوک‌ها را به‌صورت جداگانه و تنها زمانی تأیید می‌کند که به هر یک دسترسی داشته باشید. هنگامی که در حافظه خوانده می شود، بلوک به صورت موازی هش می شود. سپس هش در درخت تأیید می شود. و از آنجایی که خواندن بلوک بسیار گران است، تأخیر معرفی شده توسط این تأیید در سطح بلوک نسبتاً اسمی است.

اگر تأیید ناموفق باشد، دستگاه یک خطای I/O ایجاد می کند که نشان می دهد بلوک قابل خواندن نیست. همانطور که انتظار می رود به نظر می رسد که سیستم فایل خراب شده است.

برنامه‌ها ممکن است انتخاب کنند که بدون داده‌های به‌دست‌آمده ادامه دهند، مانند زمانی که این نتایج برای عملکرد اصلی برنامه مورد نیاز نیست. با این حال، اگر برنامه نتواند بدون داده ادامه یابد، با شکست مواجه خواهد شد.

تصحیح خطای فوروارد

Android 7.0 و بالاتر استحکام dm-verity را با تصحیح خطای رو به جلو (FEC) بهبود می بخشد. پیاده سازی AOSP با کد رایج تصحیح خطا Reed-Solomon شروع می شود و از تکنیکی به نام interleaving برای کاهش سربار فضا و افزایش تعداد بلوک های خراب قابل بازیابی استفاده می کند. برای جزئیات بیشتر در مورد FEC، راه‌اندازی تأییدشده به‌طور دقیق با تصحیح خطا را ببینید.

پیاده سازی

خلاصه

  1. یک تصویر سیستم ext4 ایجاد کنید.
  2. یک درخت هش برای آن تصویر ایجاد کنید .
  3. یک جدول dm-verity برای آن درخت هش بسازید .
  4. برای تولید امضای جدول ، آن جدول dm-verity را امضا کنید .
  5. امضای جدول و جدول dm-verity را در ابرداده های صحت قرار دهید .
  6. تصویر سیستم، ابرداده verity و درخت هش را به هم متصل کنید.

برای توضیحات مفصل درخت هش و جدول 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 آن بلوک را دریافت می کنید، هش ریشه درخت را دارید.

اندازه درخت هش (و استفاده از فضای دیسک مربوطه) با اندازه پارتیشن تایید شده متفاوت است. در عمل، اندازه درختان هش کوچک است، اغلب کمتر از 30 مگابایت.

اگر بلوکی در لایه‌ای دارید که به طور طبیعی توسط هش‌های لایه قبلی پر نشده است، باید آن را با صفر اضافه کنید تا به 4k مورد انتظار برسید. این به شما امکان می دهد بدانید درخت هش حذف نشده است و در عوض با داده های خالی تکمیل می شود.

برای تولید درخت هش، هش های لایه 2 را به هش های لایه 1، لایه 3 هش ها را به هش های لایه 2 و غیره متصل کنید. همه اینها را روی دیسک بنویسید. توجه داشته باشید که این به لایه 0 هش ریشه اشاره نمی کند.

به طور خلاصه، الگوریتم کلی برای ساخت درخت هش به شرح زیر است:

  1. نمک تصادفی (رمزگذاری هگزا دسیمال) را انتخاب کنید.
  2. تصویر سیستم خود را در بلوک های 4k جدا کنید.
  3. برای هر بلوک، هش SHA256 (نمک) آن را دریافت کنید.
  4. این هش ها را به هم متصل کنید تا یک سطح تشکیل شود
  5. سطح را با 0s به مرز بلوک 4k اضافه کنید.
  6. سطح را به درخت هش خود متصل کنید.
  7. مراحل 2-6 را با استفاده از سطح قبلی به عنوان منبع برای مرحله بعدی تکرار کنید تا زمانی که فقط یک هش داشته باشید.

نتیجه این یک هش منفرد است که هش ریشه شما است. این و نمک شما در طول ساخت جدول نقشه برداری dm-verity شما استفاده می شود.

ساخت جدول نگاشت dm-verity

جدول نگاشت dm-verity را بسازید که دستگاه بلوک (یا هدف) را برای هسته و محل درخت هش (که همان مقدار است.) مشخص می کند. این نگاشت برای تولید و راه اندازی fstab استفاده می شود. جدول همچنین اندازه بلوک ها و hash_start، محل شروع درخت هش (به طور خاص، شماره بلوک آن از ابتدای تصویر) را مشخص می کند.

برای توضیح دقیق فیلدهای جدول نگاشت هدف حقیقت، به cryptsetup مراجعه کنید.

امضای جدول dm-verity

برای ایجاد امضای جدول، جدول dm-verity را امضا کنید. هنگام تأیید یک پارتیشن، ابتدا امضای جدول تأیید می شود. این کار در مقابل یک کلید روی تصویر بوت شما در یک مکان ثابت انجام می شود. کلیدها معمولاً در سیستم های ساخت سازندگان برای گنجاندن خودکار روی دستگاه ها در یک مکان ثابت گنجانده می شوند.

برای تایید پارتیشن با این امضا و کلید ترکیبی:

  1. یک کلید RSA-2048 در قالب سازگار با libmincrypt به پارتیشن /boot در /verity_key اضافه کنید. مکان کلید مورد استفاده برای تأیید درخت هش را شناسایی کنید.
  2. در fstab ورودی مربوطه، verify به پرچم‌های fs_mgr اضافه کنید.

دسته بندی امضای جدول در فراداده

امضای جدول و جدول dm-verity را در ابرداده های صحت قرار دهید. کل بلوک ابرداده نسخه‌بندی شده است، بنابراین ممکن است گسترش یابد، مانند اضافه کردن نوع دوم امضا یا تغییر برخی سفارش‌ها.

به عنوان یک بررسی سلامت عقل، یک عدد جادویی با هر مجموعه ای از ابرداده های جدول مرتبط است که به شناسایی جدول کمک می کند. از آنجایی که طول در هدر تصویر سیستم ext4 گنجانده شده است، این روشی را برای جستجوی فراداده بدون اطلاع از محتوای خود داده فراهم می کند.

این اطمینان حاصل می کند که شما برای تأیید یک پارتیشن تأیید نشده انتخاب نکرده اید. در این صورت، عدم وجود این شماره جادویی روند تأیید را متوقف می کند. این عدد شبیه به:
0xb001b001

مقادیر بایت در هگز عبارتند از:

  • بایت اول = b0
  • بایت دوم = 01
  • بایت سوم = b0
  • بایت چهارم = 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 که قبلا توضیح داده شد بایت های طول جدول
بالشتک این سازه 0 تا 32 هزار طول دارد 0

بهینه سازی dm-verity

برای دریافت بهترین عملکرد از dm-verity، باید:

  • در هسته، NEON SHA-2 را برای ARMv7 و پسوندهای SHA-2 را برای ARMv8 روشن کنید.
  • برای یافتن بهترین پیکربندی برای دستگاه خود، تنظیمات مختلف Read-Ahead و prefetch_cluster را آزمایش کنید.