بنية التوقيع على الجهاز فقط

اعتبارًا من Android 12، أصبحت وحدة Android Runtime (ART) إحدى وحدات Mainline. قد يتطلّب تحديث الوحدة إعادة إنشاء عناصر التحويل البرمجي مسبقًا (AOT) الخاصة بحِزم bootclasspath وخادم النظام. بما أنّ هذه العناصر حساسة من الناحية الأمنية، يستخدم نظام التشغيل Android 12 ميزة تُعرف باسم التوقيع على الجهاز لمنع التلاعب بهذه العناصر. تتناول هذه الصفحة بنية التوقيع على الجهاز وتفاعلاتها مع ميزات أمان Android الأخرى.

التصميم العالي المستوى

تتضمّن ميزة "التوقيع على الجهاز" مكوّنَين أساسيَّين:

  • odrefresh هو جزء من وحدة ART Mainline. وهي المسؤولة عن إنشاء عناصر وقت التشغيل. ويتحقّق من العناصر الحالية مقارنةً بالإصدار المثبَّت من وحدة ART وملفات JAR الخاصة بمسار التمهيد وملفات JAR الخاصة بخادم النظام لتحديد ما إذا كانت هذه العناصر محدّثة أو بحاجة إلى إعادة إنشائها. إذا كان يجب إعادة إنشائها، ينشئها odrefresh ويخزّنها.

  • odsign هو ملف ثنائي يشكّل جزءًا من نظام Android الأساسي. يتم تشغيله أثناء عملية بدء التشغيل المبكر، مباشرةً بعد تثبيت القسم /data. وتتمثل مسؤوليته الرئيسية في استدعاء odrefresh للتحقّق مما إذا كان يجب إنشاء أي عناصر أو تعديلها. بالنسبة إلى أي عناصر جديدة أو معدَّلة ينشئها odrefresh، يحسب odsign دالة تجزئة. وتُعرف نتيجة عملية حساب التجزئة هذه باسم ملخّص الملف. بالنسبة إلى أي عناصر حالية، تتحقّق odsign من أنّ الملخّصات الخاصة بالعناصر الحالية تتطابق مع الملخّصات التي سبق أن حسبتها odsign. ويضمن ذلك عدم التلاعب بالنتائج.

في حالات الخطأ، مثل عدم تطابق الملخّص الخاص بملف، تتجاهل odrefresh وodsign جميع العناصر الحالية على /data وتحاول إعادة إنشائها. وفي حال تعذُّر ذلك، يعود النظام إلى وضع التجميع أثناء التشغيل (JIT).

odrefresh وodsign محميان بواسطة dm-verity، وهما جزء من سلسلة "التشغيل المتحقّق منه" في Android.

حساب ملخّصات الملفات باستخدام fs-verity

fs-verity هي ميزة في نواة Linux تعمل على التحقّق من بيانات الملفات استنادًا إلى شجرة Merkle. يؤدي تفعيل fs-verity على ملف إلى جعل نظام الملفات ينشئ شجرة Merkle فوق بيانات الملف باستخدام تجزئات SHA-256، ويخزّنها في موقع مخفي بجانب الملف، ويضع علامة للقراءة فقط على الملف. تتحقّق 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 من نظام التشغيل Android والإصدارات الأحدث.

تخزين ملخّصات الملفات

يخزّن odsign خلاصات الملفات الخاصة بالقطع الأثرية في ملف منفصل باسم odsign.info. للتأكّد من عدم التلاعب بـ odsign.info، يتم توقيع odsign.info باستخدام مفتاح توقيع يتضمّن خصائص أمان مهمة. على وجه الخصوص، لا يمكن إنشاء المفتاح واستخدامه إلا أثناء عملية التشغيل المبكر، وعندها لا يتم تشغيل سوى الرمز الموثوق به. راجِع مفاتيح التوقيع الموثوقة للحصول على التفاصيل.

التحقّق من ملخّصات الملفات

في كل عملية تشغيل، إذا تبيّن لـ odrefresh أنّ العناصر الحالية محدّثة، تتأكّد odsign من أنّه لم يتم التلاعب بالملفات منذ إنشائها. تنفّذ odsign ذلك من خلال التحقّق من ملخّصات الملفات. أولاً، يتم التحقّق من توقيع odsign.info. إذا كانت التوقيع صالحة، تتحقّق odsign من أنّ الملخّص لكل ملف يطابق الملخّص المقابل في odsign.info.

