Arsitektur penandatanganan di perangkat

Mulai Android 12, modul Android Runtime (ART) adalah modul Mainline. Mengupdate modul mungkin mengharuskan modul tersebut mem-build ulang artefak kompilasi ahead-of-time (AOT) dari jar bootclasspath dan server sistem. Karena artefak ini sensitif terhadap keamanan, Android 12 menggunakan fitur yang disebut penandatanganan di perangkat untuk mencegah artefak ini dirusak. Halaman ini membahas arsitektur penandatanganan di perangkat dan interaksinya dengan fitur keamanan Android lainnya.

Desain tingkat tinggi

Penandatanganan di perangkat memiliki dua komponen inti:

  • odrefresh adalah bagian dari modul ART Mainline. Alat ini bertanggung jawab untuk membuat artefak runtime. Alat ini memeriksa artefak yang ada dengan versi modul ART, jar bootclasspath, dan jar server sistem yang diinstal untuk menentukan apakah artefak tersebut sudah yang terbaru atau perlu dibuat ulang. Jika perlu dibuat ulang, odrefresh akan membuatnya dan menyimpannya.

  • odsign adalah biner yang merupakan bagian dari platform Android. Proses ini berjalan selama booting awal, tepat setelah partisi /data dipasang. Tanggung jawab utamanya adalah memanggil odrefresh untuk memeriksa apakah ada artefak yang perlu dibuat atau diperbarui. Untuk artefak baru atau yang diperbarui yang dihasilkan odrefresh, odsign akan menghitung fungsi hash. Hasil komputasi hash tersebut disebut ringkasan file. Untuk artefak yang sudah ada, odsign memverifikasi bahwa ringkasan artefak yang ada cocok dengan ringkasan yang telah dihitung odsign sebelumnya. Hal ini memastikan bahwa artefak tidak dirusak.

Dalam kondisi error, seperti saat ringkasan untuk file tidak cocok, odrefresh dan odsign akan menghapus semua artefak yang ada di /data dan mencoba membuatnya ulang. Jika gagal, sistem akan kembali ke mode JIT.

odrefresh dan odsign dilindungi oleh dm-verity, dan merupakan bagian dari rantai Verified Boot Android.

Komputasi ringkasan file dengan fs-verity

fs-verity adalah fitur kernel Linux yang melakukan verifikasi data file berbasis hierarki Merkle. Mengaktifkan fs-verity pada file menyebabkan sistem file membuat hierarki Merkle pada data file menggunakan hash SHA-256, menyimpannya di lokasi tersembunyi bersama file, dan menandai file sebagai hanya baca. fs-verity otomatis memverifikasi data file terhadap hierarki Merkle sesuai permintaan saat dibaca. fs-verity menyediakan hash root hierarki Merkle sebagai nilai yang disebut ringkasan file fs-verity, dan fs-verity memastikan bahwa data apa pun yang dibaca dari file konsisten dengan ringkasan file ini.

odsign menggunakan fs-verity untuk meningkatkan performa booting dengan mengoptimalkan autentikasi kriptografis artefak yang dikompilasi di perangkat pada waktu booting. Saat artefak dibuat, odsign akan mengaktifkan fs-verity di artefak tersebut. Saat odsign memverifikasi artefak, odsign akan memverifikasi ringkasan file fs-verity, bukan hash file lengkap. Dengan demikian, Anda tidak perlu membaca dan melakukan hashing data lengkap artefak pada waktu booting. Sebagai gantinya, data artefak di-hash sesuai permintaan oleh fs-verity saat digunakan, berdasarkan blok demi blok.

Pada perangkat yang kernel-nya tidak mendukung fs-verity, odsign akan kembali ke penghitungan ringkasan file di ruang pengguna. odsign menggunakan algoritma hash berbasis pohon Merkle yang sama dengan fs-verity, sehingga ringkasannya sama dalam kedua kasus tersebut. fs-verity diperlukan di semua perangkat yang diluncurkan dengan Android 11 dan yang lebih tinggi.

Penyimpanan ringkasan file

odsign menyimpan ringkasan file artefak dalam file terpisah yang disebut odsign.info. Untuk memastikan odsign.info tidak dimodifikasi, odsign.info ditandatangani dengan kunci penandatanganan yang memiliki properti keamanan penting. Secara khusus, kunci hanya dapat dibuat dan digunakan selama booting awal, saat hanya kode tepercaya yang berjalan; lihat Kunci penandatanganan tepercaya untuk mengetahui detailnya.

Verifikasi ringkasan file

Pada setiap booting, jika odrefresh menentukan bahwa artefak yang ada sudah yang terbaru, odsign akan memastikan bahwa file tersebut tidak dimodifikasi sejak file tersebut dibuat. odsign melakukannya dengan memverifikasi ringkasan file. Pertama, verifikasi tanda tangan odsign.info. Jika tanda tangan valid, odsign akan memverifikasi bahwa ringkasan setiap file cocok dengan ringkasan yang sesuai di odsign.info.

Kunci penandatanganan tepercaya

Android 12 memperkenalkan fitur Keystore baru yang disebut kunci tahap booting yang mengatasi masalah keamanan berikut:

  • Apa yang mencegah penyerang menggunakan kunci penandatanganan kami untuk menandatangani versi odsign.info mereka sendiri?
  • Apa yang mencegah penyerang membuat kunci penandatanganan sendiri dan menggunakannya untuk menandatangani versi odsign.info mereka sendiri?

Kunci tahap booting membagi siklus booting Android menjadi beberapa level, dan secara kriptografis mengikat pembuatan dan penggunaan kunci ke level yang ditentukan. odsign membuat kunci penandatanganan pada tingkat awal, saat hanya kode tepercaya yang berjalan, yang dilindungi melalui dm-verity.

