La disponibilità di un ambiente di esecuzione affidabile in un sistema su un chip (SoC) offre ai dispositivi Android l'opportunità di fornire servizi di sicurezza avanzati supportati da hardware al sistema operativo Android, ai servizi della piattaforma e persino alle app di terze parti. Gli sviluppatori che cercano le estensioni specifiche per Android dovrebbero andare su android.security.keystore .
Prima di Android 6.0, Android disponeva già di una semplice API per servizi di crittografia supportata da hardware, fornita dalle versioni 0.2 e 0.3 di Keymaster Hardware Abstraction Layer (HAL). Keystore ha fornito operazioni di verifica e firma digitale, oltre alla generazione e importazione di coppie di chiavi di firma asimmetrica. Questo è già implementato su molti dispositivi, ma ci sono molti obiettivi di sicurezza che non possono essere raggiunti facilmente solo con un'API di firma. Keystore in Android 6.0 ha esteso l'API Keystore per fornire una gamma più ampia di funzionalità.
In Android 6.0, Keystore ha aggiunto primitive crittografiche simmetriche , AES e HMAC e un sistema di controllo degli accessi per chiavi supportate da hardware. I controlli di accesso vengono specificati durante la generazione della chiave e applicati per tutta la durata della chiave. Le chiavi possono essere limitate per essere utilizzabili solo dopo che l'utente è stato autenticato e solo per scopi specificati o con parametri crittografici specificati. Per ulteriori informazioni, vedere le pagine Tag di autorizzazione e funzioni .
Oltre ad espandere la gamma di primitive crittografiche, Keystore in Android 6.0 ha aggiunto quanto segue:
- Uno schema di controllo dell'utilizzo per consentire di limitare l'utilizzo delle chiavi, per mitigare il rischio di compromissione della sicurezza a causa di un uso improprio delle chiavi
- Uno schema di controllo dell'accesso per abilitare la restrizione delle chiavi a utenti, client specifici e un intervallo di tempo definito
In Android 7.0, Keymaster 2 ha aggiunto il supporto per l'attestazione della chiave e l'associazione della versione. L'attestazione della chiave fornisce certificati di chiave pubblica che contengono una descrizione dettagliata della chiave e dei relativi controlli di accesso, per rendere verificabile in remoto l'esistenza della chiave nell'hardware sicuro e la sua configurazione.
Il binding della versione associa le chiavi al sistema operativo e alla versione a livello di patch. Ciò garantisce che un utente malintenzionato che scopre un punto debole in una versione precedente del sistema o nel software TEE non possa ripristinare un dispositivo alla versione vulnerabile e utilizzare le chiavi create con la versione più recente. Inoltre, quando una chiave con una determinata versione e livello di patch viene utilizzata su un dispositivo che è stato aggiornato a una versione o livello di patch più recenti, la chiave viene aggiornata prima di poter essere utilizzata e la versione precedente della chiave viene invalidata. Quando il dispositivo viene aggiornato, i tasti si "agganciano" in avanti insieme al dispositivo, ma qualsiasi ripristino del dispositivo a una versione precedente rende i tasti inutilizzabili.
In Android 8.0, Keymaster 3 è passato dall'HAL (Hardware Abstraction Layer) della struttura C vecchio stile all'interfaccia HAL C++ generata da una definizione nel nuovo Hardware Interface Definition Language (HIDL). Come parte della modifica, molti dei tipi di argomento sono cambiati, sebbene tipi e metodi abbiano una corrispondenza uno a uno con i vecchi tipi e con i metodi della struttura HAL. Vedere la pagina Funzioni per maggiori dettagli.
Oltre a questa revisione dell'interfaccia, Android 8.0 ha esteso la funzionalità di attestazione di Keymaster 2 per supportare l' attestazione ID . L'attestazione ID fornisce un meccanismo limitato e facoltativo per attestare con forza gli identificatori hardware, come il numero di serie del dispositivo, il nome del prodotto e l'ID telefono (IMEI/MEID). Per implementare questa aggiunta, Android 8.0 ha modificato lo schema di attestazione ASN.1 per aggiungere l'attestazione ID. Le implementazioni di Keymaster devono trovare un modo sicuro per recuperare gli elementi di dati rilevanti, nonché definire un meccanismo per disabilitare in modo sicuro e permanente la funzione.
In Android 9, gli aggiornamenti includevano:
- Aggiornamento a Keymaster 4
- Supporto per Secure Element incorporati
- Supporto per l'importazione di chiavi sicure
- Supporto per la crittografia 3DES
- Modifiche all'associazione della versione in modo che boot.img e system.img abbiano versioni impostate separatamente per consentire aggiornamenti indipendenti
Glossario
Ecco una rapida panoramica dei componenti di Keystore e delle loro relazioni.
AndroidKeystore è l'API di Android Framework e il componente utilizzato dalle app per accedere alla funzionalità Keystore. È implementato come estensione delle API standard dell'architettura di crittografia Java e consiste in codice Java che viene eseguito nello spazio di processo dell'app. AndroidKeystore
soddisfa le richieste dell'app per il comportamento del keystore inoltrandole al daemon del keystore.
Il demone keystore è un demone di sistema Android che fornisce l'accesso a tutte le funzionalità di Keystore tramite un'API Binder . È responsabile della memorizzazione dei "key blob", che contengono l'effettivo materiale della chiave segreta, crittografato in modo che Keystore possa archiviarli ma non utilizzarli o rivelarli.
keymasterd è un server HIDL che fornisce l'accesso al Keymaster TA. (Questo nome non è standardizzato ed è per scopi concettuali.)
Keymaster TA (applicazione attendibile) è il software in esecuzione in un contesto sicuro, il più delle volte in TrustZone su un SoC ARM, che fornisce tutte le operazioni sicure di Keystore, ha accesso al materiale grezzo delle chiavi, convalida tutte le condizioni di controllo dell'accesso sulle chiavi , eccetera.
LockSettingsService è il componente del sistema Android responsabile dell'autenticazione dell'utente, sia tramite password che impronta digitale. Non fa parte di Keystore, ma è rilevante perché molte operazioni sulle chiavi di Keystore richiedono l'autenticazione dell'utente. LockSettingsService
interagisce con Gatekeeper TA e Fingerprint TA per ottenere token di autenticazione, che fornisce al demone keystore e che vengono infine consumati dall'applicazione Keymaster TA.
Gatekeeper TA (applicazione attendibile) è un altro componente in esecuzione nel contesto protetto, responsabile dell'autenticazione delle password utente e della generazione di token di autenticazione utilizzati per dimostrare al Keymaster TA che è stata eseguita un'autenticazione per un particolare utente in un determinato momento.
Fingerprint TA (applicazione attendibile) è un altro componente in esecuzione nel contesto sicuro che è responsabile dell'autenticazione delle impronte digitali dell'utente e della generazione di token di autenticazione utilizzati per dimostrare al Keymaster TA che è stata eseguita un'autenticazione per un particolare utente in un determinato momento.
Architettura
L'API Android Keystore e l'HAL Keymaster sottostante forniscono un set di primitive crittografiche di base ma adeguato per consentire l'implementazione di protocolli che utilizzano chiavi supportate da hardware e controllate dall'accesso.
Keymaster HAL è una libreria caricabile dinamicamente fornita da OEM utilizzata dal servizio Keystore per fornire servizi di crittografia supportati da hardware. Per mantenere le cose al sicuro, le implementazioni HAL non eseguono alcuna operazione sensibile nello spazio utente, o anche nello spazio del kernel. Le operazioni sensibili sono delegate a un processore sicuro raggiungibile tramite un'interfaccia del kernel. L'architettura risultante si presenta così:

