Chiavi con crittografia hardware

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

Tradizionalmente, questo non è considerato un problema perché le chiavi non sono presenti durante un attacco offline, che è il tipo principale di attacco da cui la crittografia dell'archiviazione è destinata a proteggere. Tuttavia, si vuole fornire una maggiore protezione contro altri tipi di attacchi, come gli attacchi cold boot e gli attacchi online in cui un malintenzionato potrebbe essere in grado di divulgare la memoria di sistema senza compromettere completamente il dispositivo.

Per risolvere questo problema, Android 11 ha introdotto il supporto per le chiavi protette dall'hardware, in cui è presente il supporto hardware. Le chiavi sottoposte a wrapping hardware sono chiavi di archiviazione note in formato non elaborato solo all'hardware dedicato; il software vede e utilizza queste chiavi solo in formato sottoposto a wrapping (criptato). Questo hardware deve essere in grado di generare e importare chiavi di archiviazione, eseguire il wrapping delle chiavi di archiviazione in forme effimere e a lungo termine, derivare sottochiavi, programmare direttamente una sottochiave in un motore di crittografia in linea e restituire una sottochiave separata al software.

Nota:un motore di crittografia inline (o hardware di crittografia inline) si riferisce all'hardware che cripta/decripta i dati mentre sono in transito verso/da un dispositivo di archiviazione. Di solito si tratta di un controller host UFS o eMMC che implementa le estensioni di crittografia definite dalla specifica JEDEC corrispondente.

Design

Questa sezione presenta la progettazione della funzionalità di chiavi protette dall'hardware, incluso il supporto hardware necessario. 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 dover utilizzare le chiavi di crittografia non elaborate nella memoria di sistema è conservarle solo negli slot delle chiavi di un motore di crittografia inline. Tuttavia, questo approccio presenta alcuni problemi:

  • Il numero di chiavi di crittografia potrebbe superare il numero di slot per le chiavi.
  • I motori di crittografia inline in genere perdono i contenuti dei relativi slot per le chiavi se il controller host di archiviazione viene reimpostato. Il ripristino del controller host di archiviazione è una procedura standard di recupero degli errori che viene eseguita se si verificano determinati tipi di errori di archiviazione e questi errori possono verificarsi in qualsiasi momento. Pertanto, quando viene utilizzata la crittografia in linea, il sistema operativo deve essere sempre pronto a riprogrammare gli slot per le chiavi senza l'intervento dell'utente.
  • I motori di crittografia incorporati possono essere utilizzati solo per criptare/decriptare blocchi completi di dati sul disco. Tuttavia, nel caso della crittografia basata su file, il software deve comunque essere in grado di eseguire altre operazioni crittografiche, come la crittografia dei nomi dei file e la derivazione degli identificatori delle chiavi. Il software avrebbe comunque bisogno di accedere alle chiavi FBE non elaborate per svolgere altre operazioni.

Per evitare questi problemi, le chiavi di archiviazione vengono invece trasformate in chiavi con wrapping hardware, che possono essere scoperte e utilizzate solo da hardware dedicato. In questo modo è possibile supportare un numero illimitato di chiavi. Inoltre, la gerarchia delle chiavi viene modificata e spostata parzialmente su questo hardware, il che consente di restituire una sottochiave al software per le attività che non possono utilizzare un motore di crittografia inline.

Gerarchia delle chiavi

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

Il seguente diagramma mostra una tipica gerarchia delle chiavi per FBE quando non vengono utilizzate chiavi protette dall'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 determinato insieme di directory criptate, ad esempio l'archivio criptato con le credenziali per un determinato 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 è un valore utilizzato per identificare la chiave con cui un determinato file o directory è protetto.
  • Chiave di crittografia dei contenuti del file
  • Chiave di crittografia dei nomi dei file

Al contrario, il seguente diagramma mostra la gerarchia delle chiavi per la crittografia basata su file quando vengono utilizzate chiavi protette dall'hardware:

