Kunci yang digabungkan dengan hardware

Seperti sebagian besar software enkripsi disk dan file, enkripsi penyimpanan Android secara tradisional mengandalkan kunci enkripsi mentah yang ada dalam memori sistem sehingga enkripsi dapat dilakukan. Meskipun enkripsi dilakukan oleh hardware khusus, bukan oleh software, software umumnya masih perlu mengelola kunci enkripsi mentah.

Hal ini biasanya tidak dianggap sebagai masalah karena kunci tidak ada selama serangan offline, yang merupakan jenis serangan utama yang ingin dilindungi oleh enkripsi penyimpanan. Namun, ada keinginan untuk memberikan perlindungan yang lebih baik terhadap jenis serangan lain, seperti serangan cold boot attacks, dan serangan online yang mungkin dapat membocorkan memori sistem tanpa sepenuhnya membahayakan perangkat.

Untuk mengatasi masalah ini, Android 11 memperkenalkan dukungan untuk kunci yang digabungkan hardware, jika dukungan hardware tersedia. Kunci yang digabungkan hardware adalah kunci penyimpanan yang hanya diketahui dalam bentuk mentah oleh hardware khusus; software hanya melihat dan menggunakan kunci ini dalam bentuk yang digabungkan (dienkripsi). Hardware ini harus dapat membuat dan mengimpor kunci penyimpanan, menggabungkan kunci penyimpanan dalam bentuk sementara dan jangka panjang, memperoleh subkunci, memprogram satu subkunci secara langsung ke dalam mesin kripto inline, dan menampilkan subkunci terpisah ke software.

Catatan: Mesin kripto inline (atau hardware enkripsi inline) mengacu pada hardware yang mengenkripsi/mendekripsi data saat data tersebut dalam perjalanan ke/dari perangkat penyimpanan. Biasanya, ini adalah pengontrol host UFS atau eMMC yang menerapkan ekstensi kripto yang ditentukan oleh spesifikasi JEDEC yang sesuai.

Desain

Bagian ini menyajikan desain fitur kunci yang digabungkan hardware, termasuk dukungan hardware yang diperlukan untuk fitur tersebut. Diskusi ini berfokus pada enkripsi berbasis file (FBE), tetapi solusi ini juga berlaku untuk enkripsi metadata.

Salah satu cara untuk menghindari kebutuhan kunci enkripsi mentah dalam memori sistem adalah dengan menyimpannya hanya di keyslot mesin kripto inline. Namun, pendekatan ini menimbulkan beberapa masalah:

  • Jumlah kunci enkripsi mungkin melebihi jumlah keyslot.
  • Mesin kripto inline biasanya kehilangan konten keyslotnya jika pengontrol host penyimpanan direset. Reset pengontrol host penyimpanan adalah a prosedur pemulihan error standar yang dijalankan jika jenis error penyimpanan tertentu terjadi, dan error tersebut dapat terjadi kapan saja. Oleh karena itu, saat kripto inline digunakan, sistem operasi harus selalu siap untuk memprogram ulang keyslot tanpa intervensi pengguna.
  • Mesin kripto inline hanya dapat digunakan untuk mengenkripsi/mendekripsi blok data lengkap di disk. Namun, dalam kasus FBE, software masih perlu dapat melakukan pekerjaan kriptografi lainnya seperti enkripsi nama file dan memperoleh ID kunci. Software masih memerlukan akses ke kunci FBE mentah untuk melakukan pekerjaan lainnya ini.

Untuk menghindari masalah ini, kunci penyimpanan dibuat menjadi kunci yang digabungkan hardware, yang hanya dapat dibuka lapisannya dan digunakan oleh hardware khusus. Hal ini memungkinkan jumlah kunci yang tidak terbatas untuk didukung. Selain itu, hierarki kunci diubah dan sebagian dipindahkan ke hardware ini, yang memungkinkan subkunci ditampilkan ke software untuk tugas yang tidak dapat menggunakan mesin kripto inline.

Hierarki kunci

Kunci dapat diperoleh dari kunci lain menggunakan fungsi perolehan kunci (KDF) seperti HKDF, yang menghasilkan hierarki kunci.

Diagram berikut menggambarkan hierarki kunci umum untuk FBE saat kunci yang digabungkan hardware tidak digunakan:

Hierarki kunci FBE (standar)
Gambar 1. Hierarki kunci FBE (standar)

Kunci class FBE adalah kunci enkripsi mentah yang diteruskan Android ke kernel Linux untuk membuka sekumpulan direktori terenkripsi tertentu, seperti penyimpanan terenkripsi kredensial untuk pengguna Android tertentu. (Di kernel, kunci ini disebut kunci utama fscrypt.) Dari kunci ini, kernel memperoleh subkunci berikut:

  • ID kunci. ID ini tidak digunakan untuk enkripsi, tetapi merupakan nilai yang digunakan untuk mengidentifikasi kunci yang melindungi file atau direktori tertentu.
  • Kunci enkripsi konten file
  • Kunci enkripsi nama file

