Chiavi con wrapping hardware

Come la maggior parte dei software di crittografia di dischi e file, la crittografia dello spazio di archiviazione di Android tradizionalmente si basa sulle chiavi di crittografia non elaborate presenti nella memoria di sistema per eseguire la crittografia. 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 di attacco principale contro cui è intesa la crittografia dello spazio di archiviazione. Tuttavia, c'è la volontà di fornire una protezione maggiore contro altri tipi di attacchi, come gli attacchi di cold boot e gli attacchi online in cui un malintenzionato potrebbe essere in grado di rubare la memoria del sistema senza compromettere completamente il dispositivo.

Per risolvere questo problema, Android 11 ha introdotto il supporto per le chiavi con wrapping hardware, se è presente il supporto hardware. Le chiavi con wrapping hardware sono chiavi di archiviazione conosciute in forma non elaborata solo dall'hardware dedicato. Il software vede e utilizza queste chiavi solo in forma con wrapping (criptata). 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 in linea (o hardware di crittografia in linea) si riferisce all'hardware che cripta/decripta i dati mentre si spostano verso/dall'unità 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 illustra il design della funzionalità delle chiavi con wrapping 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 nelle keyslot di un motore di crittografia in linea. Tuttavia, questo approccio presenta alcuni problemi:

  • Il numero di chiavi di crittografia potrebbe superare il numero di keyslot.
  • I motori di crittografia in linea in genere perdono i contenuti degli slot delle chiavi se il controller di archiviazione (di solito UFS o eMMC) viene reimpostato. La reimpostazione del controller dello spazio di archiviazione è una procedura di recupero degli errori standard che viene eseguita in caso di determinati tipi di errori di archiviazione, che possono verificarsi in qualsiasi momento. Pertanto, quando viene utilizzata la crittografia in linea, il sistema operativo deve essere sempre pronto a riprogrammare le keyslot senza intervento dell'utente.
  • I motori di crittografia in linea possono essere utilizzati solo per criptare/decriptare blocchi completi di dati su disco. Tuttavia, nel caso della crittografia lato client, il software deve comunque essere in grado di eseguire altri compiti di crittografia, come la crittografia dei nomi file e l'ottenimento degli identificatori delle chiavi. Il software deve comunque accedere alle chiavi FBE non elaborate per svolgere quest'altra operazione.

Per evitare questi problemi, le chiavi di archiviazione vengono 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 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 in linea.

Gerarchia delle chiavi

Le chiavi possono essere derivate da altre chiavi utilizzando una KDF (funzione di derivazione delle chiavi) come HKDF, generando una gerarchia di chiavi.

Il seguente diagramma mostra una tipica gerarchia delle chiavi per la crittografia lato firmware quando non vengono utilizzate chiavi con wrapping 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 lo spazio di archiviazione con crittografia delle credenziali per un determinato utente Android. Nel kernel, questa chiave è denominata chiave master fscrypt. Da questa chiave, il kernel ricava le seguenti sottochiavi:

  • L'identificatore della chiave. Non viene utilizzato per la crittografia, ma è un valore utilizzato per identificare la chiave con cui sono protetti un determinato file o una determinata directory.
  • La chiave di crittografia dei contenuti del file
  • La chiave di crittografia dei nomi file

Al contrario, il seguente diagramma mostra la gerarchia delle chiavi per la crittografia lato client quando vengono utilizzate 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 principale rappresenta ancora la chiave che Android passa a Linux per sbloccare un insieme di directory criptate. Tuttavia, ora la chiave è in formato con wrapping temporaneo e per poter essere utilizzata deve essere passata a hardware dedicato. Questo hardware deve implementare due interfacce che accettano una chiave con wrapping temporaneo:

  • Un'interfaccia per ricavare inline_encryption_key e programmarlo direttamente in un keyslot 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'operazione blk_crypto_ll_ops::keyslot_program, che deve essere implementata dal driver di archiviazione.
  • Un'interfaccia per dedurre e restituire sw_secret ("software secret", chiamata anche "raw secret" in alcuni casi), che è la chiave che Linux utilizza per dedurre le sottochiavi per tutto ciò che non è 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 dedurre inline_encryption_key e sw_secret dalla chiave di archiviazione non elaborata, l'hardware deve utilizzare un KDF con una crittografia efficace. Questo KDF deve seguire le best practice di crittografia; deve avere un'efficacia di sicurezza di almeno 256 bit, ovvero sufficiente per qualsiasi algoritmo utilizzato in seguito. Deve inoltre utilizzare un'etichetta, un contesto e una stringa di informazioni specifiche dell'app distinte quando viene derivato ciascun tipo di sottochiave per garantire che le sottochiavi risultanti siano isolate crittograficamente, ovvero la conoscenza di una di esse non ne rivela altre. L'estensione della chiave non è necessaria, poiché la chiave di archiviazione non elaborata è già una chiave uniformemente casuale.

Tecnicamente, è possibile utilizzare qualsiasi file KDF che soddisfi i requisiti di sicurezza. Tuttavia, a scopo di test, è necessario implementare nuovamente lo stesso KDF nel codice di test. Attualmente, un KDF è stato esaminato e implementato; puoi trovarlo nel codice sorgente di vts_kernel_encryption_test. È consigliabile che l'hardware utilizzi questo KDF, che usa NIST SP 800-108 "KDF in Counter Mode" con AES-256-CMAC come PRF. Tieni presente che, per essere compatibili, tutte le parti dell'algoritmo devono essere identiche, inclusa la scelta dei contesti KDF e delle etichette per ogni sottochiave.

Wrapping di chiavi

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

  • wrapping temporaneo: 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 permanente univoca integrata nell'hardware che non è direttamente esposta all'esterno dell'hardware.

Tutte le chiavi passate al kernel Linux per sbloccare lo spazio di archiviazione vengono sottoposte a wrapping temporaneo. In questo modo, se un malintenzionato riesce a estrarre una chiave in uso dalla memoria di sistema, questa non sarà utilizzabile non solo quando il dispositivo è spento, ma anche 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 predefinite funzioneranno 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 all'avvio. Per questo motivo, è stato 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. A queste interfacce si accede 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 su 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 KeyMint convertStorageKeyToEphemeral. Questo metodo viene utilizzato sia da vold che 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 forte 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 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.

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 anziché convertStorageKeyToEphemeral.

Modifiche al kernel Linux

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

Per i kernel android14 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 e blk_crypto_ll_ops::keyslot_evict supportino la programmazione/l'espulsione delle 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, assicurati che blk_ksm_ll_ops::keyslot_program e blk_ksm_ll_ops::keyslot_evict supportino la programmazione/rimozione delle 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, assicurati che keyslot_mgmt_ll_ops::keyslot_program e keyslot_mgmt_ll_ops::keyslot_evict supportino la programmazione/rimozione delle chiavi con wrapping hardware e implementa keyslot_mgmt_ll_ops::derive_raw_secret.

Test

Sebbene la crittografia con chiavi con wrapping hardware sia più difficile da testare rispetto alla crittografia con chiavi standard, è 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 del test e verifica che gli scenari di test delle chiavi con wrapping hardware (ad esempio FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy e DmDefaultKeyTest.TestHwWrappedKey) non siano stati ignorati a causa del mancato rilevamento del supporto per le chiavi con wrapping hardware, poiché in questo caso i risultati del test sono ancora "passati".

Abilita chiavi

Una volta che il supporto delle chiavi 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 la crittografia FBE e dei metadati:

  • FBE: aggiungi il flag wrappedkey_v0 al parammetro 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.