Partecipa al nostro sondaggio sull'usabilità per migliorare questo sito.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Keystore con supporto hardware

La disponibilità di un ambiente di esecuzione affidabile in un sistema su chip (SoC) offre ai dispositivi Android l'opportunità di fornire servizi di sicurezza potenti e 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 aveva già 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 firma e verifica digitale, oltre alla generazione e all'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 facilmente raggiunti con una sola API di firma. Keystore in Android 6.0 estende 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 con supporto hardware. I controlli di accesso vengono specificati durante la generazione della chiave e applicati per la durata della chiave. Le chiavi possono essere limitate per essere utilizzabili solo dopo che l'utente si è autenticato e solo per scopi specifici 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 aggiunge 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 dell'uso improprio delle chiavi
  • Uno schema di controllo dell'accesso per abilitare la restrizione delle chiavi a utenti, client 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 protetto e la sua configurazione.

L'associazione 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 vecchia versione del sistema o del software TEE non possa riportare 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 un livello di patch più recenti, la chiave viene aggiornata prima di poter essere utilizzata e la versione precedente della chiave viene invalidata. Man mano che il dispositivo viene aggiornato, le chiavi si spostano in avanti insieme al dispositivo, ma qualsiasi ripristino del dispositivo a una versione precedente rende le chiavi inutilizzabili.

In Android 8.0, Keymaster 3 è passato dalla vecchia struttura a C Hardware Abstraction Layer (HAL) all'interfaccia C ++ HAL generata da una definizione nel nuovo HIDL (Hardware Interface Definition Language). Come parte del cambiamento, molti dei tipi di argomento sono cambiati, sebbene i tipi ei 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 estende la funzione di attestazione di Keymaster 2 per supportare l' attestazione dell'ID . L'attestazione dell'ID fornisce un meccanismo limitato e facoltativo per attestare fortemente gli identificatori hardware, come il numero di serie del dispositivo, il nome del prodotto e l'ID del telefono (IMEI / MEID). Per implementare questa aggiunta, modificare 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, oltre a definire un meccanismo per disabilitare in modo sicuro e permanente la funzione.

In Android 9, gli aggiornamenti includono:

  • Aggiorna a Keymaster 4
  • Supporto per elementi protetti incorporati
  • Supporto per l'importazione sicura delle chiavi
  • Supporto per la crittografia 3DES
  • Modifiche all'associazione di versione in modo che boot.img e system.img abbiano versioni impostate separatamente per consentire aggiornamenti indipendenti

Glossario

Ecco una rapida panoramica dei componenti del Keystore e delle loro relazioni.

AndroidKeystore è l'API di Framework Android e il componente utilizzato dalle app per accedere alla funzionalità del keystore. È implementato come un'estensione delle API Java Cryptography Architecture standard e consiste in codice Java che viene eseguito nello spazio di elaborazione dell'app. AndroidKeystore soddisfa le richieste delle app per il comportamento del keystore inoltrandole al daemon del keystore.

Il daemon del keystore è un daemon del sistema Android che fornisce l'accesso a tutte le funzionalità del Keystore tramite un'API Binder . È responsabile dell'archiviazione dei "blob di chiavi", che contengono il materiale della chiave segreta effettiva, crittografato in modo che Keystore possa archiviarlo ma non usarlo o rivelarlo.

keymasterd è un server HIDL che fornisce l'accesso a Keymaster TA. (Questo nome non è standardizzato ed è per scopi concettuali.)

Keymaster TA (applicazione affidabile) è il software in esecuzione in un contesto sicuro, il più delle volte in TrustZone su un SoC ARM, che fornisce tutte le operazioni di Keystore sicure, ha accesso al materiale della chiave grezza, convalida tutte le condizioni di controllo dell'accesso sulle chiavi , eccetera.

LockSettingsService è il componente del sistema Android responsabile dell'autenticazione dell'utente, sia della password che dell'impronta digitale. Non fa parte di Keystore, ma è rilevante perché molte operazioni chiave di Keystore richiedono l'autenticazione dell'utente. LockSettingsService interagisce con Gatekeeper TA e Fingerprint TA per ottenere i token di autenticazione, che fornisce al daemon del keystore e che vengono infine utilizzati dall'applicazione Keymaster TA.

Gatekeeper TA (applicazione attendibile) è un altro componente in esecuzione nel contesto protetto, responsabile dell'autenticazione delle password degli utenti e della generazione di token di autenticazione utilizzati per dimostrare al Keymaster TA che un'autenticazione è stata eseguita per un particolare utente in un determinato momento.