Sebaliknya, diagram berikut menggambarkan hierarki kunci untuk FBE saat kunci yang digabungkan hardware digunakan:

Hierarki kunci FBE (dengan kunci yang di-wrap hardware)
Gambar 2. Hierarki kunci FBE (dengan kunci yang digabungkan hardware)

Dibandingkan dengan kasus sebelumnya, tingkat tambahan telah ditambahkan ke hierarki kunci, dan kunci enkripsi konten file telah dipindahkan. Node root masih mewakili kunci yang diteruskan Android ke Linux untuk membuka sekumpulan direktori terenkripsi. Namun, kunci tersebut kini dalam bentuk yang digabungkan sementara, dan agar dapat digunakan, kunci tersebut harus diteruskan ke hardware khusus. Hardware ini harus menerapkan dua antarmuka yang menggunakan kunci yang digabungkan sementara:

  • Satu antarmuka untuk memperoleh inline_encryption_key dan memprogramnya secara langsung ke keyslot mesin kripto inline. Hal ini memungkinkan konten file dienkripsi/didekripsi tanpa software memiliki akses ke kunci mentah. Di kernel umum Android, antarmuka ini sesuai dengan operasi blk_crypto_ll_ops::keyslot_program, yang harus di terapkan oleh driver penyimpanan.
  • Satu antarmuka untuk memperoleh dan menampilkan sw_secret ("secret software" -- sebelumnya disebut "secret mentah"), yang merupakan kunci yang digunakan Linux untuk memperoleh subkunci untuk semua hal selain enkripsi konten file. Di kernel umum Android, antarmuka ini sesuai dengan operasi blk_crypto_ll_ops::derive_sw_secret, yang harus di terapkan oleh driver penyimpanan.

Untuk memperoleh inline_encryption_key dan sw_secret dari kunci penyimpanan mentah, hardware harus menggunakan KDF yang kuat secara kriptografi. KDF ini harus mengikuti praktik terbaik kriptografi; KDF ini harus memiliki kekuatan keamanan minimal 256 bit, yaitu cukup untuk algoritma apa pun yang digunakan nanti. KDF ini juga harus menggunakan label dan konteks yang berbeda saat memperoleh setiap jenis subkunci untuk menjamin bahwa subkunci yang dihasilkan terisolasi secara kriptografi, yaitu pengetahuan tentang salah satunya tidak mengungkapkan subkunci lainnya. Peregangan kunci tidak diperlukan, karena kunci penyimpanan mentah sudah merupakan kunci acak yang seragam.

Secara teknis, KDF apa pun yang memenuhi persyaratan keamanan dapat digunakan. Namun, untuk tujuan pengujian, vts_kernel_encryption_test menerapkan KDF yang sama dalam software untuk mereproduksi ciphertext di disk dan memverifikasi bahwa ciphertext tersebut benar. Untuk memudahkan pengujian dan memastikan bahwa KDF yang aman dan telah ditinjau digunakan, sebaiknya hardware menerapkan KDF default yang diperiksa oleh pengujian. Untuk hardware yang menggunakan KDF yang berbeda, lihat Menguji kunci yang digabungkan untuk mengetahui cara mengonfigurasi pengujian yang sesuai.

Penggabungan kunci

Untuk memenuhi sasaran keamanan kunci yang digabungkan hardware, dua jenis penggabungan kunci ditentukan:

  • Penggabungan sementara: hardware mengenkripsi kunci mentah menggunakan kunci yang dibuat secara acak pada setiap booting dan tidak diekspos secara langsung di luar hardware.
  • Penggabungan jangka panjang: hardware mengenkripsi kunci mentah menggunakan kunci unik dan persisten yang terintegrasi ke dalam hardware yang tidak diekspos secara langsung di luar hardware.

Semua kunci yang diteruskan ke kernel Linux untuk membuka penyimpanan digabungkan sementara. Hal ini memastikan bahwa jika penyerang dapat mengekstrak kunci yang sedang digunakan dari memori sistem, kunci tersebut tidak dapat digunakan tidak hanya di luar perangkat, tetapi juga di perangkat setelah di-reboot.

Pada saat yang sama, Android masih perlu dapat menyimpan versi kunci terenkripsi di disk sehingga kunci tersebut dapat dibuka lapisannya. Kunci mentah akan berfungsi untuk tujuan ini. Namun, sebaiknya kunci mentah tidak pernah ada dalam memori sistem sehingga kunci tersebut tidak pernah dapat diekstrak untuk digunakan di luar perangkat, meskipun diekstrak pada waktu booting. Oleh karena itu, konsep penggabungan jangka panjang ditentukan.

