Keystore yang didukung perangkat keras

Ketersediaan lingkungan eksekusi tepercaya dalam system on a chip (SoC) menawarkan peluang bagi perangkat Android untuk menyediakan layanan keamanan kuat yang didukung perangkat keras ke OS Android, layanan platform, dan bahkan aplikasi pihak ketiga. Pengembang yang mencari ekstensi khusus Android harus mengunjungi android.security.keystore .

Sebelum Android 6.0, Android sudah memiliki API layanan kripto sederhana yang didukung perangkat keras, yang disediakan oleh Keymaster Hardware abstraction Layer (HAL) versi 0.2 dan 0.3. Keystore menyediakan operasi penandatanganan dan verifikasi digital, ditambah pembuatan dan impor pasangan kunci penandatanganan asimetris. Hal ini sudah diterapkan di banyak perangkat, namun ada banyak tujuan keamanan yang tidak dapat dicapai dengan mudah hanya dengan API tanda tangan. Keystore di Android 6.0 memperluas Keystore API untuk menyediakan kemampuan yang lebih luas.

Di Android 6.0, Keystore menambahkan primitif kriptografi simetris , AES dan HMAC, serta sistem kontrol akses untuk kunci yang didukung perangkat keras. Kontrol akses ditentukan selama pembuatan kunci dan diterapkan selama masa pakai kunci. Kunci dapat dibatasi untuk dapat digunakan hanya setelah pengguna diautentikasi, dan hanya untuk tujuan tertentu atau dengan parameter kriptografi tertentu. Untuk informasi lebih lanjut, lihat halaman Tag dan Fungsi Otorisasi .

Selain memperluas jangkauan primitif kriptografi, Keystore di Android 6.0 menambahkan hal berikut:

  • Skema kontrol penggunaan untuk memungkinkan penggunaan kunci dibatasi, untuk memitigasi risiko gangguan keamanan akibat penyalahgunaan kunci
  • Skema kontrol akses untuk mengaktifkan pembatasan kunci untuk pengguna tertentu, klien, dan rentang waktu yang ditentukan

Di Android 7.0, Keymaster 2 menambahkan dukungan untuk pengesahan kunci dan pengikatan versi. Pengesahan kunci memberikan sertifikat kunci publik yang berisi penjelasan rinci tentang kunci dan kontrol aksesnya, agar keberadaan kunci dalam perangkat keras aman dan konfigurasinya dapat diverifikasi dari jarak jauh.

Pengikatan versi mengikat kunci ke sistem operasi dan versi tingkat patch. Hal ini memastikan bahwa penyerang yang menemukan kelemahan pada sistem versi lama atau perangkat lunak TEE tidak dapat mengembalikan perangkat ke versi yang rentan dan menggunakan kunci yang dibuat dengan versi yang lebih baru. Selain itu, ketika kunci dengan versi dan tingkat patch tertentu digunakan pada perangkat yang telah ditingkatkan ke versi atau tingkat patch yang lebih baru, kunci tersebut akan ditingkatkan sebelum dapat digunakan, dan versi kunci sebelumnya menjadi tidak valid. Saat perangkat ditingkatkan, tombol "ratchet" maju bersama dengan perangkat, tetapi setiap pengembalian perangkat ke rilis sebelumnya menyebabkan kunci tidak dapat digunakan.

Di Android 8.0, Keymaster 3 bertransisi dari Lapisan Abstraksi Perangkat Keras (HAL) struktur C gaya lama ke antarmuka C++ HAL yang dihasilkan dari definisi dalam Bahasa Definisi Antarmuka Perangkat Keras (HIDL) yang baru. Sebagai bagian dari perubahan, banyak tipe argumen berubah, meskipun tipe dan metode memiliki korespondensi satu-ke-satu dengan tipe lama dan metode struct HAL. Lihat halaman Fungsi untuk lebih jelasnya.

