Chiavi avvolte in hardware

Come la maggior parte dei software di crittografia di dischi e file, la crittografia dell'archiviazione di Android si basa tradizionalmente sulle chiavi di crittografia non elaborate presenti nella memoria di sistema in modo che la crittografia possa essere eseguita. Anche quando la crittografia viene eseguita da hardware dedicato anziché da software, il software in genere deve ancora gestire le chiavi di crittografia non elaborate.

Questo tradizionalmente non è visto come un problema perché le chiavi non saranno presenti durante un attacco offline, che è il tipo principale di attacco contro il quale la crittografia dell'archiviazione intende proteggere. Tuttavia, si desidera fornire una maggiore protezione contro altri tipi di attacchi, come attacchi di avvio a freddo e attacchi online in cui un utente malintenzionato potrebbe essere in grado di perdere memoria di sistema senza compromettere completamente il dispositivo.

Per risolvere questo problema, Android 11 ha introdotto il supporto per chiavi avvolte in hardware, dove è presente il supporto hardware. Le chiavi con wrapping hardware sono chiavi di archiviazione note solo in forma grezza all'hardware dedicato; il software vede e funziona solo con queste chiavi in ​​formato wrapping (crittografato). Questo hardware deve essere in grado di generare e importare chiavi di archiviazione, avvolgere chiavi di archiviazione in forme effimere ea lungo termine, derivare sottochiavi, programmare direttamente una sottochiave in un motore crittografico inline e restituire una sottochiave separata al software.

Nota : un motore di crittografia in linea (o hardware di crittografia in linea ) si riferisce all'hardware che crittografa / decrittografa i dati mentre è in viaggio verso / dal dispositivo di archiviazione. Di solito si tratta di un controller host UFS o eMMC che implementa le estensioni crittografiche definite dalla specifica JEDEC corrispondente.

Design

Questa sezione presenta il design della funzionalità dei tasti avvolti in hardware, incluso il supporto hardware richiesto per essa. Questa discussione si concentra sulla crittografia basata su file (FBE), ma la soluzione si applica anche alla crittografia dei metadati .

Un modo per evitare di aver bisogno delle chiavi di crittografia grezze nella memoria di sistema sarebbe tenerle solo negli slot di chiavi di un motore di crittografia in linea. Tuttavia, questo approccio presenta alcuni problemi:

  • Il numero di chiavi di crittografia può superare il numero di slot di chiavi.
  • I motori di crittografia in linea possono essere utilizzati solo per crittografare / decrittografare interi blocchi di dati su disco. Tuttavia, nel caso di FBE, il software deve ancora essere in grado di eseguire altre operazioni crittografiche come la crittografia dei nomi di file e la derivazione di identificatori di chiavi. Il software avrebbe comunque bisogno di accedere alle chiavi FBE non elaborate per eseguire quest'altro lavoro.

Per evitare questi problemi, le chiavi di archiviazione vengono invece trasformate in chiavi avvolte in hardware , che possono essere scartate e utilizzate solo da hardware dedicato. Ciò consente di supportare un numero illimitato di chiavi. Inoltre, la gerarchia delle chiavi viene modificata e parzialmente spostata su questo hardware, il che consente di restituire una sottochiave al software per attività che non possono utilizzare un motore di crittografia in linea.

Gerarchia delle chiavi

Le chiavi possono essere derivate da altre chiavi utilizzando una KDF (funzione di derivazione della chiave) come HKDF , risultando in una gerarchia di chiavi .

Il diagramma seguente illustra una tipica gerarchia di chiavi per FBE quando non vengono utilizzate chiavi avvolte in hardware:

Gerarchia delle chiavi FBE (standard)
Figura 1. Gerarchia delle chiavi FBE (standard)

La chiave della classe FBE è la chiave di crittografia non elaborata che Android passa al kernel Linux per sbloccare un particolare set di directory crittografate, come l'archiviazione crittografata con credenziali per un particolare utente Android. (Nel kernel, questa chiave è chiamata chiave master fscrypt .) Da questa chiave, il kernel deriva le seguenti sottochiavi:

  • L'identificatore della chiave. Non viene utilizzato per la crittografia, ma piuttosto è un valore utilizzato per identificare la chiave con cui è protetto un particolare file o directory.
  • La chiave di crittografia del contenuto del file
  • La chiave di crittografia dei nomi di file

