کلیدهای سخت افزاری

مانند اکثر نرم‌افزارهای رمزگذاری دیسک و فایل، رمزگذاری حافظه اندروید به طور سنتی به کلیدهای رمزگذاری خام موجود در حافظه سیستم متکی است تا رمزگذاری بتواند انجام شود. حتی زمانی که رمزگذاری توسط سخت‌افزار اختصاصی به جای نرم‌افزار انجام می‌شود، نرم‌افزار عموماً همچنان نیاز به مدیریت کلیدهای رمزگذاری خام دارد.

این موضوع به طور سنتی به عنوان یک مشکل تلقی نمی‌شود زیرا کلیدها در طول حمله آفلاین وجود ندارند، که نوع اصلی حمله‌ای است که رمزگذاری ذخیره‌سازی برای محافظت در برابر آن در نظر گرفته شده است. با این حال، تمایل به ارائه محافظت بیشتر در برابر انواع دیگر حملات، مانند حملات بوت سرد و حملات آنلاین وجود دارد که در آن یک مهاجم ممکن است بتواند حافظه سیستم را بدون به خطر انداختن کامل دستگاه، نشت دهد.

برای حل این مشکل، اندروید ۱۱ پشتیبانی از کلیدهای سخت‌افزاری را معرفی کرد، که در آن پشتیبانی سخت‌افزاری وجود دارد. کلیدهای سخت‌افزاری، کلیدهای ذخیره‌سازی هستند که به صورت خام فقط برای سخت‌افزار اختصاصی شناخته می‌شوند؛ نرم‌افزار این کلیدها را فقط به صورت رمزگذاری شده (رمزگذاری شده) می‌بیند و با آنها کار می‌کند. این سخت‌افزار باید قادر به تولید و وارد کردن کلیدهای ذخیره‌سازی، قرار دادن کلیدهای ذخیره‌سازی در قالب‌های موقت و بلندمدت، استخراج زیرکلیدها، برنامه‌نویسی مستقیم یک زیرکلیدها در یک موتور رمزنگاری درون‌خطی و بازگرداندن یک زیرکلیدی جداگانه به نرم‌افزار باشد.

توجه: یک موتور رمزنگاری درون‌خطی (یا سخت‌افزار رمزگذاری درون‌خطی ) به سخت‌افزاری اشاره دارد که داده‌ها را در حین انتقال به/از دستگاه ذخیره‌سازی، رمزگذاری/رمزگشایی می‌کند. معمولاً این یک کنترل‌کننده میزبان UFS یا eMMC است که افزونه‌های رمزنگاری تعریف‌شده توسط مشخصات JEDEC مربوطه را پیاده‌سازی می‌کند.

طراحی

این بخش، طراحی ویژگی کلیدهای سخت‌افزاری پیچیده‌شده، از جمله پشتیبانی سخت‌افزاری مورد نیاز برای آن را ارائه می‌دهد. این بحث بر رمزگذاری مبتنی بر فایل (FBE) تمرکز دارد، اما این راه‌حل برای رمزگذاری فراداده نیز صدق می‌کند.

یک راه برای جلوگیری از نیاز به کلیدهای رمزگذاری خام در حافظه سیستم، نگه داشتن آنها فقط در اسلات‌های کلید یک موتور رمزنگاری درون‌خطی است. با این حال، این رویکرد با برخی مشکلات مواجه است:

  • تعداد کلیدهای رمزگذاری ممکن است از تعداد شیارهای کلید بیشتر باشد.
  • موتورهای رمزنگاری درون‌خطی معمولاً در صورت تنظیم مجدد کنترل‌کننده میزبان ذخیره‌سازی، محتویات اسلات‌های کلید خود را از دست می‌دهند. تنظیم مجدد کنترل‌کننده میزبان ذخیره‌سازی یک رویه استاندارد بازیابی خطا است که در صورت بروز انواع خاصی از خطاهای ذخیره‌سازی اجرا می‌شود و چنین خطاهایی می‌توانند در هر زمانی رخ دهند. بنابراین، هنگامی که از رمزنگاری درون‌خطی استفاده می‌شود، سیستم عامل باید همیشه آماده برنامه‌ریزی مجدد اسلات‌های کلید بدون دخالت کاربر باشد.
  • موتورهای رمزنگاری درون‌خطی فقط می‌توانند برای رمزگذاری/رمزگشایی کل بلوک‌های داده روی دیسک استفاده شوند. با این حال، در مورد FBE، نرم‌افزار هنوز باید بتواند کارهای رمزنگاری دیگری مانند رمزگذاری نام فایل‌ها و استخراج شناسه‌های کلید را انجام دهد. نرم‌افزار همچنان برای انجام این کار دیگر به کلیدهای خام FBE نیاز دارد.