Selain revisi antarmuka ini, Android 8.0 memperluas fitur pengesahan Keymaster 2 untuk mendukung pengesahan ID . Pengesahan ID menyediakan mekanisme terbatas dan opsional untuk membuktikan secara kuat pengidentifikasi perangkat keras, seperti nomor seri perangkat, nama produk, dan ID telepon (IMEI/MEID). Untuk mengimplementasikan penambahan ini, Android 8.0 mengubah skema pengesahan ASN.1 untuk menambahkan pengesahan ID. Implementasi Keymaster perlu menemukan cara aman untuk mengambil item data yang relevan, serta menentukan mekanisme untuk menonaktifkan fitur tersebut secara aman dan permanen.

Di Android 9, pembaruan mencakup:

  • Pembaruan ke Keymaster 4
  • Dukungan untuk Elemen Aman yang tertanam
  • Dukungan untuk impor kunci yang aman
  • Dukungan untuk enkripsi 3DES
  • Perubahan pada pengikatan versi sehingga boot.img dan system.img memiliki versi yang ditetapkan secara terpisah untuk memungkinkan pembaruan independen

Glosarium

Berikut ini ikhtisar singkat komponen Keystore dan hubungannya.

AndroidKeystore adalah Android Framework API dan komponen yang digunakan oleh aplikasi untuk mengakses fungsionalitas Keystore. Ini diimplementasikan sebagai perpanjangan dari API Arsitektur Kriptografi Java standar, dan terdiri dari kode Java yang berjalan di ruang proses aplikasi itu sendiri. AndroidKeystore memenuhi permintaan aplikasi untuk perilaku Keystore dengan meneruskannya ke daemon keystore.

Daemon keystore adalah daemon sistem Android yang menyediakan akses ke semua fungsi Keystore melalui Binder API . Ini bertanggung jawab untuk menyimpan "blob kunci", yang berisi materi kunci rahasia sebenarnya, dienkripsi sehingga Keystore dapat menyimpannya tetapi tidak menggunakan atau mengungkapkannya.

keymasterd adalah server HIDL yang menyediakan akses ke Keymaster TA. (Nama ini tidak distandarisasi dan hanya untuk tujuan konseptual.)

Keymaster TA (aplikasi tepercaya) adalah perangkat lunak yang berjalan dalam konteks aman, paling sering di TrustZone pada SoC ARM, yang menyediakan semua operasi Keystore yang aman, memiliki akses ke material kunci mentah, memvalidasi semua kondisi kontrol akses pada kunci , dll.

LockSettingsService adalah komponen sistem Android yang bertanggung jawab untuk otentikasi pengguna, baik kata sandi maupun sidik jari. Ini bukan bagian dari Keystore, namun relevan karena banyak operasi kunci Keystore memerlukan autentikasi pengguna. LockSettingsService berinteraksi dengan Gatekeeper TA dan Fingerprint TA untuk mendapatkan token autentikasi, yang diberikannya ke daemon keystore, dan yang pada akhirnya digunakan oleh aplikasi Keymaster TA.

Gatekeeper TA (aplikasi tepercaya) adalah komponen lain yang berjalan dalam konteks aman, yang bertanggung jawab untuk mengautentikasi kata sandi pengguna dan menghasilkan token autentikasi yang digunakan untuk membuktikan kepada Keymaster TA bahwa autentikasi telah dilakukan untuk pengguna tertentu pada titik waktu tertentu.

TA Sidik Jari (aplikasi tepercaya) adalah komponen lain yang berjalan dalam konteks aman yang bertanggung jawab untuk mengautentikasi sidik jari pengguna dan menghasilkan token autentikasi yang digunakan untuk membuktikan kepada Keymaster TA bahwa autentikasi telah dilakukan untuk pengguna tertentu pada titik waktu tertentu.

Arsitektur

