Google si impegna a promuovere l'equità razziale per le comunità nere. Vedi come.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Implementazione di dm-verity

Android 4.4 e versioni successive supportano l'avvio verificato tramite la funzionalità opzionale del kernel Device-Mapper-Verity (dm-verity), che fornisce un controllo trasparente dell'integrità dei dispositivi a blocchi. dm-verity aiuta a prevenire i rootkit persistenti che possono trattenere i privilegi di root e compromettere i dispositivi. Questa funzione consente agli utenti Android di essere sicuri che all'avvio di un dispositivo sia nello stesso stato di quando è stato utilizzato l'ultima volta.

Le applicazioni potenzialmente dannose (PHA) con privilegi di root possono nascondersi dai programmi di rilevamento e mascherarsi in altro modo. Il software di rooting può farlo perché è spesso più privilegiato rispetto ai rilevatori, consentendo al software di "mentire" ai programmi di rilevamento.

La funzione dm-verity consente di esaminare un dispositivo a blocchi, il livello di archiviazione sottostante del file system e determinare se corrisponde alla sua configurazione prevista. Lo fa usando un albero hash crittografico. Per ogni blocco (in genere 4k), esiste un hash SHA256.

Poiché i valori di hash sono memorizzati in un albero di pagine, è necessario affidarsi solo all'hash "root" di livello superiore per verificare il resto dell'albero. La capacità di modificare uno qualsiasi dei blocchi equivarrebbe a rompere l'hash crittografico. Vedi il diagramma seguente per una rappresentazione di questa struttura.

dm-Verity-hash-table

Figura 1. Tabella hash dm-verity

Una chiave pubblica è inclusa nella partizione di avvio, che deve essere verificata esternamente dal produttore del dispositivo. Tale chiave viene utilizzata per verificare la firma per quell'hash e confermare che la partizione di sistema del dispositivo è protetta e invariata.

operazione

la protezione di dm-verity vive nel kernel. Quindi, se il software di rooting compromette il sistema prima che arrivi il kernel, manterrà tale accesso. Per mitigare questo rischio, la maggior parte dei produttori verifica il kernel utilizzando una chiave inserita nel dispositivo. Quella chiave non è modificabile una volta che il dispositivo esce dalla fabbrica.

I produttori utilizzano quella chiave per verificare la firma sul bootloader di primo livello, che a sua volta verifica la firma sui livelli successivi, il bootloader dell'applicazione e infine il kernel. Ogni produttore che desidera sfruttare l' avvio verificato dovrebbe avere un metodo per verificare l'integrità del kernel. Supponendo che il kernel sia stato verificato, il kernel può guardare un dispositivo a blocchi e verificarlo mentre è montato.

Un modo per verificare un dispositivo a blocchi consiste nell'hash direttamente i suoi contenuti e confrontarli con un valore memorizzato. Tuttavia, il tentativo di verificare un intero dispositivo a blocchi può richiedere un lungo periodo e consumare gran parte della potenza di un dispositivo. I dispositivi impiegherebbero lunghi periodi per avviarsi e quindi essere significativamente svuotati prima dell'uso.

Invece, dm-verity verifica i blocchi singolarmente e solo quando si accede a ciascuno di essi. Quando viene letto in memoria, il blocco viene sottoposto a hash in parallelo. L'hash viene quindi verificato sull'albero. E poiché leggere il blocco è un'operazione così costosa, la latenza introdotta da questa verifica a livello di blocco è relativamente nominale.

Se la verifica fallisce, il dispositivo genera un errore I / O che indica che il blocco non può essere letto. Sembrerà che il filesystem sia stato corrotto, come previsto.

Le applicazioni possono scegliere di procedere senza i dati risultanti, ad esempio quando tali risultati non sono richiesti per la funzione primaria dell'applicazione. Tuttavia, se l'applicazione non può continuare senza i dati, fallirà.

Correzione dell'errore in avanti

Android 7.0 e versioni successive migliorano la robustezza di dm-verity con la correzione degli errori in avanti (FEC). L'implementazione AOSP inizia con il codice di correzione degli errori Reed-Solomon comune e applica una tecnica chiamata interleaving per ridurre l'overhead dello spazio e aumentare il numero di blocchi danneggiati che possono essere recuperati. Per ulteriori dettagli su FEC, vedere Avvio verificato rigorosamente applicato con correzione degli errori .

Implementazione

Sommario

  1. Genera un'immagine di sistema ext4.
  2. Genera un albero di hash per quell'immagine.
  3. Costruisci una tabella dm-verity per quell'albero hash.
  4. Firma quella tabella dm-verity per produrre una firma della tabella.
  5. Raggruppa la firma della tabella e la tabella dm-verity in metadati di verità.
  6. Concatena l'immagine di sistema, i metadati di verità e l'albero di hash.

Consulta The Chromium Projects - Verified Boot per una descrizione dettagliata dell'albero hash e della tabella dm-verity.

Generare l'albero di hash

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

