از Android 12، ماژول Android Runtime (ART) یک ماژول Mainline است. به روز رسانی ماژول ممکن است به آن نیاز داشته باشد تا مصنوعات کامپایل پیش از زمان (AOT) bootclasspath jars و سرور سیستم را بازسازی کند. از آنجایی که این مصنوعات حساس به امنیت هستند، اندروید 12 از قابلیتی به نام امضای روی دستگاه استفاده می کند تا از دستکاری این مصنوعات جلوگیری کند. این صفحه معماری امضای روی دستگاه و تعاملات آن با سایر ویژگی های امنیتی اندروید را پوشش می دهد.
طراحی سطح بالا
امضای روی دستگاه دارای دو جزء اصلی است:
odrefreshبخشی از ماژول ART Mainline است. این مسئول تولید مصنوعات زمان اجرا است. این آرتیفکت های موجود را در برابر نسخه نصب شده ماژول ART، شیشه های bootclasspath و شیشه های سرور سیستم بررسی می کند تا مشخص کند که آیا به روز هستند یا نیاز به بازسازی دارند. اگر نیاز به بازسازی داشته باشند،odrefreshآنها را تولید و ذخیره می کند.odsignیک باینری است که بخشی از پلتفرم اندروید است. در اوایل راهاندازی، درست پس از نصب پارتیشن/dataاجرا میشود. مسئولیت اصلی آن فراخوانیodrefreshبرای بررسی اینکه آیا مصنوعات نیاز به تولید یا به روز رسانی دارند یا خیر است. برای هر مصنوع جدید یا به روز شده ای کهodrefreshایجاد می کند،odsignیک تابع هش را محاسبه می کند. نتیجه چنین محاسبه هش، خلاصه فایل نامیده می شود. برای هر آرتیفکتی که قبلاً وجود دارد،odsignتأیید میکند که خلاصههای مصنوعات موجود با خلاصههایی کهodsignقبلاً محاسبه کرده بود مطابقت دارد. این تضمین می کند که مصنوعات دستکاری نشده اند.
در شرایط خطا، مانند زمانی که خلاصه برای یک فایل مطابقت ندارد، odrefresh و odsign تمام مصنوعات موجود در /data را دور می اندازند و سعی می کنند آنها را بازسازی کنند. در صورت عدم موفقیت، سیستم به حالت JIT باز می گردد.
odrefresh و odsign توسط dm-verity محافظت می شوند و بخشی از زنجیره Verified Boot اندروید هستند.
محاسبه خلاصه فایل با fs-verity
fs-verity یکی از ویژگی های هسته لینوکس است که بر اساس درخت Merkle داده های فایل را تأیید می کند. فعال کردن fs-verity روی یک فایل باعث میشود که سیستم فایل با استفاده از هشهای SHA-256، درخت Merkle را روی دادههای فایل بسازد، آن را در مکانی مخفی در کنار فایل ذخیره کند و فایل را بهعنوان فقط خواندنی علامتگذاری کند. fs-verity بهطور خودکار دادههای فایل را در برابر درخت Merkle در هنگام خواندن تأیید میکند. fs-verity هش ریشه درخت Merkle را به عنوان مقداری به نام خلاصه فایل fs-verity در دسترس قرار میدهد و fs-verity تضمین میکند که هر دادهای که از فایل خوانده میشود با این خلاصه فایل سازگار است.
odsign از fs-verity برای بهبود عملکرد بوت با بهینه سازی احراز هویت رمزنگاری مصنوعات کامپایل شده روی دستگاه در زمان بوت استفاده می کند. هنگامی که یک آرتیفکت تولید می شود، odsign fs-verity را روی آن فعال می کند. وقتی odsign یک آرتیفکت را تأیید میکند، خلاصه فایل fs-verity را به جای هش کامل فایل تأیید میکند. این کار نیاز به خواندن و هش داده های کامل آرتیفکت را در زمان بوت حذف می کند. در عوض، دادههای مصنوع بر حسب تقاضا توسط fs-verity هنگام استفاده، به صورت بلوک به بلوک هش میشوند.
در دستگاههایی که هسته آنها از fs-verity پشتیبانی نمیکند، odsign به محاسبات خلاصه فایلها در فضای کاربران بازمیگردد. odsign از همان الگوریتم هش مبتنی بر درخت Merkle به عنوان fs-verity استفاده می کند، بنابراین خلاصه ها در هر صورت یکسان هستند. fs-verity در همه دستگاه هایی که با اندروید 11 و بالاتر راه اندازی شده اند مورد نیاز است.
ذخیره سازی فایل هضم
odsign خلاصه فایل های مصنوعات را در یک فایل جداگانه به نام odsign.info ذخیره می کند. برای اطمینان از اینکه odsign.info دستکاری نشده است، odsign.info با یک کلید امضا امضا می شود که دارای ویژگی های امنیتی مهم است. به طور خاص، کلید را می توان تنها در هنگام راه اندازی اولیه تولید و استفاده کرد، که در آن نقطه فقط کد قابل اعتماد در حال اجرا است. برای جزئیات به کلیدهای امضای مورد اعتماد مراجعه کنید.
تایید خلاصه فایل ها
در هر بوت، اگر odrefresh تشخیص دهد که مصنوعات موجود بهروز هستند، odsign تضمین میکند که فایلها از زمان تولید دستکاری نشدهاند. odsign این کار را با تأیید هضم فایل انجام می دهد. ابتدا، امضای odsign.info را تأیید می کند. اگر امضا معتبر باشد، odsign تأیید میکند که خلاصه هر فایل با خلاصه مربوطه در odsign.info مطابقت دارد.
کلیدهای امضای مورد اعتماد
اندروید 12 یک ویژگی جدید Keystore به نام کلیدهای مرحله راهاندازی را معرفی میکند که نگرانیهای امنیتی زیر را برطرف میکند:
- چه چیزی مانع از استفاده مهاجم از کلید امضای ما برای امضای نسخه خود از
odsign.infoمی شود؟ - چه چیزی مانع از این میشود که مهاجم کلید امضای خود را تولید کند و از آن برای امضای نسخه خود از
odsign.infoاستفاده کند؟
کلیدهای مرحله بوت، چرخه راهاندازی اندروید را به سطوح تقسیم میکنند و ایجاد و استفاده از یک کلید را به صورت رمزنگاری به سطح مشخصی گره میزنند. odsign کلید امضای خود را در سطح اولیه ایجاد می کند، زمانی که فقط کدهای قابل اعتماد در حال اجرا است، که از طریق dm-verity محافظت می شود.
سطوح مرحله بوت از 0 تا عدد جادویی 1000000000 شماره گذاری می شوند. در طول فرآیند بوت اندروید، می توانید با تنظیم یک ویژگی سیستم از init.rc ، سطح بوت را افزایش دهید. به عنوان مثال، کد زیر سطح بوت را روی 10 تنظیم می کند:
setprop keystore.boot_level 10
مشتریان Keystore می توانند کلیدهایی ایجاد کنند که به سطح بوت خاصی گره خورده باشند. به عنوان مثال، اگر یک کلید برای بوت سطح 10 ایجاد کنید، آن کلید فقط زمانی می تواند استفاده شود که دستگاه در سطح بوت 10 باشد.
odsign از سطح بوت 30 استفاده می کند و کلید امضایی که ایجاد می کند به آن سطح بوت گره خورده است. قبل از استفاده از کلید برای امضای مصنوعات، odsign تأیید می کند که کلید به سطح بوت 30 متصل است.
این از دو حمله ای که قبلا در این بخش توضیح داده شد جلوگیری می کند:
- مهاجمان نمی توانند از کلید تولید شده استفاده کنند، زیرا زمانی که مهاجم فرصت اجرای کدهای مخرب را داشته باشد، سطح بوت از 30 بیشتر شده است و Keystore از عملیاتی که از کلید استفاده می کند خودداری می کند.
- مهاجمان نمی توانند کلید جدیدی ایجاد کنند، زیرا زمانی که مهاجم فرصت اجرای کدهای مخرب را داشته باشد، سطح بوت از 30 بیشتر شده است و Keystore از ایجاد کلید جدید با آن سطح بوت خودداری می کند. اگر یک مهاجم کلید جدیدی ایجاد کند که به سطح بوت 30 گره نخورده باشد،
odsignآن را رد می کند.
Keystore تضمین می کند که سطح بوت به درستی اجرا می شود. بخشهای زیر به جزئیات بیشتری در مورد نحوه انجام این کار برای نسخههای مختلف KeyMint (که قبلاً Keymaster بود) میپردازد.
پیاده سازی Keymaster 4.0
نسخههای مختلف Keymaster اجرای کلیدهای مرحله بوت را بهطور متفاوتی انجام میدهند. در دستگاههای دارای Keymaster 4.0 TEE/StrongBox، Keymaster اجرا را به صورت زیر انجام میدهد:
- در اولین راهاندازی، Keystore یک کلید متقارن K0 با برچسب
MAX_USES_PER_BOOTروی1ایجاد میکند. این بدان معناست که کلید فقط یک بار در هر بوت قابل استفاده است. - در طول بوت، اگر سطح بوت افزایش یابد، یک کلید جدید برای آن سطح بوت میتواند با استفاده از یک تابع HKDF از K0 تولید شود:
Ki+i=HKDF(Ki, "some_fixed_string"). به عنوان مثال، اگر از سطح بوت 0 به سطح بوت 10 حرکت کنید، HKDF 10 بار برای استخراج K10 از K0 فراخوانی می شود. هنگامی که سطح بوت تغییر می کند، کلید سطح بوت قبلی از حافظه پاک می شود و کلیدهای مرتبط با سطوح بوت قبلی دیگر در دسترس نیستند.
کلید K0 یک کلید
MAX_USES_PER_BOOT=1است. این بدان معنی است که استفاده از آن کلید بعداً در هنگام بوت غیرممکن است، زیرا حداقل یک انتقال سطح بوت (به سطح بوت نهایی) همیشه رخ می دهد.
هنگامی که یک کلاینت Keystore مانند odsign درخواست می کند که یک کلید در سطح بوت i ایجاد شود، حباب آن با کلید Ki رمزگذاری می شود. از آنجایی که Ki پس از سطح بوت i در دسترس نیست، این کلید را نمی توان در مراحل بعدی بوت ایجاد یا رمزگشایی کرد.
اجرای Keymaster 4.1 و KeyMint 1.0
پیاده سازی های Keymaster 4.1 و KeyMint 1.0 تا حد زیادی مشابه پیاده سازی Keymaster 4.0 هستند. تفاوت اصلی این است که K0 یک کلید MAX_USES_PER_BOOT نیست، بلکه یک کلید EARLY_BOOT_ONLY است که در Keymaster 4.1 معرفی شد. یک کلید EARLY_BOOT_ONLY فقط در مراحل اولیه بوت، زمانی که هیچ کد غیرقابل اعتمادی اجرا نمی شود، قابل استفاده است. این یک سطح حفاظتی اضافی را فراهم می کند: در پیاده سازی Keymaster 4.0، مهاجمی که سیستم فایل و SELinux را به خطر می اندازد می تواند پایگاه داده Keystore را تغییر دهد تا MAX_USES_PER_BOOT=1 کلید خود را برای امضای مصنوعات ایجاد کند. چنین حمله ای با اجرای Keymaster 4.1 و KeyMint 1.0 غیرممکن است، زیرا کلیدهای EARLY_BOOT_ONLY فقط در هنگام راه اندازی اولیه می توانند ایجاد شوند.
جزء عمومی کلیدهای امضای قابل اعتماد
odsign جزء کلید عمومی کلید امضا را از Keystore بازیابی می کند. با این حال، Keystore آن کلید عمومی را از TEE/SE که کلید خصوصی مربوطه را نگه می دارد، بازیابی نمی کند. در عوض، کلید عمومی را از پایگاه داده روی دیسک خود بازیابی می کند. این بدان معنی است که مهاجمی که سیستم فایل را به خطر می اندازد می تواند پایگاه داده Keystore را طوری تغییر دهد که حاوی کلید عمومی باشد که بخشی از یک جفت کلید عمومی/خصوصی تحت کنترل آنها است.
برای جلوگیری از این حمله، odsign یک کلید HMAC اضافی با همان سطح بوت کلید امضا ایجاد می کند. سپس، هنگام ایجاد کلید امضا، odsign از این کلید HMAC برای ایجاد امضای کلید عمومی استفاده می کند و آن را روی دیسک ذخیره می کند. در راهاندازیهای بعدی، هنگام بازیابی کلید عمومی کلید امضا، از کلید HMAC برای تأیید مطابقت امضای روی دیسک با امضای کلید عمومی بازیابی شده استفاده میکند. اگر مطابقت داشته باشند، کلید عمومی قابل اعتماد است، زیرا کلید HMAC فقط در سطوح اولیه راهاندازی قابل استفاده است و بنابراین نمیتواند توسط مهاجم ایجاد شود.