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 ahead-of-time (AOT) dei file JAR bootclasspath e del server di sistema. Poiché questi artefatti sono sensibili alla sicurezza, Android 12 utilizza una funzionalità chiamata firma sul dispositivo per impedire la manomissione di questi artefatti. Questa pagina descrive l'architettura di 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:
odrefresh
fa parte del modulo Mainline ART. È responsabile della generazione degli artefatti di runtime. Controlla gli artefatti esistenti rispetto alla versione installata del modulo ART, dei file JAR bootclasspath e dei file JAR del server di sistema per determinare se sono aggiornati o devono essere rigenerati. Se devono essere rigenerati,odrefresh
li genera e li memorizza.odsign
è un file binario che fa parte della piattaforma Android. Viene eseguito durante l'avvio iniziale, subito dopo il montaggio della partizione/data
. La sua responsabilità principale è richiamareodrefresh
per verificare se è necessario generare o aggiornare artefatti. Per tutti gli artefatti nuovi o aggiornati cheodrefresh
genera,odsign
calcola una funzione hash. Il risultato di questo calcolo hash è chiamato digest del file. Per gli artefatti già esistenti,odsign
verifica 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 condizioni di errore, ad esempio quando il digest di un file non corrisponde,
odrefresh
e odsign
eliminano tutti gli artefatti esistenti su /data
e
tentano di rigenerarli. Se l'operazione non va a buon fine, 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 Merkle. L'attivazione di fs-verity su un file fa sì che il file system crei un albero Merkle sui dati del file utilizzando 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 Merkle su richiesta durante la lettura. fs-verity rende disponibile l'hash radice dell'albero Merkle come valore chiamato 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 le prestazioni di 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 degli artefatti vengono invece sottoposti ad hashing su richiesta da fs-verity
man mano che vengono utilizzati, blocco per blocco.
Sui dispositivi il cui kernel non supporta fs-verity, odsign
esegue il fallback al calcolo dei digest dei file nello spazio utente. odsign
utilizza lo stesso algoritmo hash basato sull'albero Merkle di fs-verity, quindi i digest sono gli stessi in entrambi i casi. fs-verity è richiesto su tutti i dispositivi lanciati con Android 11 e versioni successive.
Archiviazione dei digest dei file
odsign
memorizza i digest dei file degli artefatti in un file separato denominato
odsign.info
. Per assicurarsi che odsign.info
non sia manomesso, odsign.info
è firmato con una chiave di firma che ha importanti proprietà di sicurezza. In
particolare, la chiave può essere generata e utilizzata solo durante l'avvio anticipato, momento in cui
viene eseguito solo il 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
lo fa verificando i digest dei file. Innanzitutto, verifica la firma di odsign.info
. Se la firma è valida, 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 avvio che risolve i seguenti problemi di sicurezza:
- Che cosa impedisce a un malintenzionato di utilizzare la nostra chiave di firma per firmare la propria versione di
odsign.info
? - Che cosa impedisce a un malintenzionato di generare la propria chiave di firma e di utilizzarla
per firmare la propria versione di
odsign.info
?
Le chiavi 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 specifico. odsign
crea la propria
chiave di firma a un livello iniziale, quando è in esecuzione solo codice attendibile, protetta
tramite dm-verity
.
I livelli della 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 associate a un determinato livello di avvio. Ad esempio, se crei una chiave per il livello di avvio 10, questa 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 è associata a questo
livello di avvio. Prima di utilizzare una chiave per firmare gli artefatti, odsign
verifica che la
chiave sia associata al livello di avvio 30.
In questo modo, vengono impediti i due attacchi descritti in precedenza in questa sezione:
- Gli autori degli attacchi 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 autori degli attacchi non possono creare una nuova chiave perché, quando hanno la possibilità
di eseguire codice dannoso, il livello di avvio è aumentato oltre 30 e
Keystore si rifiuta di creare una nuova chiave con quel livello di avvio. Se un malintenzionato
crea una nuova chiave non associata al livello di avvio 30,
odsign
la rifiuta.
Keystore garantisce che il livello di avvio venga applicato correttamente. Le sezioni che seguono descrivono in dettaglio 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 avvio in modo diverso. Sui dispositivi con 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_BOOT
impostato su1
. Ciò significa che la chiave può essere utilizzata solo una 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, 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 questa chiave in un secondo momento durante l'avvio, perché si verifica sempre almeno una transizione di 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 decrittografata 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 a quelle 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 prime fasi di
avvio, quando non è in esecuzione codice non attendibile. Ciò fornisce un ulteriore livello di protezione: nell'implementazione di Keymaster 4.0, un 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 l'avvio iniziale.
Componente pubblico delle chiavi di firma attendibili
odsign
recupera il componente della chiave pubblica della chiave di firma dall'archivio chiavi.
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 malintenzionato che compromette il file
system potrebbe modificare il database dell'archivio chiavi in modo che contenga una chiave pubblica che fa parte
di una coppia di chiavi pubblica/privata sotto il suo controllo.
Per impedire questo attacco, odsign
crea una chiave HMAC aggiuntiva con lo stesso
livello di avvio della chiave di firma. Poi, quando viene creata la chiave di firma, odsign
utilizza questa chiave HMAC per creare una firma della chiave pubblica e la memorizza
su disco. Nei riavvii successivi, quando recupera la chiave pubblica della chiave di firma, utilizza la chiave HMAC per verificare che la firma sul disco corrisponda alla firma della chiave pubblica recuperata. Se corrispondono, la chiave pubblica è attendibile perché la chiave HMAC può essere utilizzata solo nei livelli di avvio iniziali e quindi non può essere stata creata da un malintenzionato.