<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 al livello 0 in blocchi da 4k, a ciascuno assegnato un hash SHA256. Lo strato 1 è formato unendo solo quegli hash SHA256 in blocchi da 4k, ottenendo un'immagine molto più piccola. Lo strato 2 è formato in modo identico, con gli hash SHA256 dello strato 1.

Questo viene fatto fino a quando gli hash SHA256 del layer precedente non possono rientrare in un singolo blocco. Quando ottieni lo SHA256 di quel blocco, hai l'hash radice dell'albero.

La dimensione dell'albero hash (e l'utilizzo dello spazio su disco corrispondente) varia in base alla dimensione della partizione verificata. In pratica, la dimensione degli alberi di hash tende ad essere piccola, spesso inferiore a 30 MB.

Se hai un blocco in un livello che non è completamente riempito naturalmente dagli hash del livello precedente, dovresti riempirlo di zeri per ottenere il 4k previsto. Ciò consente di sapere che l'albero di hash non è stato rimosso e viene invece completato con dati vuoti.

Per generare l'albero di hash, concatenare gli hash di livello 2 su quelli per lo strato 1, lo strato 3 gli hash su quelli dello strato 2 e così via. Scrivi tutto questo sul disco. Nota che questo non fa riferimento al livello 0 dell'hash radice.

Per ricapitolare, l'algoritmo generale per costruire l'albero hash è il seguente:

  1. Scegli un salt casuale (codifica esadecimale).
  2. Elimina l'immagine del tuo sistema in blocchi da 4k.
  3. Per ogni blocco, ottieni l'hash SHA256 (salato).
  4. Concatena questi hash per formare un livello
  5. Riempi il livello con 0s a un limite di blocco 4k.
  6. Concatena il livello al tuo albero di hash.
  7. Ripeti i passaggi 2-6 utilizzando il livello precedente come sorgente per il successivo fino a quando non hai un solo hash.

Il risultato di questo è un singolo hash, che è il tuo hash di root. Questo e il tuo sale vengono utilizzati durante la costruzione della tabella di mappatura dm-verity.

Creazione della tabella di mappatura dm-verity

Costruisci la tabella di mappatura dm-verity, che identifica il dispositivo a blocchi (o destinazione) per il kernel e la posizione dell'albero di hash (che è lo stesso valore.) Questa mappatura viene utilizzata per la generazione e l'avvio di fstab . La tabella identifica anche la dimensione dei blocchi e hash_start, la posizione iniziale dell'albero di hash (in particolare, il suo numero di blocco dall'inizio dell'immagine).

Vedi cryptsetup per una descrizione dettagliata dei campi della tabella di mappatura del target di verità.

Firma della tabella dm-verity

Firma la tabella dm-verity per produrre una firma della tabella. Quando si verifica una partizione, la firma della tabella viene convalidata per prima. Questo viene fatto contro una chiave dell'immagine di avvio in una posizione fissa. Le chiavi sono generalmente incluse nei sistemi di costruzione dei produttori per l'inclusione automatica sui dispositivi in ​​una posizione fissa.

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 . Identificare la posizione della chiave utilizzata per verificare l'albero hash.
  2. Nell'fstab per la voce pertinente, aggiungi verify ai flag fs_mgr .

Raggruppamento della firma della tabella in metadati

Raggruppa la firma della tabella e la tabella dm-verity in metadati di verità. L'intero blocco di metadati è sottoposto a versione in modo che possa essere esteso, ad esempio per aggiungere un secondo tipo di firma o modificare alcuni ordini.

Come controllo di integrità, un numero magico è associato a ciascun set di metadati della tabella che consente di identificare la tabella. Poiché la lunghezza è inclusa nell'intestazione dell'immagine di sistema ext4, questo fornisce un modo per cercare i metadati senza conoscere il contenuto dei dati stessi.

Questo assicura che non hai scelto di verificare una partizione non verificata. In tal caso, l'assenza di questo numero magico interromperà il processo di verifica. Questo numero ricorda:
0xb001b001

I valori byte in esadecimale sono:

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

Il diagramma seguente mostra la suddivisione dei metadati di verità:

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

E questa tabella descrive quei campi di metadati.

Tabella 1. Campi dei metadati di Verity

Campo Scopo Taglia Valore
numero magico utilizzato da fs_mgr come controllo di integrità 4 byte 0xb001b001
versione utilizzato per la versione del blocco di metadati 4 byte attualmente 0
firma la firma del tavolo in forma imbottita PKCS1.5 256 byte
lunghezza del tavolo la lunghezza della tabella dm-verity in byte 4 byte
tavolo la tabella dm-verity descritta in precedenza byte di lunghezza della tabella
imbottitura questa struttura è imbottita da 0 a 32k di lunghezza 0

Ottimizzare dm-verity

Per ottenere le migliori prestazioni da dm-verity, dovresti:

  • Nel kernel, attiva NEON SHA-2 per ARMv7 e le estensioni SHA-2 per ARMv8.
  • Sperimenta diverse impostazioni read-ahead e prefetch_cluster per trovare la migliore configurazione per il tuo dispositivo.