برای جلوگیری از این مشکلات، کلیدهای ذخیره‌سازی به کلیدهای سخت‌افزاری پیچیده‌شده تبدیل می‌شوند که می‌توانند باز شده و فقط توسط سخت‌افزار اختصاصی مورد استفاده قرار گیرند. این امر امکان پشتیبانی از تعداد نامحدودی از کلیدها را فراهم می‌کند. علاوه بر این، سلسله مراتب کلید اصلاح شده و تا حدی به این سخت‌افزار منتقل می‌شود که به یک زیرکلید اجازه می‌دهد برای کارهایی که نمی‌توانند از موتور رمزنگاری درون‌خطی استفاده کنند، به نرم‌افزار بازگردانده شود.

سلسله مراتب کلید

کلیدها را می‌توان با استفاده از یک تابع مشتق‌گیری کلید (KDF) مانند HKDF از کلیدهای دیگر استخراج کرد و در نتیجه یک سلسله مراتب کلید ایجاد کرد.

نمودار زیر یک سلسله مراتب کلید معمولی برای FBE را در زمانی که از کلیدهای سخت‌افزاری استفاده نمی‌شود ، نشان می‌دهد:

سلسله مراتب کلید FBE (استاندارد)
شکل ۱. سلسله مراتب کلید FBE (استاندارد)

کلید کلاس FBE، کلید رمزگذاری خامی است که اندروید برای باز کردن قفل مجموعه‌ای خاص از دایرکتوری‌های رمزگذاری شده، مانند فضای ذخیره‌سازی رمزگذاری شده با اعتبارنامه برای یک کاربر خاص اندروید، به هسته لینوکس منتقل می‌کند. (در هسته، این کلید، کلید اصلی fscrypt نامیده می‌شود.) هسته از این کلید، زیرکلیدهای زیر را استخراج می‌کند:

  • شناسه کلید. این برای رمزگذاری استفاده نمی‌شود، بلکه مقداری است که برای شناسایی کلیدی که یک فایل یا دایرکتوری خاص با آن محافظت می‌شود، استفاده می‌شود.
  • کلید رمزگذاری محتویات فایل
  • کلید رمزگذاری نام فایل‌ها

در مقابل، نمودار زیر سلسله مراتب کلید برای FBE را هنگام استفاده از کلیدهای سخت‌افزاری نشان می‌دهد:

سلسله مراتب کلید FBE (با کلید پیچیده شده در سخت‌افزار)
شکل ۲. سلسله مراتب کلید FBE (با کلید سخت‌افزاری)

در مقایسه با مورد قبلی، یک سطح اضافی به سلسله مراتب کلید اضافه شده است و کلید رمزگذاری محتوای فایل جابجا شده است. گره ریشه هنوز نشان دهنده کلیدی است که اندروید برای باز کردن مجموعه‌ای از دایرکتوری‌های رمزگذاری شده به لینوکس می‌دهد. با این حال، اکنون آن کلید به شکل ephemerally-wrapped است و برای استفاده باید به سخت‌افزار اختصاصی منتقل شود. این سخت‌افزار باید دو رابط را پیاده‌سازی کند که یک کلید ephemerally-wrapped را دریافت می‌کنند:

  • یک رابط برای استخراج inline_encryption_key و برنامه‌ریزی مستقیم آن در یک keyslot از موتور رمزنگاری درون‌خطی. این امر امکان رمزگذاری/رمزگشایی محتویات فایل را بدون دسترسی نرم‌افزار به کلید خام فراهم می‌کند. در هسته‌های رایج اندروید، این رابط مربوط به عملیات blk_crypto_ll_ops::keyslot_program است که باید توسط درایور ذخیره‌سازی پیاده‌سازی شود.
  • یک رابط برای استخراج و بازگرداندن sw_secret ("رمز نرم‌افزار" -- که در بعضی جاها "راز خام" نیز نامیده می‌شود)، که کلیدی است که لینوکس برای استخراج زیرکلیدها برای هر چیزی غیر از رمزگذاری محتوای فایل استفاده می‌کند. در هسته‌های رایج اندروید، این رابط مربوط به عملیات blk_crypto_ll_ops::derive_sw_secret است که باید توسط درایور ذخیره‌سازی پیاده‌سازی شود.

برای استخراج inline_encryption_key و sw_secret از کلید ذخیره‌سازی خام، سخت‌افزار باید از یک KDF قوی از نظر رمزنگاری استفاده کند. این KDF باید از بهترین شیوه‌های رمزنگاری پیروی کند؛ باید قدرت امنیتی حداقل ۲۵۶ بیت داشته باشد، یعنی برای هر الگوریتمی که بعداً استفاده می‌شود، کافی باشد. همچنین باید هنگام استخراج هر نوع زیرکلید از یک برچسب و زمینه متمایز استفاده کند تا تضمین شود که زیرکلیدهای حاصل از نظر رمزنگاری ایزوله هستند، یعنی دانستن یکی از آنها، زیرکلید دیگری را فاش نمی‌کند. بسط کلید لازم نیست، زیرا کلید ذخیره‌سازی خام از قبل یک کلید تصادفی یکنواخت است.

