وكما هو الحال مع معظم برامج تشفير الأقراص والملفات، يعتمد تشفير مساحة التخزين في Android عادةً على توفّر مفاتيح التشفير الأولية في ذاكرة النظام حتى يمكن إجراء عملية التشفير. حتى عندما يتم التشفير بواسطة أجهزة مخصّصة بدلاً من البرامج، تظل البرامج بحاجة بشكل عام إلى إدارة مفاتيح التشفير الأولية.
لا يُعدّ ذلك مشكلة عادةً لأنّ المفاتيح لا تكون متاحة أثناء الهجوم بلا إنترنت، وهو النوع الرئيسي من الهجمات التي يهدف تشفير التخزين إلى الحماية منها. ومع ذلك، هناك رغبة في توفير حماية أكبر من أنواع أخرى من الهجمات، مثل هجمات إعادة التشغيل البارد والهجمات على الإنترنت التي قد يتمكّن فيها المهاجم من تسريب ذاكرة النظام بدون اختراق الجهاز بالكامل.
لحلّ هذه المشكلة، أتاح نظام التشغيل Android 11 إمكانية استخدام المفاتيح المحمية بالأجهزة في حال توفّر دعم الأجهزة. المفاتيح المحفوظة في الأجهزة هي مفاتيح تخزين لا تعرفها البرامج إلا في شكلها المشفّر، بينما تعرفها الأجهزة المخصّصة في شكلها الأولي. يجب أن يكون هذا الجهاز قادرًا على إنشاء مفاتيح تخزين واستيرادها، وتغليف مفاتيح التخزين في أشكال مؤقتة وطويلة الأمد، واستخلاص المفاتيح الفرعية، وبرمجة أحد المفاتيح الفرعية مباشرةً في محرك تشفير مضمّن، وإرجاع مفتاح فرعي منفصل إلى البرنامج.
ملاحظة: يشير محرك التشفير المضمّن (أو أداة التشفير المضمّنة) إلى أداة تشفّر البيانات أو تفك تشفيرها أثناء انتقالها من جهاز التخزين أو إليه. ويكون ذلك عادةً وحدة تحكّم مضيفة في UFS أو eMMC تنفّذ إضافات التشفير المحدّدة بمواصفات JEDEC ذات الصلة.
تصميم
يعرض هذا القسم تصميم ميزة المفاتيح المحمية بواسطة الأجهزة، بما في ذلك متطلبات توافق الأجهزة معها. تركّز هذه المناقشة على التشفير على مستوى الملفات (FBE)، ولكن ينطبق الحل أيضًا على تشفير البيانات الوصفية.
إحدى طرق تجنُّب الحاجة إلى مفاتيح التشفير الأولية في ذاكرة النظام هي الاحتفاظ بها فقط في فتحات المفاتيح الخاصة بمحرّك التشفير المضمّن. ومع ذلك، تواجه هذه الطريقة بعض المشاكل:
- قد يتجاوز عدد مفاتيح التشفير عدد فتحات المفاتيح.
- عادةً ما تفقد محركات التشفير المضمّنة محتويات فتحات المفاتيح إذا تمت إعادة ضبط وحدة التحكّم في مضيف التخزين. إعادة ضبط وحدة التحكّم في مضيف مساحة التخزين هي إجراء عادي لاسترداد البيانات من الأخطاء يتم تنفيذه في حال حدوث أنواع معيّنة من أخطاء مساحة التخزين، ويمكن أن تحدث هذه الأخطاء في أي وقت. لذلك، عند استخدام التشفير المضمّن، يجب أن يكون نظام التشغيل جاهزًا دائمًا لإعادة برمجة فتحات المفاتيح بدون تدخل المستخدم.
- لا يمكن استخدام محرّكات التشفير المضمّنة إلا لتشفير/فك تشفير كتل كاملة من البيانات على القرص. ومع ذلك، في حالة التشفير المستند إلى الملفات، يجب أن يظل بإمكان البرنامج تنفيذ عمليات تشفير أخرى، مثل تشفير أسماء الملفات واستخلاص معرّفات المفاتيح. سيظل البرنامج بحاجة إلى إذن الوصول إلى مفاتيح التشفير المستند إلى الملفات الأولية لتنفيذ هذه المهام الأخرى.
لتجنُّب هذه المشاكل، يتم بدلاً من ذلك تحويل مفاتيح التخزين إلى مفاتيح محمية بالأجهزة، ولا يمكن فك تشفيرها واستخدامها إلا من خلال أجهزة مخصّصة. يتيح ذلك إمكانية استخدام عدد غير محدود من المفاتيح. بالإضافة إلى ذلك، يتم تعديل التسلسل الهرمي للمفاتيح ونقله جزئيًا إلى هذا الجهاز، ما يتيح إرجاع مفتاح فرعي إلى البرنامج لتنفيذ المهام التي لا يمكنها استخدام محرك تشفير مضمّن.
التسلسل الهرمي للمفاتيح
يمكن اشتقاق المفاتيح من مفاتيح أخرى باستخدام دالة اشتقاق المفاتيح (KDF)، مثل HKDF، ما يؤدي إلى إنشاء تسلسل هرمي للمفاتيح.
يوضّح المخطّط التالي تسلسلاً هرميًا نموذجيًا للمفاتيح في ميزة "التشفير المستند إلى الملفات" عند عدم استخدام المفاتيح المغلَّفة بالأجهزة:
مفتاح فئة FBE هو مفتاح التشفير الأوّلي الذي يمرّره نظام التشغيل Android إلى نواة Linux لفتح مجموعة معيّنة من الدلائل المشفّرة، مثل مساحة التخزين المشفّرة ببيانات الاعتماد لمستخدم Android معيّن. (في النواة، يُطلق على هذا المفتاح اسم مفتاح رئيسي fscrypt). يستمد النواة من هذا المفتاح المفاتيح الفرعية التالية:
- معرّف المفتاح لا يتم استخدام هذا المعرّف للتشفير، بل هو قيمة تُستخدَم لتحديد المفتاح الذي يتم من خلاله حماية ملف أو دليل معيّنَين.
- مفتاح تشفير محتوى الملف
- مفتاح تشفير أسماء الملفات
في المقابل، يوضّح المخطّط التالي التسلسل الهرمي للمفاتيح في ميزة "التشفير الكامل للجهاز" عند استخدام مفاتيح مغلفة بالأجهزة:
مقارنةً بالحالة السابقة، تمت إضافة مستوى إضافي إلى التسلسل الهرمي للمفتاح، وتمت إعادة تحديد موقع مفتاح تشفير محتوى الملف. لا تزال عقدة الجذر تمثّل المفتاح الذي يمرّره نظام التشغيل Android إلى Linux لفتح مجموعة من الأدلة المشفّرة. ومع ذلك، أصبح هذا المفتاح الآن في شكل مغلف بشكل مؤقت، ويجب تمريره إلى أجهزة مخصصة لاستخدامه. يجب أن يتضمّن هذا الجهاز واجهتَي برمجة تطبيقات تستخدمان مفتاحًا مؤقتًا:
- واجهة واحدة لاشتقاق
inline_encryption_keyوبرمجتها مباشرةً في فتحة مفتاح لمحرك التشفير المضمّن ويتيح ذلك تشفير/فك تشفير محتوى الملفات بدون أن تتمكّن البرامج من الوصول إلى مفتاح التشفير الأولي. في نواة Android الشائعة، تتوافق هذه الواجهة مع العمليةblk_crypto_ll_ops::keyslot_program، التي يجب أن ينفّذها برنامج تشغيل وحدة التخزين. - واجهة واحدة لاشتقاق
sw_secret("البرنامج السري"، والذي كان يُعرف سابقًا باسم "السر الخام") وعرضه، وهو المفتاح الذي يستخدمه نظام التشغيل Linux لاشتقاق المفاتيح الفرعية لكل شيء باستثناء تشفير محتوى الملفات في نواة Android الشائعة، تتوافق هذه الواجهة مع العمليةblk_crypto_ll_ops::derive_sw_secret، التي يجب أن ينفّذها برنامج تشغيل وحدة التخزين.
لاستخراج inline_encryption_key وsw_secret من مفتاح التخزين الأوّلي، يجب أن يستخدم الجهاز وظيفة KDF قوية من ناحية التشفير. يجب أن تتّبع وظيفة اشتقاق المفتاح هذه أفضل ممارسات التشفير، ويجب أن تتضمّن قوة أمان لا تقل عن 256 بت، أي أن تكون كافية لأي خوارزمية مستخدَمة لاحقًا. ويجب أيضًا استخدام تصنيف وسياق مميّزَين عند استخلاص كل نوع من المفاتيح الفرعية لضمان عزل المفاتيح الفرعية الناتجة تشفيرًا، أي ألّا يؤدي معرفة أحدها إلى الكشف عن أي مفتاح فرعي آخر. لا يلزم تمديد المفتاح، لأنّ مفتاح التخزين الأولي هو مفتاح عشوائي موحّد.
من الناحية الفنية، يمكن استخدام أي دالة KDF تستوفي متطلبات الأمان.
ومع ذلك، لأغراض الاختبار، تنفّذ vts_kernel_encryption_test
وظيفة اشتقاق المفاتيح نفسها في البرنامج لإعادة إنتاج النص المشفّر على القرص
والتأكّد من صحته. لتسهيل الاختبار وضمان استخدام دالة اشتقاق مفتاح آمنة تمت مراجعتها من قبل، ننصح بأن تنفّذ الأجهزة دالة اشتقاق المفتاح التلقائية التي يتحقّق منها الاختبار. بالنسبة إلى الأجهزة التي تستخدم دالة KDF مختلفة، راجِع اختبار المفاتيح المغلفة لمعرفة كيفية ضبط الاختبار وفقًا لذلك.
تغليف المفتاح
لتحقيق أهداف الأمان الخاصة بالمفاتيح المحمية بواسطة أجهزة، تم تحديد نوعَين من تغليف المفاتيح:
- التشفير المؤقت: يشفّر الجهاز المفتاح الأوّلي باستخدام مفتاح يتم إنشاؤه عشوائيًا في كل عملية تشغيل ولا يتم عرضه مباشرةً خارج الجهاز.
- التشفير طويل الأمد: يشفّر الجهاز المفتاح الأوّلي باستخدام مفتاح فريد وثابت مضمّن في الجهاز ولا يتم عرضه مباشرةً خارجه.
يتم تغليف جميع المفاتيح التي يتم تمريرها إلى نواة Linux لفتح مساحة التخزين بشكل مؤقت. يضمن ذلك أنّه إذا تمكّن أحد المهاجمين من استخراج مفتاح مستخدَم من ذاكرة النظام، لن يكون هذا المفتاح قابلاً للاستخدام ليس فقط خارج الجهاز، بل أيضًا على الجهاز بعد إعادة تشغيله.
في الوقت نفسه، يجب أن يظل بإمكان Android تخزين نسخة مشفّرة من المفاتيح على القرص حتى يمكن فتحها في المقام الأول. ستكون المفاتيح الأولية مناسبة لهذا الغرض. ومع ذلك، من المستحسن عدم توفّر المفاتيح الأولية في ذاكرة النظام على الإطلاق حتى لا يمكن استخراجها لاستخدامها خارج الجهاز، حتى إذا تم استخراجها في وقت بدء التشغيل. لهذا السبب، تم تعريف مفهوم التغليف على المدى الطويل.
ولإتاحة إدارة المفاتيح التي تمّت تغطيتها بهاتين الطريقتين المختلفتين، يجب أن تنفّذ الأجهزة الواجهات التالية:
- واجهات لإنشاء مفاتيح التخزين واستيرادها، مع إرجاعها في شكل مغلف طويل الأمد تستخدم واجهة generate
voldلإنشاء مفاتيح تخزين جديدة يستخدمها نظام التشغيل Android. تُستخدَم واجهة الاستيراد من قِبلvts_kernel_encryption_testلاستيراد مفاتيح الاختبار. - واجهة لتحويل مفتاح تخزين مغلف طويل الأمد إلى مفتاح تخزين مغلف مؤقتًا. تستخدم كل من
voldوvts_kernel_encryption_testهذه الواجهة لفتح مساحة التخزين.
خوارزمية تغليف المفتاح هي تفصيل تنفيذي، ولكن يجب أن تستخدم AEAD قويًا، مثل AES-256-GCM مع متجهات تهيئة عشوائية.
يجب إجراء تغييرات على البرنامج
يتضمّن مشروع AOSP إطار عمل أساسيًا يتيح استخدام المفاتيح المستندة إلى الأجهزة. ويشمل ذلك الدعم في مكونات مساحة المستخدم، مثل vold، بالإضافة إلى دعم نواة Linux في blk-crypto وfscrypt وdm-default-key.
التغييرات في نواة Linux
يجب تعديل برنامج تشغيل نواة Linux لوحدة التحكّم في مساحة التخزين بالجهاز مع إمكانية التشفير المضمّن ليتوافق مع المفاتيح المشفّرة بواسطة الأجهزة.
بالنسبة إلى الإصدار android17 والإصدارات الأحدث من النواة:
- اضبط
BLK_CRYPTO_KEY_TYPE_HW_WRAPPEDفيblk_crypto_profile::key_types_supported. - إتاحة
blk_crypto_ll_ops::keyslot_programبرمجة المفاتيح المغلَّفة في الأجهزة - إتاحة إمكانية إبطال مفاتيح التشفير المستندة إلى الأجهزة في
blk_crypto_ll_ops::keyslot_evict - نفِّذ
blk_crypto_ll_ops::derive_sw_secretوblk_crypto_ll_ops::import_keyوblk_crypto_ll_ops::generate_keyوblk_crypto_ll_ops::prepare_key.
بالنسبة إلى نواة android14 وandroid15 وandroid16:
- اضبط
BLK_CRYPTO_KEY_TYPE_HW_WRAPPEDفيblk_crypto_profile::key_types_supported. - إتاحة
blk_crypto_ll_ops::keyslot_programبرمجة المفاتيح المغلَّفة في الأجهزة - إتاحة إمكانية إبطال مفاتيح التشفير المستندة إلى الأجهزة في
blk_crypto_ll_ops::keyslot_evict - نفِّذ الخطوة
blk_crypto_ll_ops::derive_sw_secret.
بالنسبة إلى نواة android12 وandroid13:
- اضبط
BLK_CRYPTO_FEATURE_WRAPPED_KEYSفيblk_keyslot_manager::features. - إتاحة
blk_ksm_ll_ops::keyslot_programبرمجة المفاتيح المغلَّفة في الأجهزة - إتاحة إمكانية إبطال مفاتيح التشفير المستندة إلى الأجهزة في
blk_ksm_ll_ops::keyslot_evict - نفِّذ الخطوة
blk_ksm_ll_ops::derive_raw_secret.
بالنسبة إلى android11 نواة:
- اضبط
BLK_CRYPTO_FEATURE_WRAPPED_KEYSفيkeyslot_manager::features. - إتاحة
keyslot_mgmt_ll_ops::keyslot_programبرمجة المفاتيح المغلَّفة في الأجهزة - إتاحة إمكانية إبطال مفاتيح التشفير المستندة إلى الأجهزة في
keyslot_mgmt_ll_ops::keyslot_evict - نفِّذ الخطوة
keyslot_mgmt_ll_ops::derive_raw_secret.
تغييرات KeyMint (الإصدار القديم)
في الإصدار الحالي من المفاتيح المحمية بالأجهزة (wrappedkey)، يتم استخدام
أوامر ioctl في نواة Linux BLKCRYPTOGENERATEKEY وBLKCRYPTOIMPORTKEY وBLKCRYPTOPREPAREKEY لإنشاء المفاتيح المحمية بالأجهزة واستيرادها وإعدادها. تتطابق عمليات ioctl هذه مع الطرق في struct blk_crypto_ll_ops. ينفّذ برنامج تشغيل التخزين هذه الطرق ويتواصل مع جهاز تغليف المفاتيح لتنفيذ العملية المطلوبة. لمزيد من المعلومات حول عمليات ioctl هذه، يُرجى الاطّلاع على مستندات نواة Linux.
تمت إضافة هذه الأوامر في الإصدار 6.16 من Linux. على الأجهزة التي لم يتم إطلاقها باستخدام الحل المستند إلى ioctl، يتم استخدام حل مختلف يستند إلى KeyMint من Android (أو KeyMaster سابقًا). الحل القديم (wrappedkey_v0) غير متوافق مع نواة Linux الرئيسية أو مع الحل الحالي. يستخدم الحل القديم وظائف KeyMint التالية:
- إتاحة استخدام
TAG_STORAGE_KEYلإنشاء المفاتيح واستيرادها - إتاحة طريقة
convertStorageKeyToEphemeral
لا تكون وظيفة KeyMint هذه مطلوبة إلا على الأجهزة التي تستخدم الحل القديم، والذي يتوافق مع wrappedkey_v0 في ملف fstab.
لا تحتاج الأجهزة التي تستخدم الحلّ الحالي، والذي يتوافق مع wrappedkey
في ملف fstab، إلى تنفيذ وظيفة KeyMint هذه.
اختبار المفاتيح المغلفة
على الرغم من أنّ اختبار التشفير باستخدام مفاتيح مغلَّفة في أجهزة أصعب من اختبار التشفير باستخدام مفاتيح أولية، إلا أنّه لا يزال من الممكن إجراء الاختبار من خلال استيراد مفتاح اختبار وإعادة تنفيذ اشتقاق المفتاح الذي يجريه الجهاز. يتم تنفيذ ذلك
في vts_kernel_encryption_test. لتشغيل هذا الاختبار،
نفِّذ ما يلي:
atest -v vts_kernel_encryption_test
اقرأ سجلّ الاختبار وتأكَّد من عدم تخطّي حالات اختبار المفتاح المحمي بواسطة الجهاز (مثل FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy وDmDefaultKeyTest.TestHwWrappedKey) بسبب عدم رصد إمكانية استخدام المفاتيح المحمية بواسطة الجهاز، لأنّ نتائج الاختبار تظل "ناجحة" في هذه الحالة.
بشكلٍ تلقائي، يفترض vts_kernel_encryption_test أنّ الجهاز ينفّذ وظيفة اشتقاق مفتاح (KDF) تُسمى kdf1. تنتمي وظيفة اشتقاق المفاتيح هذه إلى مجموعة وظائف اشتقاق المفاتيح في وضع العداد من NIST
SP 800-108، وتستخدم AES-256-CMAC كدالة شبه عشوائية. لمزيد من المعلومات حول CMAC، يُرجى الاطّلاع على مواصفات CMAC. تستخدم وظيفة اشتقاق المفاتيح سياقات وتصنيفات محدّدة عند اشتقاق كل مفتاح فرعي. يجب أن تنفّذ الأجهزة هذه الدالة، بما في ذلك الاختيار الدقيق للسياق والتصنيف وتنسيق سلسلة الإدخال الثابتة عند اشتقاق كل مفتاح فرعي.
ومع ذلك، تنفّذ vts_kernel_encryption_test أيضًا وظائف KDF إضافية
kdf2 من خلال kdf4. وهي آمنة تمامًا مثل
kdf1 ولا تختلف إلا في اختيار السياقات والتصنيفات
وتنسيق سلسلة الإدخال الثابتة. وهي متوفّرة فقط لتتوافق مع الأجهزة المختلفة.
بالنسبة إلى الأجهزة التي تستخدم دالة KDF مختلفة، اضبط السمة ro.crypto.hw_wrapped_keys.kdf للنظام في PRODUCT_VENDOR_PROPERTIES على اسم دالة KDF كما هو محدّد في الرمز المصدري للاختبار. يؤدي ذلك إلى أن يبحث vts_kernel_encryption_test عن KDF بدلاً من kdf1. على سبيل المثال، لاختيار
kdf2، استخدِم:
PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2
بالنسبة إلى الأجهزة التي تستخدم دالة اشتقاق مفتاح لا يتوافق مع الاختبار، أضِف أيضًا تنفيذًا لدالة اشتقاق المفتاح هذه إلى الاختبار وأعطِها اسمًا فريدًا.
تفعيل المفاتيح الملتفة
عندما يعمل دعم المفتاح المغلّف في الجهاز بشكل صحيح، عليك إجراء التغييرات التالية على ملف fstab الخاص بالجهاز لكي يستخدمه نظام Android في التشفير المستند إلى الملفات وتشفير البيانات الوصفية:
- FBE: أضِف العلامة
wrappedkey(أوwrappedkey_v0للإصدار القديم) إلى المَعلمةfileencryption. على سبيل المثال، استخدِمfileencryption=::inlinecrypt_optimized+wrappedkey. لمزيد من التفاصيل، يُرجى الاطّلاع على مستندات FBE. - تشفير البيانات الوصفية: أضِف العلامة
wrappedkey(أوwrappedkey_v0للإصدار القديم) إلى المَعلمةmetadata_encryption. على سبيل المثال، استخدِمmetadata_encryption=:wrappedkey. لمزيد من التفاصيل، راجِع مستندات تشفير البيانات الوصفية.
في كل حالة، يتوفّر إصداران من العلامة:
- تتيح
wrappedkey، المتوافقة مع الإصدار 17 من نظام التشغيل Android والإصدارات الأحدث، استخدام الإصدار الحالي من المفاتيح المحمية بواسطة الأجهزة. هذا الإصدار متوافق مع نواة Linux الرئيسية. - تتيح واجهة برمجة التطبيقات
wrappedkey_v0، المتوافقة مع الإصدار 11 من نظام التشغيل Android والإصدارات الأحدث، استخدام الإصدار القديم من المفاتيح المحمية بواسطة الأجهزة. هذا الإصدار غير متوافق مع نواة Linux الرئيسية. ويعمل على توجيه بعض العمليات من خلال KeyMint ويستخدم تنسيقًا غير عادي على القرص. لمزيد من المعلومات، يُرجى الاطّلاع على التغييرات في KeyMint (الإصدار القديم).
على الأجهزة التي تعمل بالإصدار 17 من نظام التشغيل Android أو إصدار أحدث، يُفضّل استخدام wrappedkey.
على الأجهزة التي تم إطلاقها مسبقًا مع wrappedkey_v0، واصِل استخدام wrappedkey_v0 للتوافق مع الأنظمة القديمة.