Figura 1. Accesso a Keymaster
All'interno di un dispositivo Android, il "client" del Keymaster HAL è costituito da più livelli (es. app, framework, demone Keystore), ma questo può essere ignorato ai fini di questo documento. Ciò significa che l'API Keymaster HAL descritta è di basso livello, utilizzata dai componenti interni alla piattaforma e non esposta agli sviluppatori di app. L'API di livello superiore è descritta nel sito per sviluppatori Android .
Lo scopo del Keymaster HAL non è implementare gli algoritmi sensibili alla sicurezza, ma solo effettuare il marshalling e unmarshalling delle richieste al mondo sicuro. Il formato del filo è definito dall'implementazione.
Compatibilità con le versioni precedenti
Il Keymaster 1 HAL è completamente incompatibile con gli HAL rilasciati in precedenza, ad esempio Keymaster 0.2 e 0.3. Per facilitare l'interoperabilità sui dispositivi con Android 5.0 e versioni precedenti lanciati con i precedenti Keymaster HAL, Keystore fornisce un adattatore che implementa Keymaster 1 HAL con chiamate alla libreria hardware esistente. Il risultato non può fornire l'intera gamma di funzionalità nel Keymaster 1 HAL. In particolare, supporta solo gli algoritmi RSA ed ECDSA e tutta l'imposizione dell'autorizzazione della chiave viene eseguita dall'adattatore, nel mondo non sicuro.
Keymaster 2 ha ulteriormente semplificato l'interfaccia HAL rimuovendo i metodi get_supported_*
e consentendo al metodo finish()
di accettare l'input. Ciò riduce il numero di viaggi di andata e ritorno al TEE nei casi in cui l'input è disponibile tutto in una volta e semplifica l'implementazione della decrittazione AEAD.
In Android 8.0, Keymaster 3 è passato dall'HAL con struttura C vecchio stile all'interfaccia HAL C++ generata da una definizione nel nuovo Hardware Interface Definition Language (HIDL). Un'implementazione HAL di nuovo stile viene creata creando una sottoclasse della classe IKeymasterDevice
generata e implementando i metodi virtuali puri. Come parte della modifica, molti dei tipi di argomenti sono cambiati, sebbene tipi e metodi abbiano una corrispondenza uno a uno con i vecchi tipi e con i metodi della struttura HAL.
Panoramica HIDL
L'hardware Interface Definition Language (HIDL) fornisce un meccanismo di implementazione indipendente dal linguaggio per specificare le interfacce hardware. Gli strumenti HIDL attualmente supportano la generazione di interfacce C++ e Java. Si prevede che la maggior parte degli implementatori di Trusted Execution Environment (TEE) troveranno gli strumenti C++ più convenienti, quindi questo documento discute solo la rappresentazione C++.
Le interfacce HIDL sono costituite da un insieme di metodi, espressi come:
methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);
Esistono vari tipi predefiniti e gli HAL possono definire nuovi tipi enumerati e di struttura. Per maggiori dettagli su HIDL, vedere la sezione Riferimento .
Un metodo di esempio da Keymaster 3 IKeymasterDevice.hal
è:
generateKey(vec<KeyParameter> keyParams) generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
Questo è l'equivalente di quanto segue dal keymaster2 HAL:
keymaster_error_t (*generate_key)( const struct keymaster2_device* dev, const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics);
Nella versione HIDL, l'argomento dev
viene rimosso, perché è implicito. L'argomento params
non è più uno struct contenente un puntatore che fa riferimento a un array di oggetti key_parameter_t
, ma un vec
(vettore) contenente oggetti KeyParameter
. I valori restituiti sono elencati nella clausola " generates
", incluso un vettore di valori uint8_t
per il BLOB di chiavi.
Il metodo virtuale C++ generato dal compilatore HIDL è:
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams, generateKey_cb _hidl_cb) override;
Dove generateKey_cb
è un puntatore a funzione definito come:
std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob, const KeyCharacteristics& keyCharacteristics)>
Vale a dire, generateKey_cb
è una funzione che accetta i valori restituiti elencati nella clausola generate. La classe di implementazione HAL esegue l'override di questo metodo generateKey
e chiama il puntatore alla funzione generateKey_cb
per restituire il risultato dell'operazione al chiamante. Notare che la chiamata del puntatore alla funzione è sincrona . Il chiamante chiama generateKey
e generateKey
chiama il puntatore alla funzione fornito, che viene eseguito fino al completamento, restituendo il controllo all'implementazione generateKey
, che quindi ritorna al chiamante.
Per un esempio dettagliato, vedere l'implementazione predefinita in hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
. L'implementazione predefinita fornisce la compatibilità con le versioni precedenti per i dispositivi con HALS keymaster0, keymaster1 o keymaster2 vecchio stile.
Controllo di accesso
La regola più basilare del controllo dell'accesso al Keystore è che ogni app ha il proprio spazio dei nomi. Ma per ogni regola c'è un'eccezione. Keystore ha alcune mappe codificate che consentono a determinati componenti del sistema di accedere a determinati altri spazi dei nomi. Questo è uno strumento molto schietto in quanto fornisce a un componente il pieno controllo su un altro spazio dei nomi. E poi c'è la questione dei componenti dei fornitori come clienti di Keystore. Al momento non abbiamo modo di stabilire uno spazio dei nomi per i componenti del fornitore, ad esempio WPA supplicant.
Per accogliere i componenti dei fornitori e generalizzare il controllo degli accessi senza eccezioni codificate, Keystore 2.0 introduce i domini e gli spazi dei nomi SELinux.
Domini Keystore
Con i domini Keystore, possiamo disaccoppiare gli spazi dei nomi dagli UID. I client che accedono a una chiave in Keystore devono specificare il dominio, lo spazio dei nomi e l'alias a cui desiderano accedere. Sulla base di questa tupla e dell'identità del chiamante possiamo determinare a quale chiave vuole accedere il chiamante e se dispone delle autorizzazioni appropriate.
Introduciamo cinque parametri di dominio che regolano il modo in cui è possibile accedere alle chiavi. Controllano la semantica del parametro namespace del descrittore chiave e come viene eseguito il controllo di accesso.
-
DOMAIN_APP
: il dominio dell'app copre il comportamento legacy. La Java Keystore SPI utilizza questo dominio per impostazione predefinita. Quando viene utilizzato questo dominio, l'argomento dello spazio dei nomi viene ignorato e viene invece utilizzato l'UID del chiamante. L'accesso a questo dominio è controllato dall'etichetta Keystore alla classekeystore_key
nella politica SELinux. -
DOMAIN_SELINUX
: questo dominio indica che lo spazio dei nomi ha un'etichetta nella politica SELinux. Il parametro namespace viene ricercato e tradotto in un contesto di destinazione e viene eseguito un controllo dei permessi per il contesto SELinux chiamante per la classekeystore_key
. Quando l'autorizzazione è stata stabilita per l'operazione specificata, la tupla completa viene utilizzata per la ricerca della chiave. -
DOMAIN_GRANT
: il dominio di concessione indica che il parametro dello spazio dei nomi è un identificatore di concessione. Il parametro alias viene ignorato. I controlli SELinux vengono eseguiti quando viene creata la concessione. Un ulteriore controllo degli accessi controlla solo se l'UID del chiamante corrisponde all'UID dei beneficiari della concessione richiesta. -
DOMAIN_KEY_ID
: questo dominio indica che il parametro namespace è un ID chiave univoco. La chiave stessa potrebbe essere stata creata conDOMAIN_APP
oDOMAIN_SELINUX
. Il controllo delle autorizzazioni viene eseguito dopo che ildomain
e lo spazio deinamespace
sono stati caricati dal database delle chiavi come se il BLOB fosse caricato dalla tupla di dominio, spazio dei nomi e alias. La logica del dominio id chiave è la continuità. Quando si accede a una chiave tramite alias, le chiamate successive possono operare su chiavi diverse, poiché una nuova chiave potrebbe essere stata generata o importata e associata a questo alias. L'ID chiave, tuttavia, non cambia mai. Pertanto, quando si utilizza una chiave per ID chiave dopo che è stata caricata dal database Keystore utilizzando l'alias una volta, si può essere certi che sia la stessa chiave purché l'ID chiave esista ancora. Questa funzionalità non è esposta agli sviluppatori di app. Al contrario, viene utilizzato all'interno della SPI di Android Keystore per fornire un'esperienza più coerente anche se utilizzato contemporaneamente in modo non sicuro. -
DOMAIN_BLOB
: il dominio BLOB indica che il chiamante gestisce il BLOB da solo. Viene utilizzato per i client che devono accedere al Keystore prima che la partizione di dati venga montata. Il BLOB di chiavi è incluso nel campoblob
del descrittore di chiavi.
Utilizzando il dominio SELinux, possiamo fornire ai componenti del fornitore l'accesso a spazi dei nomi Keystore molto specifici che possono essere condivisi dai componenti del sistema come la finestra di dialogo delle impostazioni.
Politica SELinux per keystore_key
Le etichette dello spazio dei nomi vengono configurate utilizzando il file keystore2_key_context
.
Ogni riga in questi file associa un id numerico dello spazio dei nomi a un'etichetta SELinux. Per esempio,
# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and # Settings to share keystore keys. 102 u:object_r:wifi_key:s0
Dopo aver impostato un nuovo spazio dei nomi delle chiavi in questo modo, possiamo accedervi aggiungendo una policy appropriata. Ad esempio, per consentire a wpa_supplicant
di ottenere e utilizzare le chiavi nel nuovo spazio dei nomi, aggiungeremo la seguente riga a hal_wifi_supplicant.te
:
allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };
Dopo aver impostato il nuovo spazio dei nomi, AndroidKeyStore può essere utilizzato quasi come al solito. L'unica differenza è che è necessario specificare l'ID dello spazio dei nomi. Per caricare e importare chiavi da e in Keystore, l'ID dello spazio dei nomi viene specificato utilizzando AndroidKeyStoreLoadStoreParameter
. Per esempio,
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import java.security.KeyStore; KeyStore keystore = KeyStore.getInstance("AndroidKeyStore"); keystore.load(new AndroidKeyStoreLoadStoreParameter(102));
Per generare una chiave in un determinato spazio dei nomi, è necessario fornire l'ID dello spazio dei nomi utilizzando KeyGenParameterSpec.Builder#setNamespace():
import android.security.keystore.KeyGenParameterSpec; KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(); specBuilder.setNamespace(102);
I seguenti file di contesto possono essere utilizzati per configurare gli spazi dei nomi SELinux di Keystore 2.0. Ogni partizione ha un intervallo riservato diverso di 10.000 ID spazio dei nomi per evitare collisioni.
Partizione | Gamma | File di configurazione |
---|---|---|
Sistema | 0 ... 9.999 | /system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts |
Sistema esteso | 10.000 ... 19.999 | /system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts |
Prodotto | 20.000 ... 29.999 | /product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts |
Venditore | 30.000 ... 39.999 | /vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts |
Il client richiede la chiave richiedendo il dominio SELinux e lo spazio dei nomi virtuale desiderato, in questo caso "wifi_key"
, tramite il suo id numerico.
Oltre a ciò, sono stati definiti i seguenti spazi dei nomi. Se sostituiscono regole speciali, la tabella seguente indica l'UID a cui corrispondevano.
ID spazio dei nomi | Etichetta politica SE | UID | Descrizione |
---|---|---|---|
0 | su_key | N / A | Chiave super utente. Utilizzato solo per testare su userdebug e build eng. Non rilevante per le build degli utenti. |
1 | shell_key | N / A | Spazio dei nomi disponibile per la shell. Utilizzato principalmente per i test, ma può essere utilizzato anche su build utente dalla riga di comando. |
100 | chiave_vold | N / A | Destinato all'uso da vold. |
101 | odsing_key | N / A | Utilizzato dal daemon di firma sul dispositivo. |
102 | chiave_wifi | AIUTO_WIFI(1010) | Utilizzato dal sistema Wi-Fi di Android incluso wpa_supplicant. |
120 | resume_on_reboot_key | AID_SYSTEM(1000) | Utilizzato dal server di sistema di Android per supportare la ripresa al riavvio. |
Accedi ai vettori
La classe SELinux keystore_key
è invecchiata parecchio e alcuni dei permessi, come la verify
o il sign
, hanno perso il loro significato. Ecco il nuovo set di autorizzazioni, keystore2_key
, che Keystore 2.0 imporrà.
Autorizzazione | Significato |
---|---|
delete | Selezionato durante la rimozione delle chiavi da Keystore. |
get_info | Selezionato quando vengono richiesti i metadati di una chiave. |
grant | Il chiamante ha bisogno di questa autorizzazione per creare una concessione alla chiave nel contesto di destinazione. |
manage_blob | Il chiamante può usare DOMAIN_BLOB sullo spazio dei nomi SELinux dato, gestendo così i BLOB da solo. Questo è particolarmente utile per vold. |
rebind | Questa autorizzazione controlla se un alias può essere rebound a una nuova chiave. Questo è necessario per l'inserimento e implica che la chiave precedentemente associata verrà eliminata. È fondamentalmente un'autorizzazione di inserimento, ma cattura meglio la semantica del keystore. |
req_forced_op | I client con questa autorizzazione possono creare operazioni non potato e la creazione dell'operazione non fallisce mai a meno che tutti gli slot operativi non siano occupati da operazioni non potato. |
update | Necessario per aggiornare il sottocomponente di una chiave. |
use | Selezionato durante la creazione di un'operazione Keymint che utilizza il materiale della chiave, ad esempio per la firma, la codifica/decodifica. |
use_dev_id | Necessario durante la generazione di informazioni di identificazione del dispositivo, ad esempio l'attestazione dell'ID dispositivo. |
Inoltre, abbiamo suddiviso un insieme di autorizzazioni keystore non specifiche per la chiave nella classe di sicurezza SELinux keystore2
:
Autorizzazione | Significato |
---|---|
add_auth | Richiesto dal provider di autenticazione come Gatekeeper o BiometricsManager per l'aggiunta di token di autenticazione. |
clear_ns | Precedentemente clear_uid, questa autorizzazione consente a un non proprietario di uno spazio dei nomi di eliminare tutte le chiavi in quello spazio dei nomi. |
list | Richiesto dal sistema per enumerare le chiavi in base a varie proprietà, come la proprietà o il limite di autenticazione. Questa autorizzazione non è richiesta dai chiamanti che enumerano i propri spazi dei nomi. Questo è coperto dall'autorizzazione get_info . |
lock | Questa autorizzazione consente di bloccare Keystore, ovvero eliminare la chiave principale, in modo tale che le chiavi associate all'autenticazione diventino inutilizzabili e non creabili. |
reset | Questa autorizzazione consente di ripristinare Keystore alle impostazioni di fabbrica, eliminando tutte le chiavi che non sono vitali per il funzionamento del sistema operativo Android. |
unlock | Questa autorizzazione è necessaria per tentare di sbloccare la chiave principale per le chiavi associate all'autenticazione. |