Android Keystore API dan Keymaster HAL yang mendasarinya menyediakan serangkaian primitif kriptografi yang dasar namun memadai untuk memungkinkan penerapan protokol menggunakan kunci yang dikontrol akses dan didukung perangkat keras.

Keymaster HAL adalah pustaka yang disediakan OEM dan dapat dimuat secara dinamis yang digunakan oleh layanan Keystore untuk menyediakan layanan kriptografi yang didukung perangkat keras. Untuk menjaga keamanan, implementasi HAL tidak melakukan operasi sensitif apa pun di ruang pengguna, atau bahkan di ruang kernel. Operasi sensitif didelegasikan ke prosesor aman yang dicapai melalui beberapa antarmuka kernel. Arsitektur yang dihasilkan terlihat seperti ini:

Akses ke Keymaster

Gambar 1. Akses ke Keymaster

Dalam perangkat Android, "klien" Keymaster HAL terdiri dari beberapa lapisan (misalnya aplikasi, kerangka kerja, daemon Keystore), namun hal tersebut dapat diabaikan untuk keperluan dokumen ini. Artinya, Keymaster HAL API yang dijelaskan adalah tingkat rendah, digunakan oleh komponen internal platform, dan tidak diekspos ke pengembang aplikasi. API tingkat yang lebih tinggi dijelaskan di situs Pengembang Android .

Tujuan dari Keymaster HAL bukan untuk mengimplementasikan algoritma yang sensitif terhadap keamanan tetapi hanya untuk mengatur dan membatalkan permintaan ke dunia yang aman. Format kawat ditentukan oleh implementasi.

Kompatibilitas dengan versi sebelumnya

Keymaster 1 HAL sepenuhnya tidak kompatibel dengan HAL yang dirilis sebelumnya, misalnya Keymaster 0.2 dan 0.3. Untuk memfasilitasi interoperabilitas pada perangkat yang menjalankan Android 5.0 dan versi lebih lama yang diluncurkan dengan Keymaster HAL lama, Keystore menyediakan adaptor yang mengimplementasikan Keymaster 1 HAL dengan panggilan ke pustaka perangkat keras yang ada. Hasilnya tidak dapat menyediakan fungsionalitas penuh di Keymaster 1 HAL. Secara khusus, ini hanya mendukung algoritma RSA dan ECDSA, dan semua penegakan otorisasi kunci dilakukan oleh adaptor, di dunia yang tidak aman.

Keymaster 2 semakin menyederhanakan antarmuka HAL dengan menghapus metode get_supported_* dan mengizinkan metode finish() menerima masukan. Hal ini mengurangi jumlah perjalanan bolak-balik ke TEE jika input tersedia sekaligus, dan menyederhanakan penerapan dekripsi AEAD.

Di Android 8.0, Keymaster 3 bertransisi dari struktur C HAL gaya lama ke antarmuka C++ HAL yang dihasilkan dari definisi dalam Hardware Interface Definition Language (HIDL) yang baru. Implementasi HAL gaya baru dibuat dengan membuat subkelas kelas IKeymasterDevice yang dihasilkan dan mengimplementasikan metode virtual murni. Sebagai bagian dari perubahan, banyak tipe argumen telah berubah, meskipun tipe dan metode memiliki korespondensi satu-ke-satu dengan tipe lama dan metode struct HAL.

Ikhtisar HIDL

Hardware Interface Definition Language (HIDL) menyediakan mekanisme implementasi yang tidak bergantung pada bahasa untuk menentukan antarmuka perangkat keras. Perkakas HIDL saat ini mendukung pembuatan antarmuka C++ dan Java. Diharapkan sebagian besar pelaksana Lingkungan Eksekusi Tepercaya (TEE) akan menganggap perkakas C++ lebih nyaman, sehingga dokumen ini hanya membahas representasi C++.

Antarmuka HIDL terdiri dari sekumpulan metode, yang dinyatakan sebagai:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Ada berbagai tipe yang telah ditentukan sebelumnya, dan HAL dapat menentukan tipe enumerasi dan struktur baru. Untuk rincian lebih lanjut tentang HIDL, lihat bagian Referensi .