از نظر فنی، هر KDF که الزامات امنیتی را برآورده کند، می‌تواند مورد استفاده قرار گیرد. با این حال، برای اهداف آزمایش، vts_kernel_encryption_test همان KDF را در نرم‌افزار پیاده‌سازی می‌کند تا متن رمز روی دیسک را بازتولید کرده و صحت آن را تأیید کند. برای سهولت آزمایش و اطمینان از استفاده از KDF امن و از قبل بررسی شده، توصیه می‌کنیم سخت‌افزار، KDF پیش‌فرضی را که آزمایش بررسی می‌کند، پیاده‌سازی کند. برای سخت‌افزاری که از KDF متفاوتی استفاده می‌کند، برای نحوه پیکربندی آزمایش بر اساس آن، به Test Wrapped Keys مراجعه کنید.

بسته بندی کلید

برای دستیابی به اهداف امنیتی کلیدهای سخت‌افزاری، دو نوع پوشش کلید تعریف شده است:

  • بسته‌بندی زودگذر : سخت‌افزار، کلید خام را با استفاده از کلیدی که به صورت تصادفی در هر بوت تولید می‌شود و مستقیماً در خارج از سخت‌افزار در معرض دید نیست، رمزگذاری می‌کند.
  • بسته‌بندی بلندمدت : سخت‌افزار، کلید خام را با استفاده از یک کلید منحصر به فرد و پایدار که درون سخت‌افزار تعبیه شده و مستقیماً در خارج از سخت‌افزار در معرض دید نیست، رمزگذاری می‌کند.

تمام کلیدهایی که برای باز کردن قفل حافظه به هسته لینوکس ارسال می‌شوند، به صورت موقت رمزگذاری می‌شوند. این امر تضمین می‌کند که اگر مهاجمی بتواند یک کلید در حال استفاده را از حافظه سیستم استخراج کند، آن کلید نه تنها در خارج از دستگاه، بلکه پس از راه‌اندازی مجدد نیز در دستگاه غیرقابل استفاده خواهد بود.

در عین حال، اندروید همچنان باید بتواند یک نسخه رمزگذاری شده از کلیدها را روی دیسک ذخیره کند تا بتوان آنها را در وهله اول قفل‌گشایی کرد. کلیدهای خام برای این منظور مناسب هستند. با این حال، مطلوب است که هرگز کلیدهای خام در حافظه سیستم وجود نداشته باشند تا هرگز نتوان آنها را برای استفاده در خارج از دستگاه استخراج کرد، حتی اگر در زمان بوت استخراج شوند. به همین دلیل، مفهوم بسته‌بندی طولانی مدت تعریف شده است.

برای پشتیبانی از مدیریت کلیدهای پیچیده شده به این دو روش مختلف، سخت‌افزار باید رابط‌های زیر را پیاده‌سازی کند:

  • رابط‌هایی برای تولید و وارد کردن کلیدهای ذخیره‌سازی، و بازگرداندن آنها به شکل بسته‌بندی‌شده‌ی بلندمدت. این رابط‌ها به طور غیرمستقیم از طریق KeyMint قابل دسترسی هستند و با برچسب TAG_STORAGE_KEY KeyMint مطابقت دارند. قابلیت "generate" توسط vold برای تولید کلیدهای ذخیره‌سازی جدید برای استفاده توسط اندروید استفاده می‌شود، در حالی که قابلیت "import" توسط 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 دستگاه باید اصلاح شود تا از TAG_STORAGE_KEY پشتیبانی کند و متد convertStorageKeyToEphemeral را پیاده‌سازی کند.

در Keymaster، به جای convertStorageKeyToEphemeral از exportKey استفاده شده است.

تغییرات هسته لینوکس

درایور هسته لینوکس برای موتور رمزنگاری درون‌خطی دستگاه باید اصلاح شود تا از کلیدهای سخت‌افزاری پشتیبانی کند.

برای کرنل‌های 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 از برنامه‌نویسی/حذف کلیدهای سخت‌افزاری پشتیبانی کنند و 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 دستگاه ایجاد کنید تا اندروید از آن برای رمزگذاری FBE و فراداده استفاده کند:

  • FBE: پرچم wrappedkey_v0 را به پارامتر fileencryption اضافه کنید. برای مثال، از fileencryption=::inlinecrypt_optimized+wrappedkey_v0 استفاده کنید. برای جزئیات بیشتر، به مستندات FBE مراجعه کنید.
  • رمزگذاری فراداده: پرچم wrappedkey_v0 را به پارامتر metadata_encryption اضافه کنید. برای مثال، از metadata_encryption=:wrappedkey_v0 استفاده کنید. برای جزئیات بیشتر، به مستندات رمزگذاری فراداده مراجعه کنید.