Fingerprint TA (applicazione attendibile) è un altro componente in esecuzione nel contesto protetto che è responsabile dell'autenticazione delle impronte digitali degli utenti e della generazione di token di autenticazione utilizzati per dimostrare al Keymaster TA che un'autenticazione è stata eseguita per un particolare utente in un determinato momento.

Architettura

L'API Keystore Android e l'HAL Keymaster sottostante forniscono un set di base ma adeguato di primitive crittografiche per consentire l'implementazione di protocolli utilizzando chiavi con supporto hardware e controllate dall'accesso.

Keymaster HAL è una libreria caricabile dinamicamente fornita dall'OEM utilizzata dal servizio Keystore per fornire servizi crittografici supportati da hardware. Per mantenere le cose al sicuro, le implementazioni di HAL non eseguono operazioni sensibili nello spazio utente, o anche nello spazio del kernel. Le operazioni sensibili sono delegate a un processore protetto raggiungibile tramite alcune interfacce del kernel. L'architettura risultante è simile a questa:

Accesso a Keymaster

Figura 1. Accesso a Keymaster

All'interno di un dispositivo Android, il "client" del Keymaster HAL è costituito da più livelli (es. App, framework, daemon Keystore), ma che possono essere ignorati 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 sul sito per sviluppatori Android .

Lo scopo del Keymaster HAL non è quello di implementare gli algoritmi sensibili alla sicurezza, ma solo di effettuare il marshalling e l'annullamento del marshalling delle richieste nel mondo protetto. 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 che eseguono Android 5.0 e versioni precedenti lanciate con i vecchi Keymaster HAL, Keystore fornisce un adattatore che implementa l'HAL Keymaster 1 con chiamate alla libreria hardware esistente. Il risultato non può fornire l'intera gamma di funzionalità nel Keymaster 1 HAL. In particolare, supporta solo algoritmi RSA ed ECDSA e tutta l'applicazione dell'autorizzazione della chiave viene eseguita dall'adattatore, nel mondo non protetto.

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 round trip al TEE nei casi in cui l'input è disponibile tutto in una volta e semplifica l'implementazione della decrittografia AEAD.

In Android 8.0, Keymaster 3 è passato dalla vecchia struttura C HAL all'interfaccia C ++ HAL generata da una definizione nel nuovo HIDL (Hardware Interface Definition Language). Viene creata un'implementazione HAL di nuovo stile creando una sottoclasse della classe IKeymasterDevice generata e implementando i metodi virtuali puri. Come parte del cambiamento, 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.

Panoramica di HIDL

Il linguaggio HIDL (Hardware Interface Definition Language) 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 Riferimenti .

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 da 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ù una struttura 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 generate_cb è un puntatore a funzione definito come:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

Cioè, generate_cb è una funzione che accetta i valori di ritorno elencati nella clausola generate. La classe di implementazione HAL sovrascrive questo metodo generateKey e chiama il puntatore alla funzione generate_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 a funzione fornito, che viene eseguito fino al completamento, restituendo il controllo all'implementazione generateKey , che poi 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 all'archivio chiavi è che ogni app ha il proprio spazio dei nomi. Ma per ogni regola c'è un'eccezione. Keystore ha alcune mappe hard coded che consentono a determinati componenti di sistema di accedere a determinati altri spazi dei nomi. Questo è uno strumento molto schietto in quanto fornisce a un componente il controllo completo su un altro spazio dei nomi. E poi c'è la questione dei componenti del fornitore come clienti di Keystore. Al momento non abbiamo modo di stabilire uno spazio dei nomi per i componenti di un fornitore, ad esempio, supplicant WPA.

Al fine di accogliere i componenti del fornitore e generalizzare il controllo degli accessi senza eccezioni hard coded, Keystore 2.0 introduce domini e spazi dei nomi selinux.

Domini del keystore

Con i domini Keystore, possiamo separare gli spazi dei nomi dagli uid. I client che accedono a una chiave nel 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 il chiamante desidera accedere e se dispone delle autorizzazioni appropriate.

