مانند بسیاری از نرم افزارهای رمزگذاری دیسک و فایل، رمزگذاری ذخیره سازی اندروید به طور سنتی به کلیدهای رمزگذاری خام موجود در حافظه سیستم متکی است تا رمزگذاری انجام شود. حتی زمانی که رمزگذاری توسط سخت افزار اختصاصی به جای نرم افزار انجام می شود، نرم افزار به طور کلی هنوز نیاز به مدیریت کلیدهای رمزگذاری خام دارد.
این معمولاً به عنوان یک مشکل تلقی نمی شود زیرا کلیدها در طول حمله آفلاین وجود ندارند، که نوع اصلی حمله است که رمزگذاری ذخیره سازی برای محافظت در برابر آن طراحی شده است. با این حال، میل به افزایش محافظت در برابر انواع دیگر حملات، مانند حملات بوت سرد ، و حملات آنلاین که در آن مهاجم ممکن است بتواند حافظه سیستم را بدون به خطر انداختن کامل دستگاه نشت کند، وجود دارد.
برای حل این مشکل، اندروید 11 پشتیبانی از کلیدهای سخت افزاری را معرفی کرد، جایی که پشتیبانی سخت افزاری وجود دارد. کلیدهای سخت افزاری کلیدهای ذخیره سازی هستند که به صورت خام فقط برای سخت افزار اختصاصی شناخته می شوند. نرم افزار این کلیدها را فقط به صورت پیچیده (رمزگذاری شده) می بیند و با آنها کار می کند. این سختافزار باید قادر به تولید و وارد کردن کلیدهای ذخیرهسازی، بستهبندی کلیدهای ذخیرهسازی به شکلهای زودگذر و بلندمدت، استخراج کلیدهای فرعی، برنامهنویسی مستقیم یک کلید فرعی در یک موتور کریپتو درون خطی و بازگرداندن یک کلید فرعی جداگانه به نرمافزار باشد.
توجه: یک موتور کریپتو درون خطی (یا سختافزار رمزگذاری درون خطی ) به سختافزاری اطلاق میشود که دادهها را در حالی که در راه است به/از دستگاه ذخیرهسازی رمزگذاری/رمزگشایی میکند. معمولاً این یک کنترلکننده میزبان UFS یا eMMC است که پسوندهای رمزنگاری تعریف شده توسط مشخصات JEDEC مربوطه را پیادهسازی میکند.
طراحی
این بخش طراحی ویژگی کلیدهای پیچیده شده با سخت افزار را ارائه می دهد، از جمله اینکه چه پشتیبانی سخت افزاری برای آن لازم است. این بحث بر روی رمزگذاری مبتنی بر فایل (FBE) متمرکز است، اما راه حل برای رمزگذاری ابرداده نیز کاربرد دارد.
یکی از راههای اجتناب از نیاز به کلیدهای رمزنگاری خام در حافظه سیستم این است که آنها را فقط در شکافهای یک موتور رمزنگاری درون خطی نگه دارید. با این حال، این رویکرد با مشکلاتی روبرو می شود:
- ممکن است تعداد کلیدهای رمزگذاری از تعداد شکاف های کلید بیشتر شود.
- موتورهای رمزنگاری درون خطی معمولاً در صورت بازنشانی کنترلکننده میزبان ذخیرهسازی، محتویات کلیدهای خود را از دست میدهند. بازنشانی کنترلکننده میزبان ذخیرهسازی یک روش استاندارد بازیابی خطا است که در صورت بروز انواع خاصی از خطاهای ذخیرهسازی اجرا میشود و چنین خطاهایی در هر زمانی ممکن است رخ دهند. بنابراین، زمانی که از رمزنگاری درون خطی استفاده میشود، سیستم عامل باید همیشه آماده برنامهریزی مجدد شکافها بدون دخالت کاربر باشد.
- موتورهای رمزنگاری درون خطی را می توان فقط برای رمزگذاری/رمزگشایی بلوک های کامل داده روی دیسک استفاده کرد. با این حال، در مورد FBE، نرم افزار هنوز باید بتواند کارهای رمزنگاری دیگری مانند رمزگذاری نام فایل ها و استخراج شناسه های کلیدی را انجام دهد. نرم افزار همچنان برای انجام این کار دیگر نیاز به دسترسی به کلیدهای خام FBE دارد.
برای جلوگیری از این مشکلات، کلیدهای ذخیره سازی در عوض به کلیدهای سخت افزاری تبدیل می شوند که فقط توسط سخت افزار اختصاصی قابل باز کردن و استفاده هستند. این اجازه می دهد تا تعداد نامحدودی از کلیدها پشتیبانی شوند. علاوه بر این، سلسلهمراتب کلید اصلاح شده و تا حدی به این سختافزار منتقل میشود، که اجازه میدهد یک کلید فرعی به نرمافزار برای کارهایی که نمیتوانند از یک موتور رمزنگاری درون خطی استفاده کنند، بازگردانده شود.
سلسله مراتب کلیدی
کلیدها را می توان با استفاده از یک تابع مشتق کلید (KDF) مانند HKDF از کلیدهای دیگر مشتق کرد که منجر به سلسله مراتب کلید می شود.
نمودار زیر یک سلسله مراتب کلید معمولی برای FBE را در زمانی که از کلیدهای پیچیده شده با سخت افزار استفاده نمی شود نشان می دهد:
کلید کلاس FBE کلید رمزگذاری خامی است که اندروید به هسته لینوکس ارسال می کند تا مجموعه خاصی از دایرکتوری های رمزگذاری شده را باز کند، مانند فضای ذخیره سازی رمزگذاری شده با اعتبار برای یک کاربر خاص اندروید. (در هسته، این کلید یک کلید اصلی fscrypt نامیده می شود.) از این کلید، هسته زیر کلیدهای زیر را استخراج می کند:
- شناسه کلید این برای رمزگذاری استفاده نمی شود، بلکه مقداری است که برای شناسایی کلیدی که یک فایل یا دایرکتوری خاص با آن محافظت می شود، استفاده می شود.
- کلید رمزگذاری محتویات فایل
- کلید رمزگذاری نام فایل ها
در مقابل، نمودار زیر سلسله مراتب کلید را برای FBE در هنگام استفاده از کلیدهای پیچیده شده با سخت افزار نشان می دهد:
در مقایسه با مورد قبلی، یک سطح اضافی به سلسله مراتب کلید اضافه شده است و کلید رمزگذاری محتویات فایل جابجا شده است. گره ریشه همچنان نشان دهنده کلیدی است که اندروید برای باز کردن مجموعه ای از دایرکتوری های رمزگذاری شده به لینوکس می دهد. با این حال، اکنون آن کلید به شکل موقتی پیچیده شده است و برای استفاده باید از آن به سخت افزار اختصاصی منتقل شود. این سخت افزار باید دو اینترفیس را پیاده سازی کند که یک کلید موقتی پیچیده شده باشد:
- یک رابط برای استخراج
inline_encryption_key
و برنامهریزی مستقیم آن در شکاف کلید موتور کریپتو داخلی. این اجازه می دهد تا محتویات فایل بدون دسترسی نرم افزار به کلید خام، رمزگذاری/رمزگشایی شوند. در کرنل های رایج اندروید، این رابط مربوط به عملیاتblk_crypto_ll_ops::keyslot_program
است که باید توسط درایور ذخیره سازی پیاده سازی شود. - یک رابط برای استخراج و بازگرداندن
sw_secret
("محرمانه نرم افزار" -- همچنین در برخی مکان ها "راز خام" نیز نامیده می شود)، که کلیدی است که لینوکس برای استخراج کلیدهای فرعی برای هر چیزی غیر از رمزگذاری محتوای فایل استفاده می کند. در هسته های رایج اندروید، این رابط مربوط به عملیاتblk_crypto_ll_ops::derive_sw_secret
است که باید توسط درایور ذخیره سازی پیاده سازی شود.
برای استخراج inline_encryption_key
و sw_secret
از کلید ذخیره سازی خام، سخت افزار باید از یک KDF رمزنگاری قوی استفاده کند. این KDF باید بهترین شیوه های رمزنگاری را دنبال کند. باید حداقل 256 بیت قدرت امنیتی داشته باشد، یعنی برای هر الگوریتمی که بعدا استفاده می شود کافی است. همچنین هنگام استخراج هر نوع از کلیدهای فرعی باید از یک برچسب و زمینه متمایز استفاده کند تا تضمین کند که کلیدهای فرعی حاصل از نظر رمزنگاری ایزوله هستند، یعنی دانش یکی از آنها هیچ کلید دیگری را آشکار نمی کند. کشش کلید لازم نیست، زیرا کلید ذخیره سازی خام در حال حاضر یک کلید تصادفی یکنواخت است.
از نظر فنی، هر KDF که الزامات امنیتی را برآورده می کند، می تواند مورد استفاده قرار گیرد. با این حال، برای اهداف آزمایشی، vts_kernel_encryption_test
همان KDF را در نرمافزار پیادهسازی میکند تا متن رمز روی دیسک را بازتولید کند و صحت آن را تأیید کند. برای سهولت تست و اطمینان از استفاده از یک KDF ایمن و از قبل بازبینی شده، توصیه می کنیم سخت افزار KDF پیش فرضی را که آزمایش آن را بررسی می کند، پیاده سازی کند. برای سختافزاری که از KDF متفاوتی استفاده میکند، برای نحوه پیکربندی تست بر اساس آن، به تست کلیدهای بستهبندی شده مراجعه کنید.
بسته بندی کلید
برای دستیابی به اهداف امنیتی کلیدهای سخت افزاری، دو نوع بسته بندی کلید تعریف شده است:
- بسته بندی زودگذر : سخت افزار کلید خام را با استفاده از کلیدی رمزگذاری می کند که به طور تصادفی در هر بوت ایجاد می شود و مستقیماً در خارج از سخت افزار قرار نمی گیرد.
- بستهبندی طولانیمدت : سختافزار کلید خام را با استفاده از یک کلید منحصربهفرد و ثابت که در سختافزار تعبیه شده است، رمزگذاری میکند که مستقیماً در خارج از سختافزار قرار نمیگیرد.
تمام کلیدهایی که برای باز کردن قفل فضای ذخیرهسازی به هسته لینوکس ارسال میشوند، بهصورت موقتی پیچیده شدهاند. این تضمین میکند که اگر مهاجم بتواند یک کلید در حال استفاده را از حافظه سیستم استخراج کند، آن کلید نه تنها در دستگاه، بلکه در دستگاه پس از راهاندازی مجدد غیرقابل استفاده است.
در عین حال، اندروید همچنان باید بتواند نسخه رمزگذاری شده ای از کلیدها را روی دیسک ذخیره کند تا بتوان در وهله اول قفل آنها را باز کرد. کلیدهای خام برای این منظور کار می کنند. با این حال، مطلوب است که کلیدهای خام به هیچ وجه در حافظه سیستم وجود نداشته باشند تا هرگز نتوان آنها را برای استفاده خارج از دستگاه استخراج کرد، حتی اگر در زمان بوت استخراج شوند. به همین دلیل مفهوم بسته بندی طولانی مدت تعریف شده است.
برای پشتیبانی از مدیریت کلیدهای پیچیده شده به این دو روش مختلف، سخت افزار باید رابط های زیر را پیاده سازی کند:
- رابطهایی برای تولید و وارد کردن کلیدهای ذخیرهسازی، بازگرداندن آنها به شکل بستهبندی شده طولانیمدت. این واسط ها به طور غیر مستقیم از طریق KeyMint قابل دسترسی هستند و با برچسب
TAG_STORAGE_KEY
KeyMint مطابقت دارند. توانایی "تولید" توسطvold
برای تولید کلیدهای ذخیره سازی جدید برای استفاده توسط اندروید استفاده می شود، در حالی که توانایی "وارد کردن" توسطvts_kernel_encryption_test
برای وارد کردن کلیدهای آزمایشی استفاده می شود. - رابطی برای تبدیل یک کلید ذخیره سازی پیچیده شده طولانی مدت به یک کلید ذخیره سازی موقتی. این مربوط به روش
convertStorageKeyToEphemeral
KeyMint است. این روش هم توسطvold
و هم توسطvts_kernel_encryption_test
برای باز کردن قفل حافظه استفاده می شود.
الگوریتم بسته بندی کلید یک جزئیات پیاده سازی است، اما باید از یک AEAD قوی مانند AES-256-GCM با IV های تصادفی استفاده کند.
تغییرات نرم افزاری مورد نیاز است
AOSP در حال حاضر یک چارچوب اساسی برای پشتیبانی از کلیدهای سخت افزاری دارد. این شامل پشتیبانی در مولفههای فضای کاربری مانند vold
و همچنین پشتیبانی از هسته لینوکس در blk-crypto ، fscrypt و dm-default-key است.
با این حال، برخی تغییرات خاص پیاده سازی مورد نیاز است.
KeyMint تغییر می کند
پیاده سازی KeyMint دستگاه باید برای پشتیبانی از TAG_STORAGE_KEY
و پیاده سازی روش convertStorageKeyToEphemeral
اصلاح شود.
در Keymaster، exportKey
به جای convertStorageKeyToEphemeral
استفاده شد.
تغییرات هسته لینوکس
درایور هسته لینوکس برای موتور کریپتو درون خطی دستگاه باید برای پشتیبانی از کلیدهای سخت افزاری اصلاح شود.
برای کرنلهای android14
و بالاتر، 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
پشتیبانی از برنامهنویسی سخت افزاری، و implemented. keyslot_mgmt_ll_ops::derive_raw_secret
.
کلیدهای پیچیده شده را تست کنید
اگرچه تست رمزگذاری با کلیدهای پیچیده سختافزاری سختتر از رمزگذاری با کلیدهای خام است، اما هنوز هم میتوان با وارد کردن یک کلید تست و اجرای مجدد مشتق کلیدی که سختافزار انجام میدهد، آزمایش کرد. این در vts_kernel_encryption_test
پیاده سازی شده است. برای اجرای این تست، اجرا کنید:
atest -v vts_kernel_encryption_test
گزارش آزمایشی را بخوانید و بررسی کنید که موارد تست کلید پیچیدهشده با سختافزار (به عنوان مثال، FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
و DmDefaultKeyTest.TestHwWrappedKey
) به دلیل پشتیبانی از سختافزار رد نشدهاند.
به طور پیشفرض، vts_kernel_encryption_test
فرض میکند که سختافزار یک KDF را پیادهسازی میکند که آن را kdf1
مینامد. این KDF متعلق به خانواده حالت شمارنده KDFها از NIST SP 800-108 است و از AES-256-CMAC به عنوان تابع شبه تصادفی استفاده می کند. برای اطلاعات بیشتر در مورد CMAC، مشخصات CMAC را ببینید. KDF هنگام استخراج هر کلید فرعی از زمینه ها و برچسب های خاصی استفاده می کند. سختافزار باید این KDF را پیادهسازی کند، از جمله انتخاب دقیق زمینه، برچسب و قالببندی رشته ورودی ثابت هنگام استخراج هر کلید فرعی.
با این حال، vts_kernel_encryption_test
KDF های اضافی kdf2
تا kdf4
را نیز پیاده سازی می کند. اینها به همان اندازه kdf1
ایمن هستند و فقط در انتخاب زمینهها، برچسبها و قالببندی رشته ورودی ثابت تفاوت دارند. آنها فقط برای قرار دادن سخت افزارهای مختلف وجود دارند.
برای دستگاههایی که از KDF دیگری استفاده میکنند، ویژگی سیستم ro.crypto.hw_wrapped_keys.kdf
را در PRODUCT_VENDOR_PROPERTIES
روی نام KDF همانطور که در کد منبع آزمایشی تعریف شده است، تنظیم کنید. این باعث می شود vts_kernel_encryption_test
به جای kdf1
آن KDF را بررسی کند. به عنوان مثال، برای انتخاب kdf2
، از:
PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2
برای دستگاههایی که از KDF استفاده میکنند که تست آن را پشتیبانی نمیکند، پیادهسازی آن KDF را نیز به تست اضافه کنید و نامی منحصربهفرد به آن بدهید.
کلیدهای بسته بندی شده را فعال کنید
وقتی پشتیبانی کلید سختافزاری دستگاه به درستی کار میکند، تغییرات زیر را در فایل fstab
دستگاه اعمال کنید تا Android از آن برای رمزگذاری FBE و ابرداده استفاده کند:
- FBE: پرچم
wrappedkey_v0
را به پارامترfileencryption
اضافه کنید. برای مثال، ازfileencryption=::inlinecrypt_optimized+wrappedkey_v0
استفاده کنید. برای جزئیات بیشتر، به مستندات FBE مراجعه کنید. - رمزگذاری فراداده: پرچم
wrappedkey_v0
را به پارامترmetadata_encryption
اضافه کنید. برای مثال، ازmetadata_encryption=:wrappedkey_v0
استفاده کنید. برای جزئیات بیشتر، به مستندات رمزگذاری ابرداده مراجعه کنید.