Contoh metode dari Keymaster 3 IKeymasterDevice.hal adalah:

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

Ini setara dengan yang berikut ini dari keymaster2 HAL:

keymaster_error_t (*generate_key)(
        const struct keymaster2_device* dev,
        const keymaster_key_param_set_t* params,
        keymaster_key_blob_t* key_blob,
        keymaster_key_characteristics_t* characteristics);

Dalam versi HIDL, argumen dev dihilangkan, karena bersifat implisit. Argumen params bukan lagi sebuah struct yang berisi pointer yang mereferensikan array objek key_parameter_t , tetapi sebuah vec (vektor) yang berisi objek KeyParameter . Nilai yang dikembalikan tercantum dalam klausa " generates ", termasuk vektor nilai uint8_t untuk gumpalan kunci.

Metode virtual C++ yang dihasilkan oleh kompiler HIDL adalah:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

Dimana generateKey_cb adalah penunjuk fungsi yang didefinisikan sebagai:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Artinya, generateKey_cb adalah fungsi yang mengambil nilai kembalian yang tercantum dalam klausa generate. Kelas implementasi HAL mengesampingkan metode generateKey ini dan memanggil penunjuk fungsi generateKey_cb untuk mengembalikan hasil operasi ke pemanggil. Perhatikan bahwa panggilan penunjuk fungsi adalah synchronous . Penelepon memanggil generateKey dan generateKey memanggil penunjuk fungsi yang disediakan, yang dijalankan hingga selesai, mengembalikan kontrol ke implementasi generateKey , yang kemudian kembali ke pemanggil.

Untuk contoh detailnya, lihat implementasi default di hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . Implementasi default menyediakan kompatibilitas mundur untuk perangkat dengan keymaster0, keymaster1, atau keymaster2 HALS gaya lama.

Kontrol akses

Aturan paling dasar dari kontrol akses Keystore adalah setiap aplikasi memiliki namespace sendiri. Namun untuk setiap aturan ada pengecualian. Keystore memiliki beberapa peta hard-code yang memungkinkan komponen sistem tertentu mengakses namespace tertentu lainnya. Ini adalah instrumen yang sangat tumpul karena memberikan satu komponen kendali penuh atas namespace lainnya. Lalu ada masalah komponen vendor sebagai klien Keystore. Saat ini kami tidak memiliki cara untuk membuat namespace untuk komponen vendor, misalnya pemohon WPA.

Untuk mengakomodasi komponen vendor dan menggeneralisasi kontrol akses tanpa pengecualian hard-code, Keystore 2.0 memperkenalkan domain dan namespace SELinux.

Domain Penyimpanan Kunci

Dengan domain Keystore, kita dapat memisahkan namespace dari UID. Klien yang mengakses kunci di Keystore harus menentukan domain, namespace, dan alias yang ingin mereka akses. Berdasarkan tupel ini dan identitas penelepon, kita dapat menentukan kunci mana yang ingin diakses oleh penelepon dan apakah kunci tersebut memiliki izin yang sesuai.

