A partire da Android 12, il modulo Android Runtime (ART) è un modulo Mainline. L'aggiornamento del modulo potrebbe richiedere la ricompilazione degli artefatti di compilazione AOT (Ahead-Of-Time) dei JAR di bootclasspath e del server di sistema. Poiché questi artefatti sono sensibili alla sicurezza, Android 12 utilizza una funzionalità chiamata firma sul dispositivo per impedire che vengano manomessi. Questa pagina illustra l'architettura della firma sul dispositivo e le sue interazioni con altre funzionalità di sicurezza di Android.
Progettazione di alto livello
La firma sul dispositivo ha due componenti principali:
odrefreshfa parte del modulo Mainline ART. È responsabile della generazione degli artefatti di runtime. Controlla gli artefatti esistenti rispetto alla versione installata del modulo ART, ai JAR di bootclasspath e ai JAR del server di sistema per determinare se sono aggiornati o se devono essere rigenerati. Se devono essere rigenerati,odrefreshli genera e li archivia.odsignè un file binario che fa parte della piattaforma Android. Viene eseguito durante la fase iniziale di avvio, subito dopo il montaggio della partizione/data. La sua responsabilità principale è richiamareodrefreshper verificare se è necessario generare o aggiornare artefatti. Per tutti gli artefatti nuovi o aggiornati generati daodrefresh,odsigncalcola una funzione hash. Il risultato di questo calcolo hash è chiamato digest del file. Per tutti gli artefatti già esistenti,odsignverifica che i digest degli artefatti esistenti corrispondano ai digest calcolati in precedenza daodsign. In questo modo si garantisce che gli artefatti non siano stati manomessi.
In caso di errori, ad esempio quando il digest di un file non corrisponde, odrefresh e odsign eliminano tutti gli artefatti esistenti in /data e tentano di rigenerarli. Se l'operazione non riesce, il sistema torna alla modalità JIT.
odrefresh e odsign sono protetti da dm-verity e fanno parte della catena di avvio verificato di Android.
Calcolo dei digest dei file con fs-verity
fs-verity è una funzionalità del kernel Linux che esegue la verifica dei dati dei file basata sull'albero di Merkle. L'attivazione di fs-verity su un file fa sì che il file system crei un albero di Merkle sui dati del file utilizzando gli hash SHA-256, lo memorizzi in una posizione nascosta accanto al file e contrassegni il file come di sola lettura. fs-verity verifica automaticamente i dati del file rispetto all'albero di Merkle su richiesta durante la lettura. fs-verity rende disponibile l'hash della radice dell'albero di Merkle come valore chiamato il digest del file fs-verity e garantisce che tutti i dati letti dal file siano coerenti con questo digest del file.
odsign utilizza fs-verity per migliorare il rendimento dell'avvio ottimizzando l'autenticazione crittografica degli artefatti compilati sul dispositivo al momento dell'avvio. Quando viene generato un artefatto, odsign abilita fs-verity. Quando odsign verifica un artefatto, verifica il digest del file fs-verity anziché l'hash completo del file. In questo modo non è necessario leggere e calcolare l'hash di tutti i dati dell'artefatto al momento dell'avvio. I dati dell'artefatto vengono invece sottoposti a hashing su richiesta da fs-verity durante l'utilizzo, blocco per blocco.
Sui dispositivi il cui kernel non supporta fs-verity, odsign torna al calcolo dei digest dei file nello spazio utente. odsign utilizza lo stesso algoritmo hash basato sull'albero di Merkle di fs-verity, quindi i digest sono gli stessi in entrambi i casi. fs-verity è obbligatorio su tutti i dispositivi lanciati con Android 11 e versioni successive.
Archiviazione dei digest dei file
odsign archivia i digest dei file degli artefatti in un file separato chiamato
odsign.info. Per assicurarsi che odsign.info non venga manomesso, odsign.info viene firmato con una chiave di firma che ha importanti proprietà di sicurezza. In particolare, la chiave può essere generata e utilizzata solo durante la fase iniziale di avvio, quando è in esecuzione solo codice attendibile. Per maggiori dettagli, consulta Chiavi di firma attendibili.
Verifica dei digest dei file
A ogni avvio, se odrefresh determina che gli artefatti esistenti sono aggiornati, odsign garantisce che i file non siano stati manomessi dalla loro generazione. odsign esegue questa operazione verificando i digest dei file. Innanzitutto, verifica la firma di odsign.info. Se la firma è valida, allora
odsign verifica che il digest di ogni file corrisponda al digest corrispondente
in odsign.info.
Chiavi di firma attendibili
Android 12 introduce una nuova funzionalità di Keystore chiamata chiavi di fase di avvio che risolve i seguenti problemi di sicurezza:
- Cosa impedisce a un utente malintenzionato di utilizzare la nostra chiave di firma per firmare la propria versione di
odsign.info? - Cosa impedisce a un utente malintenzionato di generare la propria chiave di firma e di utilizzarla per firmare la propria versione di
odsign.info?
Le chiavi di fase di avvio dividono il ciclo di avvio di Android in livelli e collegano crittograficamente la creazione e l'utilizzo di una chiave a un livello specificato. odsign crea la chiave di firma a un livello iniziale, quando è in esecuzione solo codice attendibile, protetto tramite dm-verity.
I livelli di fase di avvio sono numerati da 0 al numero magico 1000000000. Durante la procedura di avvio di Android, puoi aumentare il livello di avvio impostando una proprietà di sistema da init.rc. Ad esempio, il seguente codice imposta il livello di avvio su 10:
setprop keystore.boot_level 10
I client di Keystore possono creare chiavi collegate a un determinato livello di avvio. Ad esempio, se crei una chiave per il livello di avvio 10, questa chiave può essere utilizzata solo quando il dispositivo è al livello di avvio 10.
odsign utilizza il livello di avvio 30 e la chiave di firma che crea è collegata a questo livello di avvio. Prima di utilizzare una chiave per firmare gli artefatti, odsign verifica che la chiave sia collegata al livello di avvio 30.
In questo modo si evitano i due attacchi descritti in precedenza in questa sezione:
- Gli utenti malintenzionati non possono utilizzare la chiave generata perché, quando hanno la possibilità di eseguire codice dannoso, il livello di avvio è aumentato oltre 30 e Keystore rifiuta le operazioni che utilizzano la chiave.
- Gli utenti malintenzionati non possono creare una nuova chiave perché, quando hanno la possibilità di eseguire codice dannoso, il livello di avvio è aumentato oltre 30 e Keystore rifiuta di creare una nuova chiave con questo livello di avvio. Se un utente malintenzionato crea una nuova chiave non collegata al livello di avvio 30,
odsignla rifiuta.
Keystore garantisce che il livello di avvio venga applicato correttamente. Le sezioni seguenti forniscono maggiori dettagli su come viene eseguita questa operazione per le diverse versioni di KeyMint (in precedenza Keymaster).
Implementazione di Keymaster 4.0
Versioni diverse di Keymaster gestiscono l'implementazione delle chiavi di fase di avvio in modo diverso. Sui dispositivi con un TEE/StrongBox Keymaster 4.0, Keymaster gestisce l'implementazione nel seguente modo:
- Al primo avvio, Keystore crea una chiave simmetrica K0 con il tag
MAX_USES_PER_BOOTimpostato su1. Ciò significa che la chiave può essere utilizzata una sola volta per avvio. - Durante l'avvio, se il livello di avvio viene aumentato, è possibile generare una nuova chiave per quel livello di avvio
da K0 utilizzando una funzione HKDF:
Ki+i=HKDF(Ki, "some_fixed_string"). Ad esempio, se passi dal livello di avvio 0 al livello di avvio 10, l'HKDF viene richiamato 10 volte per derivare K10 da K0. Quando il livello di avvio cambia, la chiave per il livello di avvio precedente viene cancellata dalla memoria e le chiavi associate ai livelli di avvio precedenti non sono più disponibili.
La chiave K0 è una chiave
MAX_USES_PER_BOOT=1. Ciò significa che è anche impossibile utilizzare la chiave in un secondo momento durante l'avvio, perché si verifica sempre almeno una transizione del livello di avvio (al livello di avvio finale).
Quando un client Keystore come odsign richiede la creazione di una chiave nel livello di avvio i, il relativo blob viene criptato con la chiave Ki. Poiché Ki non è disponibile
dopo il livello di avvio i, questa chiave non può essere creata o decriptata nelle fasi di avvio successive.
Implementazione di Keymaster 4.1 e KeyMint 1.0
Le implementazioni di Keymaster 4.1 e KeyMint 1.0 sono in gran parte uguali all'implementazione di Keymaster 4.0. La differenza principale è che K0 non è una chiave MAX_USES_PER_BOOT, ma una chiave EARLY_BOOT_ONLY, introdotta in Keymaster 4.1. Una chiave EARLY_BOOT_ONLY può essere utilizzata solo durante le fasi iniziali dell'avvio, quando non è in esecuzione codice non attendibile. Ciò fornisce un ulteriore livello di protezione: nell'implementazione di Keymaster 4.0, un utente malintenzionato che compromette il file system e SELinux può modificare il database Keystore per creare la propria chiave MAX_USES_PER_BOOT=1 per firmare gli artefatti. Un attacco di questo tipo è impossibile con le implementazioni di Keymaster 4.1 e KeyMint 1.0, perché le chiavi EARLY_BOOT_ONLY possono essere create solo durante la fase iniziale di avvio.
Componente pubblico delle chiavi di firma attendibili
odsign recupera il componente della chiave pubblica della chiave di firma da Keystore.
Tuttavia, Keystore non recupera la chiave pubblica dal TEE/SE che contiene la chiave privata corrispondente. Recupera invece la chiave pubblica dal proprio database su disco. Ciò significa che un utente malintenzionato che compromette il file system potrebbe modificare il database Keystore in modo che contenga una chiave pubblica che fa parte di una coppia di chiavi pubblica/privata sotto il suo controllo.
Per evitare questo attacco, odsign crea una chiave HMAC aggiuntiva con lo stesso livello di avvio della chiave di firma. Poi, quando crea la chiave di firma, odsign utilizza questa chiave HMAC per creare una firma della chiave pubblica e la memorizza su disco. Negli avvii successivi, quando recupera la chiave pubblica della chiave di firma, utilizza la chiave HMAC per verificare che la firma su disco corrisponda alla firma della chiave pubblica recuperata. Se corrispondono, la chiave pubblica è attendibile, perché la chiave HMAC può essere utilizzata solo nelle fasi iniziali di avvio e quindi non può essere stata creata da un utente malintenzionato.