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

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

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

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

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

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

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

يحمي dm-verity كلًّا من odrefresh وodsign، وهما جزء من سلسلة التشغيل المُتحقّق منه في 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 على جميع الأجهزة التي تم تشغيلها باستخدام Android 11 والإصدارات الأحدث.

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

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

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

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

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

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

  • ما الذي يمنع المهاجم من استخدام مفتاح التوقيع الخاص بنا لتوقيع نسخته الخاصة من 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.

يضمن "متجر المفاتيح" فرض مستوى التشغيل بشكل صحيح. تتناول القسم التالية بالتفصيل كيفية إجراء ذلك لإصدارات Keymaster المختلفة.

تنفيذ Keymaster 4.0

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

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

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

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