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:
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:
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'operazioneblk_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'operazioneblk_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 davold
per generare nuove chiavi di archiviazione da utilizzare con Android, mentre la funzionalità "importa" viene utilizzata davts_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 davold
che davts_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 parametrofileencryption
. Ad esempio, usafileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Per maggiori dettagli, consulta la documentazione di FBE. - Crittografia dei metadati: aggiungi il flag
wrappedkey_v0
al parametrometadata_encryption
. Ad esempio, usametadata_encryption=:wrappedkey_v0
. Per ulteriori dettagli, consulta la documentazione sulla crittografia dei metadati.