Introduciamo cinque parametri di dominio che regolano la modalità di accesso alle chiavi. Controllano la semantica del parametro dello spazio dei nomi del descrittore della chiave e il modo in cui viene eseguito il controllo dell'accesso.

  • DOMAIN_APP : il dominio dell'app copre il comportamento precedente. Java Keystore SPI utilizza questo dominio per impostazione predefinita. Quando viene utilizzato questo dominio, l'argomento dello spazio dei nomi viene ignorato e al suo posto viene utilizzato l'uid del chiamante. L'accesso a questo dominio è controllato dall'etichetta keystore alla classe keystore_key nella policy SELinux.
  • DOMAIN_SELINUX : questo dominio indica che lo spazio dei nomi ha un'etichetta nella policy SELinux. Il parametro dello spazio dei nomi viene cercato e tradotto in un contesto di destinazione e viene eseguito un controllo dei permessi per il contesto SELinux chiamante per la classe keystore_key . Una volta stabilita l'autorizzazione 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 dell'accesso verifica solo se l'uid del chiamante corrisponde all'uid del beneficiario della sovvenzione richiesta.
  • DOMAIN_KEY_ID : questo dominio indica che il parametro dello spazio dei nomi è un ID chiave univoco. La chiave stessa potrebbe essere stata creata con DOMAIN_APP o DOMAIN_SELINUX . Il controllo delle autorizzazioni viene eseguito dopo che il domain e lo namespace dei namespace sono stati caricati dal database delle chiavi nello stesso modo in cui il BLOB veniva caricato dal dominio, dallo spazio dei nomi e dalla tupla alias. La logica per il dominio dell'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 della chiave, tuttavia, non cambia mai. Quindi, quando si utilizza una chiave con l'ID della chiave dopo che è stata caricata dal database del keystore utilizzando l'alias una volta, si può essere certi che sia la stessa chiave fintanto che l'ID della chiave esiste ancora. Questa funzionalità non è esposta agli sviluppatori di app, ma viene utilizzata all'interno di Android Keystore SPI per fornire un'esperienza più coerente anche se utilizzata contemporaneamente in modo non sicuro.
  • DOMAIN_BLOB : il dominio DOMAIN_BLOB indica che il chiamante gestisce il BLOB da solo. Viene utilizzato per i client che devono accedere al Keystore prima che la partizione dati venga montata. Il BLOB chiave è incluso nel campo blob del descrittore di chiave.

Utilizzando il dominio SELinux, possiamo dare ai componenti del fornitore l'accesso a spazi dei nomi del keystore molto specifici che possono essere condivisi dai componenti di 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 mappatura un id di spazio dei nomi numerico 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 concedere l'accesso ad esso 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_keys:keystore2_key { get, use };

Dopo aver configurato 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 le chiavi da e nel 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, l'id dello spazio dei nomi deve essere fornito 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 usati per configurare gli spazi dei nomi SELinux Keystore 2.0. Ogni partizione ha un intervallo riservato diverso di 10.000 ID spazio dei nomi per evitare conflitti.

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, qui "wifi_keys" , tramite il suo id numerico.

Oltre a ciò sono stati definiti i seguenti spazi dei nomi. Laddove sostituiscono regole speciali, la tabella seguente indica l'UID a cui corrispondevano.

ID spazio dei nomi Etichetta SEPolicy UID Descrizione
0 su_key N / A Chiave super utente. Utilizzato solo per test su build userdebug e 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 nelle build degli utenti dalla riga di comando.
100 vold_key N / A Destinato all'uso da parte di vold.
101 odsing_key N / A Utilizzato dal demone di firma sul dispositivo.
102 wfi_key AID_WIFI (1010) Utilizzato dal sistema Wi-Fi di Android incluso wpa_supplicant.

Accedi ai vettori

La classe SELinux keystore_key è invecchiata un po '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 applicherà.

Autorizzazione Significato
delete Selezionato durante la rimozione delle chiavi da Keystore.
get_info Controllato quando vengono richiesti i metadati di una chiave.
grant Il chiamante necessita di questa autorizzazione per creare una concessione alla chiave nel contesto di destinazione.
manage_blob Il chiamante può utilizzare DOMAIN_BLOB sullo spazio dei nomi SELinux specificato, gestendo in tal modo i BLOB da solo. Questo è particolarmente utile per vold.
rebind Questa autorizzazione controlla se un alias può essere ricollegato a una nuova chiave. Ciò è necessario per l'inserimento e implica che la chiave associata in precedenza verrà eliminata. È fondamentalmente un permesso di inserimento, ma cattura meglio la semantica del keystore.
req_forced_op I client con questa autorizzazione possono creare operazioni non eliminabili e la creazione dell'operazione non fallisce mai, a meno che tutti gli slot di operazione non siano occupati da operazioni non eliminabili.
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, en / decryption.
use_dev_id Obbligatorio durante la generazione di informazioni di identificazione del dispositivo, come l'attestazione dell'ID dispositivo.

Inoltre, abbiamo suddiviso una serie di autorizzazioni keystore non specifiche per la chiave nella classe di sicurezza di 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 da 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 il Keystore, ovvero di rimuovere la chiave principale, in modo tale che le chiavi associate all'autorizzazione diventino inutilizzabili e non modificabili.
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.