Level tahap booting diberi nomor dari 0 hingga angka ajaib 1000000000. Selama proses booting Android, Anda dapat meningkatkan level booting dengan menetapkan properti sistem dari init.rc. Misalnya, kode berikut menetapkan level booting ke 10:

setprop keystore.boot_level 10

Klien Keystore dapat membuat kunci yang terikat dengan level booting tertentu. Misalnya, jika Anda membuat kunci untuk level booting 10, kunci tersebut hanya dapat digunakan saat perangkat berada di level booting 10.

odsign menggunakan level booting 30, dan kunci penandatanganan yang dibuatnya terikat dengan level booting tersebut. Sebelum menggunakan kunci untuk menandatangani artefak, odsign akan memverifikasi bahwa kunci terikat dengan level booting 30.

Hal ini mencegah dua serangan yang dijelaskan sebelumnya di bagian ini:

  • Penyerang tidak dapat menggunakan kunci yang dihasilkan, karena pada saat penyerang memiliki kesempatan untuk menjalankan kode berbahaya, level booting telah meningkat melebihi 30, dan Keystore menolak operasi yang menggunakan kunci.
  • Penyerang tidak dapat membuat kunci baru, karena pada saat penyerang memiliki peluang untuk menjalankan kode berbahaya, tingkat booting telah meningkat di atas 30, dan Keystore menolak untuk membuat kunci baru dengan tingkat booting tersebut. Jika penyerang membuat kunci baru yang tidak terikat dengan tingkat booting 30, odsign akan menolaknya.

Keystore memastikan bahwa tingkat booting diterapkan dengan benar. Bagian berikut membahas lebih detail cara melakukannya untuk berbagai versi Keymaster.

Implementasi Keymaster 4.0

Versi Keymaster yang berbeda menangani implementasi kunci tahap booting dengan cara yang berbeda. Pada perangkat dengan TEE/Strongbox Keymaster 4.0, Keymaster menangani penerapan sebagai berikut:

  1. Saat pertama kali di-booting, Keystore membuat kunci simetris K0 dengan tag MAX_USES_PER_BOOT yang ditetapkan ke 1. Artinya, kunci hanya dapat digunakan sekali per booting.
  2. Selama booting, jika level booting ditingkatkan, kunci baru untuk level booting tersebut dapat dibuat dari K0 menggunakan fungsi HKDF: Ki+i=HKDF(Ki, "some_fixed_string"). Misalnya, jika Anda berpindah dari level booting 0 ke level booting 10, HKDF akan dipanggil 10 kali untuk mendapatkan K10 dari K0.
  3. Saat level booting berubah, kunci untuk level booting sebelumnya akan dihapus dari memori, dan kunci yang terkait dengan level booting sebelumnya tidak lagi tersedia.

    Kunci K0 adalah kunci MAX_USES_PER_BOOT=1. Artinya, kunci tersebut juga tidak dapat digunakan nanti saat booting, karena setidaknya satu transisi tingkat booting (ke tingkat booting akhir) selalu terjadi.

Saat klien Keystore seperti odsign meminta kunci untuk dibuat di level booting i, blob-nya dienkripsi dengan kunci Ki. Karena Ki tidak tersedia setelah level booting i, kunci ini tidak dapat dibuat atau didekripsi di tahap booting berikutnya.

Implementasi Keymaster 4.1 dan KeyMint 1.0

Implementasi Keymaster 4.1 dan KeyMint 1.0 sebagian besar sama dengan implementasi Keymaster 4.0. Perbedaan utamanya adalah K0 bukan kunci MAX_USES_PER_BOOT, tetapi kunci EARLY_BOOT_ONLY, yang diperkenalkan di Keymaster 4.1. Kunci EARLY_BOOT_ONLY hanya dapat digunakan selama fase awal booting, saat tidak ada kode yang tidak tepercaya yang berjalan. Hal ini memberikan tingkat perlindungan tambahan: dalam penerapan Keymaster 4.0, penyerang yang membahayakan sistem file dan SELinux dapat mengubah database Keystore untuk membuat kunci MAX_USES_PER_BOOT=1-nya sendiri untuk menandatangani artefak. Serangan semacam itu tidak mungkin dengan implementasi Keymaster 4.1 dan KeyMint 1.0, karena kunci EARLY_BOOT_ONLY hanya dapat dibuat selama booting awal.

Komponen publik dari kunci penandatanganan tepercaya

odsign mengambil komponen kunci publik dari kunci penandatanganan dari Keystore. Namun, Keystore tidak mengambil kunci publik tersebut dari TEE/SE yang menyimpan kunci pribadi yang sesuai. Sebagai gantinya, server mengambil kunci publik dari database di disk-nya sendiri. Artinya, penyerang yang membahayakan sistem file dapat mengubah database Keystore agar berisi kunci publik yang merupakan bagian dari pasangan kunci publik/pribadi yang berada di bawah kendali mereka.

Untuk mencegah serangan ini, odsign membuat kunci HMAC tambahan dengan tingkat booting yang sama dengan kunci penandatanganan. Kemudian, saat membuat kunci penandatanganan, odsign menggunakan kunci HMAC ini untuk membuat tanda tangan kunci publik dan menyimpannya di disk. Pada booting berikutnya, saat mengambil kunci publik dari kunci penandatanganan, sistem akan menggunakan kunci HMAC untuk memverifikasi bahwa tanda tangan di disk cocok dengan tanda tangan kunci publik yang diambil. Jika cocok, kunci publik dapat dipercaya, karena kunci HMAC hanya dapat digunakan di tingkat booting awal sehingga tidak dapat dibuat oleh penyerang.