Arsitektur penandatanganan di perangkat

Mulai Android 12, modul Android Runtime (ART) adalah modul Mainline. Mengupdate modul mungkin mengharuskan modul membangun kembali artefak kompilasi ahead-of-time (AOT) dari bootclasspath JAR 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 Mainline ART. Tanggung jawabnya adalah membuat artefak runtime. Alat ini memeriksa artefak yang ada terhadap versi modul ART yang diinstal, JAR bootclasspath, dan JAR server sistem untuk menentukan apakah artefak tersebut sudah 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 boot awal, tepat setelah partisi /data di-mount. Tanggung jawab utamanya adalah memanggil odrefresh untuk memeriksa apakah ada artefak yang perlu dibuat atau diupdate. Untuk artefak baru atau yang diupdate yang dihasilkan odrefresh, odsign 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 sebelumnya dihitung oleh odsign. Hal ini memastikan bahwa artefak tidak dirusak.

Dalam kondisi error, seperti saat ringkasan 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 Booting Terverifikasi Android.

Penghitungan ringkasan file dengan fs-verity

fs-verity adalah fitur kernel Linux yang melakukan verifikasi data file berbasis pohon Merkle. Mengaktifkan fs-verity pada file menyebabkan sistem file membuat pohon Merkle pada data file menggunakan hash SHA-256, menyimpannya di lokasi tersembunyi bersama file, dan menandai file sebagai hanya baca. fs-verity secara otomatis memverifikasi data file terhadap pohon Merkle sesuai permintaan saat dibaca. fs-verity membuat hash root pohon Merkle tersedia 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 kriptografi artefak yang dikompilasi di perangkat pada waktu booting. Saat artefak dibuat, odsign mengaktifkan fs-verity di artefak tersebut. Saat memverifikasi artefak, odsign memverifikasi ringkasan file fs-verity, bukan hash file lengkap. Hal ini menghilangkan kebutuhan untuk membaca dan membuat hash data lengkap artefak saat waktu booting. Data artefak di-hash sesuai permintaan oleh fs-verity saat digunakan, berdasarkan per blok.

Pada perangkat yang kernelnya tidak mendukung fs-verity, odsign akan dikembalikan ke penghitungan ringkasan file di ruang pengguna. odsign menggunakan algoritma hash berbasis pohon Merkle yang sama dengan fs-verity, sehingga digestnya 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 dapat dibuat dan digunakan hanya selama booting awal, yang pada saat itu 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 up-to-date, odsign memastikan bahwa file tidak dirusak sejak dibuat. odsign melakukannya dengan memverifikasi ringkasan file. Pertama, kode ini memverifikasi 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 miliknya sendiri?

Kunci tahap booting membagi siklus booting Android menjadi beberapa level, dan secara kriptografi mengikat pembuatan dan penggunaan kunci ke level tertentu. odsign membuat kunci penandatanganannya di 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 tingkat booting dengan menyetel 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 pada level booting tertentu. Misalnya, jika Anda membuat kunci untuk tingkat booting 10, kunci tersebut hanya dapat digunakan saat perangkat berada di tingkat booting 10.

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

Tindakan 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 di atas 30, dan Keystore menolak operasi yang menggunakan kunci tersebut.
  • Penyerang tidak dapat membuat kunci baru, karena pada saat penyerang memiliki kesempatan untuk menjalankan kode berbahaya, level booting telah meningkat di atas 30, dan Keystore menolak untuk membuat kunci baru dengan level booting tersebut. Jika penyerang membuat kunci baru yang tidak terikat ke level booting 30, odsign akan menolaknya.

Keystore memastikan bahwa tingkat booting diterapkan dengan benar. Bagian berikut membahas lebih mendetail cara melakukannya untuk berbagai versi KeyMint (sebelumnya Keymaster).

Implementasi Keymaster 4.0

Versi Keymaster yang berbeda menangani implementasi kunci tahap booting secara berbeda. Di perangkat dengan TEE/StrongBox Keymaster 4.0, Keymaster menangani implementasi sebagai berikut:

  1. Saat booting pertama, Keystore membuat kunci simetris K0 dengan setelan tag MAX_USES_PER_BOOT 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 dipanggil 10 kali untuk mendapatkan K10 dari K0.
  3. Saat tingkat booting berubah, kunci untuk tingkat booting sebelumnya akan dihapus dari memori, dan kunci yang terkait dengan tingkat booting sebelumnya tidak lagi tersedia.

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

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

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 terjadi dengan penerapan Keymaster 4.1 dan KeyMint 1.0, karena kunci EARLY_BOOT_ONLY hanya dapat dibuat selama booting awal.

Komponen publik 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, kunci publik diambil dari database di disknya sendiri. Artinya, penyerang yang membahayakan sistem file dapat mengubah database Keystore agar berisi kunci publik yang merupakan bagian dari pasangan kunci publik/pribadi 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, kunci tersebut 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 level booting awal dan oleh karena itu tidak mungkin dibuat oleh penyerang.