Kami memperkenalkan lima parameter domain yang mengatur bagaimana kunci dapat diakses. Mereka mengontrol semantik parameter namespace dari deskriptor kunci dan bagaimana kontrol akses dilakukan.

  • DOMAIN_APP : Domain aplikasi mencakup perilaku lama. Java Keystore SPI menggunakan domain ini secara default. Saat domain ini digunakan, argumen namespace diabaikan dan UID pemanggil digunakan sebagai gantinya. Akses ke domain ini dikontrol oleh label Keystore ke kelas keystore_key dalam kebijakan SELinux.
  • DOMAIN_SELINUX : Domain ini menunjukkan bahwa namespace memiliki label dalam kebijakan SELinux. Parameter namespace dicari dan diterjemahkan ke dalam konteks target, dan pemeriksaan izin dilakukan untuk konteks pemanggilan SELinux untuk kelas keystore_key . Ketika izin telah ditetapkan untuk operasi tertentu, tupel lengkap digunakan untuk pencarian kunci.
  • DOMAIN_GRANT : Domain hibah menunjukkan bahwa parameter namespace adalah pengidentifikasi hibah. Parameter alias diabaikan. Pemeriksaan SELinux dilakukan ketika hibah dibuat. Kontrol akses lebih lanjut hanya memeriksa apakah UID pemanggil cocok dengan UID penerima hibah yang diminta.
  • DOMAIN_KEY_ID : Domain ini menunjukkan bahwa parameter namespace adalah id kunci unik. Kuncinya sendiri mungkin dibuat dengan DOMAIN_APP atau DOMAIN_SELINUX . Pemeriksaan izin dilakukan setelah domain dan namespace dimuat dari database kunci dengan cara yang sama seperti jika blob dimuat oleh domain, namespace, dan tuple alias. Alasan untuk domain id kunci adalah kontinuitas. Saat mengakses kunci dengan alias, panggilan berikutnya mungkin beroperasi pada kunci yang berbeda, karena kunci baru mungkin telah dibuat atau diimpor dan diikat ke alias ini. Namun, id kuncinya tidak pernah berubah. Jadi ketika menggunakan key by key id setelah dimuat dari database Keystore menggunakan alias satu kali, dapat dipastikan bahwa key tersebut adalah key yang sama selama key id tersebut masih ada. Fungsionalitas ini tidak diekspos ke pengembang aplikasi. Sebaliknya, ini digunakan dalam Android Keystore SPI untuk memberikan pengalaman yang lebih konsisten bahkan ketika digunakan secara bersamaan dengan cara yang tidak aman.
  • DOMAIN_BLOB : Domain blob menunjukkan bahwa pemanggil mengelola blob itu sendiri. Ini digunakan untuk klien yang perlu mengakses Keystore sebelum partisi data dipasang. Blob kunci disertakan dalam bidang blob pada deskriptor kunci.

Dengan menggunakan domain SELinux, kami dapat memberikan komponen vendor akses ke namespace Keystore yang sangat spesifik yang dapat dibagikan oleh komponen sistem seperti dialog pengaturan.

Kebijakan SELinux untuk keystore_key

Label namespace dikonfigurasi menggunakan file keystore2_key_context .
Setiap baris dalam file ini memetakan id namespace numerik ke label SELinux. Misalnya,

# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and
# Settings to share keystore keys.
102            u:object_r:wifi_key:s0

Setelah menyiapkan namespace kunci baru dengan cara ini, kita dapat memberikan akses ke sana dengan menambahkan kebijakan yang sesuai. Misalnya, untuk mengizinkan wpa_supplicant mendapatkan dan menggunakan kunci di namespace baru, kami akan menambahkan baris berikut ke hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

Setelah menyiapkan namespace baru, AndroidKeyStore dapat digunakan hampir seperti biasa. Satu-satunya perbedaan adalah ID namespace harus ditentukan. Untuk memuat dan mengimpor kunci dari dan ke Keystore, id namespace ditentukan menggunakan AndroidKeyStoreLoadStoreParameter . Misalnya,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

Untuk menghasilkan kunci dalam namespace tertentu, id namespace harus diberikan menggunakan KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

File konteks berikut dapat digunakan untuk mengonfigurasi namespace Keystore 2.0 SELinux. Setiap partisi memiliki rentang cadangan 10.000 id namespace yang berbeda untuk menghindari tabrakan.

Partisi Jangkauan File konfigurasi
Sistem 0 ... 9.999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Sistem yang Diperluas 10.000...19.999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Produk 20.000...29.999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Penjual 30.000...39.999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