Untuk mendukung pengelolaan kunci yang digabungkan dengan dua cara yang berbeda ini, hardware harus menerapkan antarmuka berikut:

  • Antarmuka untuk membuat dan mengimpor kunci penyimpanan, yang menampilkannya dalam bentuk yang digabungkan jangka panjang. Antarmuka generate digunakan oleh vold untuk membuat kunci penyimpanan baru yang akan digunakan oleh Android. Antarmuka import digunakan oleh vts_kernel_encryption_test untuk mengimpor kunci pengujian.
  • Antarmuka untuk mengonversi kunci penyimpanan yang digabungkan jangka panjang menjadi kunci penyimpanan yang digabungkan sementara. Antarmuka ini digunakan oleh vold dan vts_kernel_encryption_test untuk membuka penyimpanan.

Algoritma penggabungan kunci adalah detail implementasi, tetapi harus menggunakan AEAD yang kuat seperti AES-256-GCM dengan IV acak.

Perubahan software yang diperlukan

AOSP sudah memiliki framework dasar untuk mendukung kunci yang digabungkan hardware. Hal ini mencakup dukungan dalam komponen ruang pengguna seperti vold, serta dukungan kernel Linux di blk-crypto, fscrypt dan dm-default-key.

Perubahan kernel Linux

Driver kernel Linux untuk pengontrol penyimpanan perangkat dengan dukungan enkripsi inline harus diubah untuk mendukung kunci yang digabungkan hardware.

Untuk kernel android17 dan yang lebih tinggi:

  • Tetapkan BLK_CRYPTO_KEY_TYPE_HW_WRAPPED di blk_crypto_profile::key_types_supported.
  • Buat blk_crypto_ll_ops::keyslot_program mendukung pemrograman kunci yang digabungkan hardware.
  • Buat blk_crypto_ll_ops::keyslot_evict mendukung evicting kunci yang digabungkan hardware.
  • Terapkan blk_crypto_ll_ops::derive_sw_secret, blk_crypto_ll_ops::import_key, blk_crypto_ll_ops::generate_key, dan blk_crypto_ll_ops::prepare_key.

Untuk kernel android14, android15, dan android16:

  • Tetapkan BLK_CRYPTO_KEY_TYPE_HW_WRAPPED di blk_crypto_profile::key_types_supported.
  • Buat blk_crypto_ll_ops::keyslot_program mendukung pemrograman kunci yang digabungkan hardware.
  • Buat blk_crypto_ll_ops::keyslot_evict mendukung evicting kunci yang digabungkan hardware.
  • Terapkan blk_crypto_ll_ops::derive_sw_secret.

Untuk kernel android12 dan android13:

  • Tetapkan BLK_CRYPTO_FEATURE_WRAPPED_KEYS di blk_keyslot_manager::features.
  • Buat blk_ksm_ll_ops::keyslot_program mendukung pemrograman kunci yang digabungkan hardware.
  • Buat blk_ksm_ll_ops::keyslot_evict mendukung kunci yang digabungkan hardware evicting.
  • Terapkan blk_ksm_ll_ops::derive_raw_secret.

Untuk kernel android11:

  • Tetapkan BLK_CRYPTO_FEATURE_WRAPPED_KEYS di keyslot_manager::features.
  • Buat keyslot_mgmt_ll_ops::keyslot_program mendukung pemrograman kunci yang digabungkan hardware.
  • Buat keyslot_mgmt_ll_ops::keyslot_evict mendukung evicting kunci yang digabungkan hardware.
  • Terapkan keyslot_mgmt_ll_ops::derive_raw_secret.

Perubahan KeyMint (lama)

Dalam versi kunci yang digabungkan hardware saat ini (wrappedkey), pembuatan, impor, dan persiapan kunci yang digabungkan hardware menggunakan ioctl kernel Linux BLKCRYPTOGENERATEKEY, BLKCRYPTOIMPORTKEY, dan BLKCRYPTOPREPAREKEY. Ioctl ini sesuai dengan metode di struct blk_crypto_ll_ops. Driver penyimpanan menerapkan metode ini dan berkomunikasi dengan hardware penggabungan kunci untuk melakukan operasi yang diminta. Untuk mengetahui informasi selengkapnya tentang ioctl ini, lihat dokumentasi kernel Linux..

Ioctl ini ditambahkan di Linux 6.16. Pada perangkat yang tidak diluncurkan dengan solusi berbasis ioctl, solusi lain yang menggunakan Android KeyMint (atau sebelumnya KeyMaster) digunakan. Solusi lama (wrappedkey_v0) tidak kompatibel dengan kernel Linux utama atau dengan solusi saat ini. Solusi lama menggunakan fungsi KeyMint berikut:

  • Dukungan untuk TAG_STORAGE_KEY, baik untuk pembuatan maupun impor kunci.
  • Dukungan untuk metode convertStorageKeyToEphemeral.