Gerarchia delle chiavi FBE (con chiave con wrapping hardware)
Figura 2. Gerarchia delle chiavi FBE (con chiave protetta dall'hardware)

Rispetto al caso precedente, è stato aggiunto un livello aggiuntivo alla gerarchia delle chiavi e la chiave di crittografia dei contenuti dei file è stata riassegnata. Il nodo radice rappresenta ancora la chiave che Android passa a Linux per sbloccare un insieme di directory criptate. Tuttavia, ora la chiave è in formato con wrapping effimero e per essere utilizzata deve essere passata a un hardware dedicato. Questo hardware deve implementare due interfacce che accettano una chiave con wrapping temporaneo:

  • Un'unica interfaccia per derivare inline_encryption_key e programmarlo direttamente in uno slot per chiavi del motore di crittografia inline. Ciò consente di criptare/decriptare i contenuti dei file senza che il software abbia accesso alla chiave non elaborata. Nei kernel comuni di Android, questa interfaccia corrisponde all'operazione blk_crypto_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), ovvero la chiave che Linux utilizza per derivare le chiavi secondarie per tutto ciò che non riguarda la crittografia dei contenuti dei file. Nei kernel comuni di Android, questa interfaccia corrisponde all'operazione blk_crypto_ll_ops::derive_sw_secret, che deve essere implementata dal driver di archiviazione.

Per derivare inline_encryption_key e sw_secret dalla chiave di archiviazione non elaborata, l'hardware deve utilizzare una KDF con crittografia efficace. Questa KDF deve seguire le best practice di crittografia; deve avere una solidità della sicurezza di almeno 256 bit, ovvero sufficiente per qualsiasi algoritmo utilizzato in seguito. Inoltre, deve utilizzare un'etichetta e un contesto distinti quando deriva ogni tipo di sottochiave per garantire che le sottochiavi risultanti siano isolate crittograficamente, ovvero che la conoscenza di una di queste non riveli nessun'altra. Lo stretching della chiave non è necessario, poiché la chiave di archiviazione non elaborata è già una chiave casuale uniforme.

Tecnicamente, è possibile utilizzare qualsiasi KDF che soddisfi i requisiti di sicurezza. Tuttavia, a scopo di test, vts_kernel_encryption_test implementa la stessa KDF nel software per riprodurre il testo cifrato su disco e verificare che sia corretto. Per facilitare i test e garantire l'utilizzo di una KDF sicura e già esaminata, consigliamo all'hardware di implementare la KDF predefinita che il test verifica. Per l'hardware che utilizza una KDF diversa, consulta Testare le chiavi protette per scoprire come configurare il test di conseguenza.

Wrapping delle chiavi

Per soddisfare gli obiettivi di sicurezza delle chiavi protette dall'hardware, sono definiti due tipi di wrapping delle chiavi:

  • Wrapping effimero: l'hardware cripta la chiave non elaborata utilizzando una chiave generata in modo casuale a ogni avvio e non esposta direttamente all'esterno dell'hardware.
  • Wrapping a lungo termine: l'hardware cripta la chiave non elaborata utilizzando una chiave univoca e persistente integrata nell'hardware che non è direttamente esposta all'esterno dell'hardware.

Tutte le chiavi trasmesse al kernel Linux per sbloccare lo spazio di archiviazione sono incapsulate in modo effimero. In questo modo, se un malintenzionato riesce a estrarre una chiave in uso dalla memoria di sistema, questa chiave non è utilizzabile non solo al di fuori del dispositivo, ma anche sul dispositivo dopo un riavvio.

Allo stesso tempo, Android deve comunque essere in grado di archiviare una versione criptata delle chiavi sul disco in modo che possano essere sbloccate. Le chiavi non elaborate sono adatte a questo scopo. Tuttavia, è preferibile che le chiavi non siano mai presenti nella memoria di sistema, in modo che non possano mai essere estratte per essere utilizzate al di fuori del dispositivo, anche se estratte all'avvio. Per questo motivo, è stato definito il concetto di wrapping a lungo termine.

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

  • Interfacce per generare e importare chiavi di archiviazione, restituendole in formato con wrapping a lungo termine. Si accede a queste interfacce indirettamente tramite KeyMint e corrispondono al tag TAG_STORAGE_KEY KeyMint. La funzionalità "genera" viene utilizzata da vold per generare nuove chiavi di archiviazione da utilizzare con Android, mentre la funzionalità "importa" viene utilizzata da vts_kernel_encryption_test per importare le 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 lo spazio di archiviazione.

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

