Implementazione di dm-verity

Android 4.4 e versioni successive supportano l'Avvio verificato tramite la funzionalità facoltativa del kernel device-mapper-verity (dm-verity), che fornisce un controllo dell'integrità trasparente dei dispositivi di blocco. dm-verity contribuisce a impedire la presenza di rootkit persistenti che possono mantenere i privilegi di root e compromettere i dispositivi. Questo aiuta gli utenti Android ad assicurarsi che quando si avviano un dispositivo si trovi nello stesso utilizzato per l'ultima volta.

Le applicazioni potenzialmente dannose con privilegi di root possono essere nascoste da dei programmi di rilevamento delle minacce e altrimenti mascherarsi. Il software di rooting può perché spesso sono più privilegiati dei rilevatori, abilitando il software per "mentire" ai programmi di rilevamento.

La funzionalità dm-verity consente di esaminare un dispositivo a blocchi, lo spazio di archiviazione livello del file system e determinare se corrisponde al livello previsto configurazione. Per farlo, utilizza una struttura ad albero di hash crittografico. Per ogni blocco (in genere 4 kB), è presente un hash SHA256.

Poiché i valori hash sono memorizzati in una struttura di pagine, solo lo stato "radice" l'hashing deve essere considerato attendibile per verificare il resto dell'albero. La possibilità di modificare un blocco equivale a rompere l'hash di crittografia. Consulta il seguente diagramma per una rappresentazione di questa struttura.

dm-verity-hash-table

Figura 1. Tabella hash dm-verity

Nella partizione di avvio è inclusa una chiave pubblica, che deve essere verificata da parte del produttore del dispositivo. Questa chiave viene utilizzata per verificare la firma per l'hash e confermare che la partizione di sistema del dispositivo sia protetta e invariata.

Funzionamento

La protezione dm-verity risiede nel kernel. Quindi se il software di rooting compromette sistema prima che appaia il kernel, mantiene quell'accesso. Per ridurre questo rischio, la maggior parte dei produttori verifica il kernel utilizzando una chiave incorporata nel dispositivo. Questa chiave non è modificabile una volta che il dispositivo esce dalla fabbrica.

I produttori usano questa chiave per verificare la firma al primo livello bootloader, che a sua volta verifica la firma nei livelli successivi, bootloader dell'app e infine il kernel. Ogni produttore che vuole usufruire dell'avvio verificato deve avere un metodo per verificare l'integrità del kernel. Supponendo che il kernel sia stato verificato, può esaminare un dispositivo di blocco e verificarlo durante il montaggio.

Un modo per verificare un dispositivo a blocchi è eseguire l'hashing diretto dei suoi contenuti e confrontare in un valore memorizzato. Tuttavia, il tentativo di verificare un intero dispositivo di blocco può richiedere un periodo di tempo prolungato e consumare molta energia del dispositivo. I dispositivi impiegavano molto tempo per l'avvio e si scaricavano notevolmente prima dell'uso.

Dm-verity verifica invece i blocchi singolarmente e solo quando o rifiutano le richieste in base all'organizzazione a cui si accede. Quando viene letto in memoria, il blocco viene sottoposto ad hashing in parallelo. L'hash viene poi verificato nell'albero. E siccome leggere il blocco è così costoso operativa, la latenza introdotta da questa verifica a livello di blocco è relativamente nominali.

Se la verifica non va a buon fine, il dispositivo genera un errore di I/O che indica il blocco non possono essere letti. Sembra che il file system sia stato danneggiato, come previsto.

Le app possono scegliere di procedere senza i dati risultanti, ad esempio quando non sono necessari per la funzione principale dell'app. Tuttavia, se l'app non può continuare senza i dati, l'operazione non va a buon fine.

Correzione degli errori in avanti

Android 7.0 e versioni successive migliora la robustezza della verifica dm con errori di inoltro di correzione (FEC). L'implementazione AOSP inizia con il codice di correzione degli errori Reed-Solomon comune e applica una tecnica chiamata interlacciamento per ridurre l'overhead dello spazio e aumentare il numero di blocchi danneggiati che possono essere recuperati. Per ulteriori dettagli sulla FEC, vedi Avvio verificato rigorosamente con correzione errori.

Implementazione

Riepilogo

  1. Genera un'immagine di sistema ext4.
  2. Genera una struttura ad albero di hash per l'immagine.
  3. Crea una tabella dm-verity per l'albero di hash.
  4. Firma la tabella dm-verity per produrre una tabella firma.
  5. Raggruppa la firma della tabella e la tabella dm-verity nei metadati di Verity.
  6. Concatena l'immagine di sistema, i metadati di Verity e l'albero di hash.

Per una descrizione dettagliata dell'albero di hash e della tabella dm-verity, consulta la pagina The Chromium Projects - Verified Boot.

Genera l'albero di hash

Come descritto nell'introduzione, l'albero di hash è parte integrante della dm-verity. Lo strumento cryptsetup genera un albero di hash per te. In alternativa, ne viene definito uno compatibile qui:

<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>

Per formare l'hash, l'immagine di sistema viene suddivisa a livello 0 in blocchi 4K, a ciascuno viene assegnato un hash SHA256. Il livello 1 si crea unendo solo gli hash SHA256 in blocchi di 4000 pixel, che risultano in un'immagine molto più piccola. Il livello 2 viene formato in modo identico, con gli hash SHA256 del livello 1.