Fungsi KeyMint ini hanya diperlukan di perangkat yang menggunakan solusi lama, yang sesuai dengan wrappedkey_v0 dalam file fstab.

Perangkat yang menggunakan solusi saat ini, yang sesuai dengan wrappedkey dalam file fstab, tidak perlu menerapkan fungsi KeyMint ini.

Menguji kunci yang digabungkan

Meskipun enkripsi dengan kunci yang digabungkan hardware lebih sulit diuji daripada enkripsi dengan kunci mentah, pengujian masih dapat dilakukan dengan mengimpor kunci pengujian dan menerapkan kembali perolehan kunci yang dilakukan hardware. Hal ini diterapkan di vts_kernel_encryption_test. Untuk menjalankan pengujian ini, jalankan:

atest -v vts_kernel_encryption_test

Baca log pengujian dan pastikan kasus pengujian kunci yang digabungkan hardware (misalnya, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy dan DmDefaultKeyTest.TestHwWrappedKey) tidak dilewati karena dukungan untuk kunci yang digabungkan hardware tidak terdeteksi, karena hasil pengujian masih "passed" dalam kasus tersebut.

Secara default, vts_kernel_encryption_test mengasumsikan hardware menerapkan KDF yang disebut kdf1. KDF ini termasuk dalam keluarga mode penghitung KDF dari NIST SP 800-108, dan menggunakan AES-256-CMAC sebagai fungsi pseudorandom. Untuk mengetahui informasi selengkapnya tentang CMAC, lihat spesifikasi CMAC. KDF menggunakan konteks dan label tertentu saat memperoleh setiap subkunci. Hardware harus menerapkan KDF ini, termasuk pilihan konteks, label, dan pemformatan string input tetap yang tepat saat memperoleh setiap subkunci.

Namun, vts_kernel_encryption_test juga menerapkan KDF tambahan kdf2 hingga kdf4. KDF ini sama amannya dengan kdf1 dan hanya berbeda dalam pilihan konteks, label, dan pemformatan string input tetap. KDF ini hanya ada untuk mengakomodasi hardware yang berbeda.

Untuk perangkat yang menggunakan KDF yang berbeda, tetapkan properti sistem ro.crypto.hw_wrapped_keys.kdf di PRODUCT_VENDOR_PROPERTIES ke nama KDF seperti yang ditentukan dalam kode sumber pengujian. Hal ini menyebabkan vts_kernel_encryption_test memeriksa KDF tersebut, bukan kdf1. Misalnya, untuk memilih kdf2, gunakan:

PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2

Untuk perangkat yang menggunakan KDF yang tidak didukung pengujian, tambahkan juga implementasi KDF tersebut ke pengujian dan beri nama yang unik.

Mengaktifkan kunci yang digabungkan

Jika dukungan kunci yang digabungkan hardware perangkat berfungsi dengan benar, lakukan perubahan berikut pada file fstab perangkat agar Android menggunakannya untuk FBE dan enkripsi metadata:

  • FBE: tambahkan tanda wrappedkey (atau wrappedkey_v0 untuk versi lama) ke parameter fileencryption. Misalnya, gunakan fileencryption=::inlinecrypt_optimized+wrappedkey. Untuk mengetahui detail selengkapnya, lihat dokumentasi FBE.
  • Enkripsi metadata: tambahkan tanda wrappedkey (atau wrappedkey_v0 untuk versi lama) ke metadata_encryption parameter. Misalnya, gunakan metadata_encryption=:wrappedkey. Untuk mengetahui detail selengkapnya, lihat dokumentasi enkripsi metadata.

Dalam setiap kasus, ada dua versi tanda:

  • wrappedkey, yang didukung oleh Android 17 dan yang lebih tinggi, mengaktifkan versi kunci yang digabungkan hardware saat ini. Versi ini kompatibel dengan kernel Linux utama.
  • wrappedkey_v0, yang didukung oleh Android 11 dan yang lebih tinggi, mengaktifkan versi kunci yang digabungkan hardware lama. Versi ini tidak kompatibel dengan kernel Linux utama. Versi ini memproxy operasi tertentu melalui KeyMint dan menggunakan format di disk yang tidak standar. Untuk mengetahui informasi selengkapnya, lihat Perubahan KeyMint (lama).

Pada perangkat yang diluncurkan dengan Android 17 atau yang lebih tinggi, sebaiknya gunakan wrappedkey.

Pada perangkat yang sudah diluncurkan dengan wrappedkey_v0, terus gunakan wrappedkey_v0 untuk kompatibilitas mundur.