Al contrario, il diagramma seguente illustra la gerarchia delle chiavi per FBE quando vengono utilizzate chiavi avvolte in hardware:

Gerarchia delle chiavi FBE (con chiave avvolta in hardware)
Figura 2. Gerarchia delle chiavi FBE (con chiave avvolta in hardware)

Rispetto al caso precedente, è stato aggiunto un ulteriore livello alla gerarchia delle chiavi e la chiave di crittografia del contenuto del file è stata riposizionata. Il nodo radice rappresenta ancora la chiave che Android passa a Linux per sbloccare un set di directory crittografate. Tuttavia, ora quella chiave è in una forma temporanea e per essere utilizzata deve essere passata a un hardware dedicato. Questo hardware deve implementare due interfacce che accettano una chiave avvolta in modo temporaneo:

  • Un'interfaccia per derivare inline_encryption_key e programmarla direttamente in uno slot per chiavi del motore di crittografia inline. Ciò consente di crittografare / decrittografare il contenuto del file senza che il software abbia accesso alla chiave non elaborata. Nei kernel comuni di Android, questa interfaccia corrisponde all'operazione blk_ksm_ll_ops::keyslot_program , che deve essere implementata dal driver di archiviazione.
  • Un'interfaccia per derivare e restituire sw_secret ("software secret" - chiamato anche "raw secret" in alcuni punti), che è la chiave che Linux usa per derivare le sottochiavi per tutto tranne che per la crittografia del contenuto dei file. Nei kernel comuni di Android, questa interfaccia corrisponde all'operazione blk_ksm_ll_ops::derive_raw_secret , che deve essere implementata dal driver di archiviazione.

Per derivare inline_encryption_key e sw_secret dalla chiave di archiviazione non sw_secret , l'hardware deve utilizzare un KDF crittograficamente potente. Questo KDF deve seguire le migliori pratiche di crittografia; deve avere una forza di sicurezza di almeno 256 bit, cioè sufficiente per qualsiasi algoritmo utilizzato in seguito. Inoltre, deve utilizzare un'etichetta distinta, un contesto e / o una stringa di informazioni specifica dell'applicazione quando si ricava ogni tipo di sottochiave per garantire che le sottochiavi risultanti siano crittograficamente isolate, ovvero la conoscenza di una di esse non ne rivela altre. L'estensione della chiave non è richiesta, poiché la chiave di archiviazione non elaborata è già una chiave uniformemente casuale.

Tecnicamente, è possibile utilizzare qualsiasi KDF che soddisfi i requisiti di sicurezza. Tuttavia, a scopo di test, è necessario reimplementare lo stesso KDF nel codice di test. Attualmente, un KDF è stato rivisto e implementato; può essere trovato nel codice sorgente di vts_kernel_encryption_test . Si raccomanda che l'hardware utilizzi questo KDF, che utilizza NIST SP 800-108 "KDF in Counter Mode" con AES-256-CMAC come PRF. Nota che per essere compatibile, tutte le parti dell'algoritmo devono essere identiche, inclusa la scelta dei contesti KDF e delle etichette per ogni sottochiave.

Avvolgimento delle chiavi

Per soddisfare gli obiettivi di sicurezza delle chiavi con wrapping hardware, vengono definiti due tipi di wrapping delle chiavi:

  • Avvolgimento temporaneo : l'hardware crittografa la chiave grezza utilizzando una chiave che viene generata casualmente ad ogni avvio e non è esposta direttamente all'esterno dell'hardware.
  • Wrapping a lungo termine : l'hardware crittografa la chiave grezza utilizzando una chiave univoca e persistente incorporata nell'hardware che non è direttamente esposta all'esterno dell'hardware.

Tutte le chiavi passate al kernel Linux per sbloccare l'archiviazione vengono avvolte in modo temporaneo. Ciò garantisce che se un utente malintenzionato è in grado di estrarre una chiave in uso dalla memoria di sistema, tale chiave sarà inutilizzabile non solo fuori dal dispositivo, ma anche sul dispositivo dopo un riavvio.

