Chiavi con crittografia hardware

Come la maggior parte dei software di crittografia di dischi e file, la crittografia dello spazio di 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 contro cui la crittografia dello spazio di archiviazione è progettata per proteggere. Tuttavia, si desidera fornire una maggiore protezione contro altri tipi di attacchi, come gli attacchi avvio a freddo e gli attacchi online in cui un aggressore potrebbe essere in grado di far trapelare la memoria di sistema senza compromettere completamente il dispositivo.

Per risolvere questo problema, Android 11 ha introdotto il supporto per le chiavi con wrapping hardware, dove è presente il supporto hardware. Le chiavi con 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 con 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 le sottochiavi, programmare direttamente una sottochiave in un motore di crittografia in linea 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 cripta/decripta i dati mentre sono in transito verso/da il dispositivo di archiviazione. In genere si tratta di un controller host UFS o eMMC che implementa le estensioni di crittografia definite dalla specifica JEDEC corrispondente.

Design

Questa sezione presenta il design della funzionalità delle chiavi con wrapping hardware, incluso il supporto hardware richiesto. 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 avere le chiavi di crittografia non elaborate nella memoria di sistema è mantenerle solo negli slot delle chiavi di un motore di crittografia in linea. Tuttavia, questo approccio presenta alcuni problemi:

  • Il numero di chiavi di crittografia potrebbe superare il numero di slot delle chiavi.
  • In genere, i motori di crittografia in linea perdono i contenuti dei relativi slot delle chiavi se il controller host di archiviazione viene reimpostato. La reimpostazione del controller host di archiviazione è una procedura standard di ripristino 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 delle chiavi senza l'intervento dell'utente.
  • I motori di crittografia in linea possono essere utilizzati solo per criptare/decriptare blocchi completi di dati sul disco. Tuttavia, nel caso di FBE, il software deve comunque essere in grado di eseguire altre operazioni di crittografia, come la crittografia dei nomi dei file e la derivazione degli identificatori delle chiavi. Il software avrebbe comunque bisogno dell'accesso alle chiavi FBE non elaborate per eseguire queste altre operazioni.

Per evitare questi problemi, le chiavi di archiviazione vengono invece trasformate in chiavi con wrapping hardware, che possono essere sottoposte a wrapping e utilizzate solo da hardware dedicato. In questo modo è possibile 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 le attività che non possono utilizzare un motore di crittografia in linea.

Gerarchia delle chiavi

Le chiavi possono essere derivate da altre chiavi utilizzando una funzione di derivazione delle chiavi (KDF), ad esempio HKDF, con conseguente gerarchia delle chiavi.

Il seguente diagramma illustra una tipica gerarchia delle chiavi per FBE quando le chiavi con wrapping hardware non vengono utilizzate:

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