Questo viene fatto finché gli hash SHA256 del livello precedente non rientrano in un singolo bloccare. Quando ottieni l'SHA256 di quel blocco, hai l'hash principale dell'albero.

La dimensione dell'albero di hash (e l'utilizzo dello spazio su disco corrispondente) varia con delle dimensioni della partizione verificata. In pratica, la dimensione degli alberi di hashing tende a essere piccoli, spesso inferiori a 30 MB.

Se in uno strato è presente un blocco che non è completamente riempito naturalmente dal hash dello strato precedente, dovresti riempirlo con zeri per ottenere previsto 4K. Ciò consente di sapere che l'albero di hash non è stato rimosso e completa invece con dati vuoti.

Per generare l'albero di hash, concatena gli hash del livello 2 su quelli del livello 1, il livello 3 gli hash su quelli del livello 2 e così via. Scrivi tutto questo su disco. Tieni presente che questo non fa riferimento al livello 0 dell'hash principale.

Ricapitolando, l'algoritmo generale per costruire l'albero di hash è il seguente:

  1. Scegli un sale casuale (codifica esadecimale).
  2. Annulla l'analisi dell'immagine di sistema in blocchi da 4.000.
  3. Per ogni blocco, ottieni l'hash SHA256 (salato).
  4. Concatena questi hash per formare un livello
  5. Inserisci zeri nel livello fino a un confine di blocchi di 4K.
  6. Collega il livello all'albero di hash.
  7. Ripeti i passaggi 2-6 usando il livello precedente come sorgente per quello successivo, di avere un solo hash.

Il risultato è un singolo hash, ovvero l'hash principale. Questo valore e il tuo valore di salt vengono utilizzati durante la creazione della tabella di mappatura di dm-verity.

Crea la tabella di mappatura dm-verity

Crea la tabella di mappatura dm-verity, che identifica il dispositivo a blocchi (o target) il kernel e la posizione dell'albero di hash (che è lo stesso valore). Questo il mapping viene utilizzato per la generazione e l'avvio di fstab. La tabella identifica inoltre la dimensione dei blocchi e hash_start, la posizione iniziale dell'albero di hash (nello specifico, il numero di blocco dall'inizio dell'immagine).

Per una descrizione dettagliata dei campi della tabella di mappatura dei target di Verity, consulta cryptsetup.

Firma la tabella dm-verity

Firma la tabella dm-verity per produrre una firma della tabella. Durante la verifica di un , la firma della tabella viene convalidata per prima. Questo avviene sulla base di una chiave l'immagine di avvio in una posizione fissa. Le chiavi sono generalmente incluse nel dei produttori creare sistemi per l'inclusione automatica sui dispositivi in un in ogni località.

Per verificare la partizione con questa combinazione di firma e chiave:

  1. Aggiungi una chiave RSA-2048 in formato compatibile con libmincrypt alla partizione /boot in /verity_key. Identifica la posizione della chiave utilizzata per la verifica l'albero hash.
  2. Nel file fstab relativo alla voce pertinente, aggiungi verify ai flag fs_mgr.

Raggruppa la firma della tabella nei metadati

Raggruppa la firma della tabella e la tabella dm-verity nei metadati di Verity. L'intero blocco di metadati è sottoposto a controllo della versione, pertanto può essere esteso, ad esempio per aggiungere un secondo tipo di firma o modificare l'ordine.

Come controllo di integrità, a ogni insieme di metadati della tabella viene associato un numero magico che aiuta a identificare la tabella. Poiché la lunghezza è inclusa nel sistema ext4, intestazione immagine, che offre un modo per cercare i metadati senza conoscere i contenuti dei dati stessi.

In questo modo ti assicuri di non aver scelto di verificare una partizione non verificata. Se sì, l'assenza di questo numero magico interrompe la procedura di verifica. Questo numero è simile a 0xb001b001.

I valori in esadecimale dei byte sono:

  • primo byte = b0
  • secondo byte = 01
  • terzo byte = b0
  • quarto byte = 01

Il seguente diagramma illustra la suddivisione dei metadati di verifica:

<magic number>|<version>|<signature>|<table length>|<table>|<padding>
\-------------------------------------------------------------------/
\----------------------------------------------------------/   |
                            |                                  |
                            |                                 32K
                       block content

Questa tabella descrive i campi dei metadati.

Tabella 1. Campi dei metadati di verifica

Campo Finalità Dimensioni Valore
numero magico usato da fs_mgr come controllo di integrità 4 byte 0xb001b001
versione utilizzato per eseguire la versione del blocco dei metadati 4 byte attualmente 0
firma la firma della tabella in PKCS1.5 modulo riempito 256 byte
lunghezza tabella la lunghezza della tabella dm-verity in byte 4 byte
tavolo la tabella dm-verity descritta in precedenza byte di lunghezza tabella
padding questa struttura ha una lunghezza da 0 riempita fino a 32 KB 0

Ottimizza dm-verity

Per ottenere il massimo delle prestazioni da dm-verity, devi:

  • Nel kernel, attiva NEON SHA-2 per ARMv7 e le estensioni SHA-2 per ARMv8.
  • Prova diverse impostazioni di lettura anticipata e prefetch_cluster per trovare la configurazione migliore per il tuo dispositivo.