Allo stesso tempo, Android deve ancora essere in grado di memorizzare una versione crittografata delle chiavi su disco in modo che possano essere sbloccate in primo luogo. Le chiavi non elaborate funzionerebbero per questo scopo. Tuttavia, è preferibile che le chiavi non elaborate non siano mai presenti nella memoria di sistema in modo che non possano mai essere estratte per essere utilizzate fuori dal dispositivo, anche se estratte al momento dell'avvio. Per questo motivo viene definito il concetto di avvolgimento a lungo termine.

Per supportare la gestione delle chiavi racchiuse in questi due modi diversi, l'hardware deve implementare le seguenti interfacce:

  • Interfacce per generare e importare chiavi di archiviazione, restituendole in forma impacchettata a lungo termine. Queste interfacce sono accessibili indirettamente tramite KeyMint e corrispondono al tag TAG_STORAGE_KEY KeyMint. L'abilità "generate" è usata da vold per generare nuove chiavi di archiviazione da usare da Android, mentre l'abilità "import" è usata da vts_kernel_encryption_test per importare chiavi di test.
  • Un'interfaccia per convertire una chiave di archiviazione con wrapping a lungo termine in una chiave di archiviazione con wrapping temporaneo. Corrisponde al metodo convertStorageKeyToEphemeral KeyMint. Questo metodo viene utilizzato sia da vold che da vts_kernel_encryption_test per sbloccare l'archiviazione.

L'algoritmo di wrapping della chiave è un dettaglio di implementazione, ma dovrebbe utilizzare un AEAD forte come AES-256-GCM con IV casuali.

Sono necessarie modifiche al software

AOSP ha già un framework di base per supportare le chiavi con wrapping hardware. Ciò include il supporto nei componenti dello spazio utente come vold , così come il supporto del kernel Linux in blk-crypto , fscrypt e dm-default-key .

Tuttavia, sono necessarie alcune modifiche specifiche dell'implementazione.

Cambiamenti di KeyMint

L'implementazione KeyMint del dispositivo deve essere modificata per supportare TAG_STORAGE_KEY e implementare il metodo convertStorageKeyToEphemeral .

In Keymaster, exportKey stato utilizzato al posto di convertStorageKeyToEphemeral .

Modifiche al kernel Linux

Il driver del kernel Linux per il motore di crittografia in linea del dispositivo deve essere modificato per impostare BLK_CRYPTO_FEATURE_WRAPPED_KEYS , fare in modo che le operazioni keyslot_program() e keyslot_evict() supportino la programmazione / rimozione di chiavi avvolte dall'hardware e implementare l'operazione derive_raw_secret() .

Testing

Sebbene la crittografia con chiavi avvolte in hardware sia più difficile da testare rispetto alla crittografia con chiavi standard, è comunque possibile eseguire il test importando una chiave di prova e reimplementando la derivazione della chiave eseguita dall'hardware. Questo è implementato in vts_kernel_encryption_test . Per eseguire questo test, esegui:

atest -v vts_kernel_encryption_test

Leggere il registro del test e verificare che i casi di test delle chiavi con wrapping hardware (ad es. FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy e DmDefaultKeyTest.TestHwWrappedKey ) non siano stati ignorati a causa del supporto per chiavi con wrapping hardware non rilevate, poiché i risultati del test verranno comunque "passati" in questo caso.

Abilitare

Una volta che il supporto della chiave con wrapping hardware del dispositivo funziona correttamente, puoi apportare le seguenti modifiche al file fstab del dispositivo per fare in modo che Android lo utilizzi per FBE e crittografia dei metadati:

  • FBE: aggiungi il flag wrappedkey_v0 al parametro fileencryption . Ad esempio, usa fileencryption=::inlinecrypt_optimized+wrappedkey_v0 . Per ulteriori dettagli, vedere la documentazione di FBE .
  • Crittografia dei metadati: aggiungi il flag wrappedkey_v0 al parametro metadata_encryption . Ad esempio, utilizza metadata_encryption=:wrappedkey_v0 . Per ulteriori dettagli, vedere la documentazione sulla crittografia dei metadati .