La chiave di classe FBE è la chiave di crittografia non elaborata che Android passa al kernel Linux per sbloccare un determinato insieme di directory criptate, ad esempio lo spazio di archiviazione criptato con le credenziali per un determinato utente Android. (Nel kernel, questa chiave è chiamata chiave principale 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 è protetto un determinato file o directory.
  • La chiave di crittografia dei contenuti dei file
  • La chiave di crittografia dei nomi dei file

Al contrario, il seguente diagramma illustra la gerarchia delle chiavi per FBE quando vengono utilizzate le chiavi con wrapping hardware:

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

Rispetto al caso precedente, è stato aggiunto un livello aggiuntivo alla gerarchia delle chiavi e la chiave di crittografia dei contenuti dei file è stata spostata. 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 all'hardware dedicato. Questo hardware deve implementare due interfacce che accettano una chiave con wrapping effimero:

  • Un'interfaccia per derivare inline_encryption_key e programmarla direttamente in uno slot delle chiavi del motore di crittografia in linea. In questo modo, i contenuti dei file possono essere criptati/decriptati senza che il software abbia accesso alla chiave non elaborata. Nei kernel comuni di Android, questa interfaccia corrisponde all' blk_crypto_ll_ops::keyslot_program operazione, che deve essere implementata dal driver di archiviazione.
  • Un'interfaccia per derivare e restituire sw_secret ("secret software" -- in precedenza chiamato "raw secret"), che è la chiave utilizzata da Linux per derivare le sottochiavi per tutto ciò che non riguarda la crittografia dei contenuti dei file. Nei kernel comuni di Android, questa interfaccia corrisponde all' blk_crypto_ll_ops::derive_sw_secret operazione, 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 crittograficamente avanzata. Questa KDF deve seguire le best practice di crittografia; deve avere una forza di 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 crittograficamente isolate, ovvero la conoscenza di una di esse non rivela nessun'altra. Lo stretching delle chiavi 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 verificarne la correttezza. Per semplificare 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 con wrapping per scoprire come configurare il test di conseguenza.

Wrapping di chiavi

Per soddisfare gli obiettivi di sicurezza delle chiavi con wrapping hardware, vengono 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 persistente e univoca integrata nell'hardware che non è esposta direttamente all'esterno dell'hardware.

Tutte le chiavi passate al kernel Linux per sbloccare lo spazio di archiviazione sono con wrapping effimero. In questo modo, se un aggressore è in grado di 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 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 al di fuori del dispositivo, anche se estratte al momento dell'avvio. Per questo motivo, viene definito il concetto di wrapping a lungo termine.

Per supportare la gestione delle chiavi con 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. L'interfaccia generate viene utilizzata da vold per generare nuove chiavi di archiviazione da utilizzare in Android. L'interfaccia import 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 effimero. Questa interfaccia viene utilizzata sia da vold sia da vts_kernel_encryption_test per sbloccare lo spazio di archiviazione.

L'algoritmo di wrapping delle chiavi è un dettaglio di implementazione, ma deve utilizzare un AEAD avanzato come AES-256-GCM con IV casuali.

Modifiche software richieste

AOSP ha già un framework di base per il supporto delle chiavi con wrapping 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.

Modifiche al kernel Linux

Il driver del kernel Linux per il controller di archiviazione del dispositivo con supporto per la crittografia in linea deve essere modificato per supportare le chiavi con wrapping hardware.

Per i kernel android17 e versioni successive:

  • Imposta BLK_CRYPTO_KEY_TYPE_HW_WRAPPED in blk_crypto_profile::key_types_supported.
  • Fai in modo che blk_crypto_ll_ops::keyslot_program supporti la programmazione delle chiavi con wrapping hardware.
  • Fai in modo che blk_crypto_ll_ops::keyslot_evict supporti l'eliminazione delle chiavi con wrapping hardware.
  • Implement blk_crypto_ll_ops::derive_sw_secret, blk_crypto_ll_ops::import_key, blk_crypto_ll_ops::generate_key, and blk_crypto_ll_ops::prepare_key.

Per i kernel android14, android15 e android16:

  • Imposta BLK_CRYPTO_KEY_TYPE_HW_WRAPPED in blk_crypto_profile::key_types_supported.
  • Fai in modo che blk_crypto_ll_ops::keyslot_program supporti la programmazione delle chiavi con wrapping hardware.
  • Fai in modo che blk_crypto_ll_ops::keyslot_evict supporti l'eliminazione delle chiavi con wrapping hardware.
  • 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.
  • Fai in modo che blk_ksm_ll_ops::keyslot_program supporti la programmazione delle chiavi con wrapping hardware.
  • Fai in modo che blk_ksm_ll_ops::keyslot_evict supporti l'eliminazione delle chiavi con wrapping hardware.
  • Implementa blk_ksm_ll_ops::derive_raw_secret.

Per i kernel android11:

  • Imposta BLK_CRYPTO_FEATURE_WRAPPED_KEYS in keyslot_manager::features.
  • Fai in modo che keyslot_mgmt_ll_ops::keyslot_program supporti la programmazione delle chiavi con wrapping hardware.
  • Fai in modo che keyslot_mgmt_ll_ops::keyslot_evict supporti l'eliminazione delle chiavi con wrapping hardware.
  • Implementa keyslot_mgmt_ll_ops::derive_raw_secret.

Modifiche a KeyMint (legacy)

Nella versione attuale delle chiavi con wrapping hardware (wrappedkey), la generazione, l'importazione e la preparazione delle chiavi con wrapping hardware utilizzano gli ioctl del kernel Linux BLKCRYPTOGENERATEKEY, BLKCRYPTOIMPORTKEY e BLKCRYPTOPREPAREKEY. Questi ioctl corrispondono ai metodi in struct blk_crypto_ll_ops. Il driver di archiviazione implementa questi metodi e comunica con l'hardware di wrapping delle chiavi per eseguire l'operazione richiesta. Per ulteriori informazioni su questi ioctl, consulta la documentazione del kernel Linux..

Questi ioctl sono stati aggiunti in Linux 6.16. Sui dispositivi che non sono stati lanciati con la soluzione basata su ioctl, viene utilizzata una soluzione diversa che utilizza Android KeyMint (o in precedenza KeyMaster). La soluzione legacy (wrappedkey_v0) non è compatibile con il kernel Linux mainline o con la soluzione attuale. La soluzione legacy utilizza le seguenti funzionalità di KeyMint:

  • Supporto per TAG_STORAGE_KEY, sia per la generazione che per l'importazione delle chiavi.
  • Supporto per il metodo convertStorageKeyToEphemeral.

Questa funzionalità di KeyMint è necessaria solo sui dispositivi che utilizzano la soluzione legacy, corrispondente a wrappedkey_v0 nel file fstab.

I dispositivi che utilizzano la soluzione attuale, corrispondente a wrappedkey nel file fstab, non richiedono l'implementazione di questa funzionalità di KeyMint.

Testare le chiavi con wrapping

Sebbene la crittografia con chiavi con wrapping 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 delle chiavi eseguita dall'hardware. Questa operazione viene eseguita in vts_kernel_encryption_test. Per eseguire questo test:

atest -v vts_kernel_encryption_test

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

Per impostazione predefinita, vts_kernel_encryption_test presuppone che l'hardware implementi una KDF denominata 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 la specifica 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 da kdf2 a kdf4. Queste sono altrettanto sicure 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 una KDF diversa, imposta la proprietà di sistema ro.crypto.hw_wrapped_keys.kdf in PRODUCT_VENDOR_PROPERTIES sul nome della KDF come definito nel codice sorgente del test. In questo modo, vts_kernel_encryption_test cercherà questa KDF anziché kdf1. Ad esempio, per selezionare kdf2:

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 delle chiavi 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 (o wrappedkey_v0 per la versione legacy) al parametro fileencryption. Ad esempio, utilizza fileencryption=::inlinecrypt_optimized+wrappedkey. Per ulteriori dettagli, consulta la documentazione FBE.
  • Crittografia dei metadati: aggiungi il flag wrappedkey (o wrappedkey_v0 per la versione legacy) al metadata_encryption parametro. Ad esempio, utilizza metadata_encryption=:wrappedkey. Per ulteriori dettagli, consulta la documentazione sulla crittografia dei metadati.

In ogni caso, esistono due versioni del flag:

  • wrappedkey, supportato da Android 17 e versioni successive, attiva la versione attuale delle chiavi con wrapping hardware. Questa versione è compatibile con il kernel Linux mainline.
  • wrappedkey_v0, supportato da Android 11 e versioni successive, attiva la versione legacy delle chiavi con wrapping hardware. Questa versione non è compatibile con il kernel Linux mainline. Esegue il proxy di determinate operazioni tramite KeyMint e utilizza un formato su disco non standard. Per ulteriori informazioni, consulta Modifiche a KeyMint (legacy).

Sui dispositivi lanciati con Android 17 o versioni successive, preferisci wrappedkey.

Sui dispositivi già lanciati con wrappedkey_v0, continua a utilizzare wrappedkey_v0 per la compatibilità con le versioni precedenti.