Klien meminta kunci dengan meminta domain SELinux dan namespace virtual yang diinginkan, dalam hal ini "wifi_key" , dengan id numeriknya.

Di atasnya, namespace berikut telah ditentukan. Jika aturan tersebut menggantikan aturan khusus, tabel berikut menunjukkan UID yang digunakan untuk menyesuaikannya.

ID ruang nama Label Kebijakan SE UID Keterangan
0 su_key T/A Kunci pengguna super. Hanya digunakan untuk pengujian pada userdebug dan build eng. Tidak relevan pada build pengguna.
1 shell_key T/A Namespace tersedia untuk shell. Sebagian besar digunakan untuk pengujian, tetapi dapat digunakan pada build pengguna juga dari baris perintah.
100 vold_key T/A Ditujukan untuk digunakan oleh vold.
101 odsing_key T/A Digunakan oleh daemon penandatanganan di perangkat.
102 wifi_key AID_WIFI(1010) Digunakan oleh sistem Wifi Android termasuk wpa_supplicant.
120 resume_on_reboot_key AID_SYSTEM(1000) Digunakan oleh server sistem Android untuk mendukung resume saat reboot.

Akses Vektor

Kelas SELinux keystore_key sudah cukup tua dan beberapa izin, seperti verify atau sign telah kehilangan maknanya. Berikut adalah kumpulan izin baru, keystore2_key , yang akan diterapkan oleh Keystore 2.0.

Izin Arti
delete Diperiksa saat melepas kunci dari Keystore.
get_info Diperiksa ketika metadata kunci diminta.
grant Penelepon memerlukan izin ini untuk membuat pemberian izin pada kunci dalam konteks target.
manage_blob Penelepon dapat menggunakan DOMAIN_BLOB pada namespace SELinux yang diberikan, sehingga mengelola blob sendiri. Ini secara khusus berguna untuk vold.
rebind Izin ini mengontrol apakah alias dapat dikembalikan ke kunci baru. Ini diperlukan untuk penyisipan dan menyiratkan bahwa kunci yang diikat sebelumnya akan dihapus. Ini pada dasarnya adalah izin penyisipan, tetapi menangkap semantik keystore dengan lebih baik.
req_forced_op Klien dengan izin ini dapat membuat operasi yang tidak dapat dilakukan, dan pembuatan operasi tidak akan pernah gagal kecuali semua slot operasi diambil oleh operasi yang tidak dapat dilakukan.
update Diperlukan untuk memperbarui subkomponen kunci.
use Diperiksa saat membuat operasi Keymint yang menggunakan material kunci, misalnya untuk penandatanganan, en/dekripsi.
use_dev_id Diperlukan saat membuat informasi pengidentifikasi perangkat, seperti pengesahan id perangkat.

Selain itu, kami membagi serangkaian izin keystore khusus non-kunci di kelas keamanan SELinux keystore2 :

Izin Arti
add_auth Diperlukan oleh penyedia autentikasi seperti Gatekeeper atau BiometricsManager untuk menambahkan token autentikasi.
clear_ns Sebelumnya clear_uid, izin ini memungkinkan orang yang bukan pemilik namespace menghapus semua kunci di namespace tersebut.
list Diperlukan oleh sistem untuk menghitung kunci berdasarkan berbagai properti, seperti kepemilikan atau batasan autentikasi. Izin ini tidak diperlukan oleh penelepon yang menyebutkan namespace mereka sendiri. Ini tercakup dalam izin get_info .
lock Izin ini memungkinkan untuk mengunci Keystore, yaitu mengeluarkan kunci master, sehingga kunci terikat autentikasi menjadi tidak dapat digunakan dan tidak dapat dibuat.
reset Izin ini memungkinkan untuk mereset Keystore ke default pabrik, menghapus semua kunci yang tidak penting untuk berfungsinya OS Android.
unlock Izin ini diperlukan untuk mencoba membuka kunci master untuk kunci terikat autentikasi.