مفاتيح التوقيع الموثوق بها

يقدّم Android 12 ميزة جديدة في Keystore تُعرف باسم مفاتيح مرحلة بدء التشغيل، وهي تعالج المخاوف الأمنية التالية:

  • ما الذي يمنع المهاجم من استخدام مفتاح التوقيع لتوقيع إصدار خاص به من odsign.info؟
  • ما الذي يمنع المخترق من إنشاء مفتاح توقيع خاص به واستخدامه لتوقيع نسخته الخاصة من odsign.info؟

تقسّم مفاتيح مرحلة التشغيل دورة تشغيل Android إلى مستويات، وتربط بشكل مشفّر إنشاء المفتاح واستخدامه بمستوى محدّد. ينشئ odsign مفتاح التوقيع الخاص به على مستوى مبكر، عندما لا يتم تشغيل سوى الرمز الموثوق به، ويتم حمايته من خلال dm-verity.

يتم ترقيم مستويات مرحلة التشغيل من 0 إلى الرقم السحري 1000000000. أثناء عملية تمهيد Android، يمكنك زيادة مستوى التمهيد من خلال ضبط خاصية نظام من 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 بشكل مختلف مع تنفيذ مفاتيح مرحلة بدء التشغيل. على الأجهزة التي تتضمّن بيئة تنفيذ موثوقة (TEE) أو StrongBox بإصدار Keymaster 4.0، يتولّى Keymaster تنفيذ ما يلي:

  1. عند بدء التشغيل لأول مرة، تنشئ خدمة Keystore مفتاحًا متماثلاً K0 مع ضبط العلامة MAX_USES_PER_BOOT على 1. وهذا يعني أنّه لا يمكن استخدام المفتاح إلا مرة واحدة لكل عملية تشغيل.
  2. أثناء عملية التشغيل، إذا تم رفع مستوى التشغيل، يمكن إنشاء مفتاح جديد لهذا المستوى من K0 باستخدام دالة HKDF: Ki+i=HKDF(Ki, "some_fixed_string"). على سبيل المثال، إذا انتقلت من مستوى التشغيل 0 إلى مستوى التشغيل 10، سيتم استدعاء HKDF 10 مرات لاشتقاق K10 من K0.
  3. عندما يتغير مستوى التشغيل، يتم محو مفتاح مستوى التشغيل السابق من الذاكرة، وتصبح المفاتيح المرتبطة بمستويات التشغيل السابقة غير متاحة.

    المفتاح 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 خاص به لتوقيع العناصر. لا يمكن تنفيذ هذا النوع من الهجمات باستخدام الإصدارين 4.1 من Keymaster و1.0 من KeyMint، لأنّه لا يمكن إنشاء مفاتيح EARLY_BOOT_ONLY إلا أثناء عملية بدء التشغيل المبكر.

المكوّن العام لمفاتيح التوقيع الموثوقة

يسترد odsign مكوّن المفتاح العام لمفتاح التوقيع من "مخزن المفاتيح". ومع ذلك، لا يسترد Keystore المفتاح العام من بيئة التنفيذ الموثوقة (TEE) أو العنصر الآمن (SE) اللذين يحتويان على المفتاح الخاص المقابل. بدلاً من ذلك، يسترد المفتاح العام من قاعدة البيانات الخاصة به على القرص. وهذا يعني أنّ أي مهاجم يخترق نظام الملفات يمكنه تعديل قاعدة بيانات Keystore لتتضمّن مفتاحًا عامًا يندرج ضمن زوج مفاتيح عام/خاص يخضع لتحكّمه.

لمنع هذا النوع من الهجمات، تنشئ odsign مفتاح HMAC إضافيًا بمستوى تمهيد مماثل لمفتاح التوقيع. بعد ذلك، عند إنشاء مفتاح التوقيع، تستخدم odsign مفتاح HMAC هذا لإنشاء توقيع للمفتاح العام وتخزّنه على القرص. في عمليات إعادة التشغيل اللاحقة، عند استرداد المفتاح العام لمفتاح التوقيع، يتم استخدام مفتاح HMAC للتحقّق من أنّ التوقيع المخزّن على القرص يطابق توقيع المفتاح العام الذي تم استرداده. وفي حال تطابقت القيمتان، يكون المفتاح العام موثوقًا به لأنّه لا يمكن استخدام مفتاح HMAC إلا في مستويات بدء التشغيل المبكر، وبالتالي لا يمكن أن يكون قد أنشأه مهاجم.