Sono necessarie modifiche al software

AOSP dispone già di un framework di base per il supporto delle chiavi con integrazione hardware. Ciò include il supporto nei componenti dello spazio utente come vold, nonché il supporto del kernel Linux in blk-crypto, fscrypt e dm-default-key.

Tuttavia, sono necessarie alcune modifiche specifiche per l'implementazione.

Modifiche a KeyMint

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

In Keymaster, è stato utilizzato exportKey al posto di convertStorageKeyToEphemeral.

Modifiche al kernel Linux

Il driver del kernel Linux per il motore di crittografia inline del dispositivo deve essere modificato per supportare le chiavi protette dall'hardware.

Per i kernel android14 e versioni successive, imposta BLK_CRYPTO_KEY_TYPE_HW_WRAPPED in blk_crypto_profile::key_types_supported, crea blk_crypto_ll_ops::keyslot_program e blk_crypto_ll_ops::keyslot_evict supporta la programmazione/l'espulsione di chiavi con wrapping hardware e implementa blk_crypto_ll_ops::derive_sw_secret.

Per i kernel android12 e android13, imposta BLK_CRYPTO_FEATURE_WRAPPED_KEYS in blk_keyslot_manager::features, crea blk_ksm_ll_ops::keyslot_program e blk_ksm_ll_ops::keyslot_evict supporta la programmazione/l'espulsione di chiavi con wrapping hardware e implementa blk_ksm_ll_ops::derive_raw_secret.

Per i kernel android11, imposta BLK_CRYPTO_FEATURE_WRAPPED_KEYS in keyslot_manager::features, crea keyslot_mgmt_ll_ops::keyslot_program e keyslot_mgmt_ll_ops::keyslot_evict supporta la programmazione/l'espulsione delle chiavi con wrapping hardware e implementa keyslot_mgmt_ll_ops::derive_raw_secret.

Testare le chiavi con wrapping

Sebbene la crittografia con chiavi protette dall'hardware sia più difficile da testare rispetto alla crittografia con chiavi non elaborate, è comunque possibile eseguire il test importando una chiave di test e reimplementando la derivazione della chiave eseguita dall'hardware. Questa funzionalità è implementata in vts_kernel_encryption_test. Per eseguire questo test, esegui:

atest -v vts_kernel_encryption_test

Leggi il log di test e verifica che gli scenari di test delle chiavi protette dall'hardware (ad esempio, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy e DmDefaultKeyTest.TestHwWrappedKey) non siano stati ignorati perché non è stato rilevato il supporto per le chiavi protette dall'hardware, in quanto in questo caso i risultati del test sono comunque "superati".

Per impostazione predefinita, vts_kernel_encryption_test presuppone che l'hardware implementi una KDF che chiama kdf1. Questa KDF appartiene alla famiglia di KDF in modalità contatore di NIST SP 800-108 e utilizza AES-256-CMAC come funzione pseudocasuale. Per ulteriori informazioni su CMAC, consulta le specifiche CMAC. La KDF utilizza contesti ed etichette specifici quando deriva ogni sottochiave. L'hardware deve implementare questa KDF, inclusa la scelta esatta di contesto, etichetta e formattazione della stringa di input fissa durante la derivazione di ogni sottochiave.

Tuttavia, vts_kernel_encryption_test implementa anche KDF aggiuntive kdf2 tramite kdf4. Sono altrettanto sicuri di kdf1 e differiscono solo per la scelta di contesti, etichette e formattazione della stringa di input fissa. Esistono solo per adattarsi a hardware diversi.

Per i dispositivi che utilizzano un KDF diverso, imposta la proprietà di sistema ro.crypto.hw_wrapped_keys.kdf in PRODUCT_VENDOR_PROPERTIES sul nome del KDF come definito nel codice sorgente del test. In questo modo, vts_kernel_encryption_test controlla la KDF anziché kdf1. Ad esempio, per selezionare kdf2, utilizza:

PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2

Per i dispositivi che utilizzano una KDF non supportata dal test, aggiungi anche un'implementazione di questa KDF al test e assegnale un nome univoco.

Attivare le chiavi con wrapping

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

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