In Keymaster 1, tutte le chiavi keymaster erano legate crittograficamente al dispositivo Root of Trust o alla chiave di avvio verificata. In Keymaster 2 e 3, tutte le chiavi sono anche associate al sistema operativo e al livello di patch dell'immagine di sistema. 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. In questo modo, man mano che il dispositivo viene aggiornato, i tasti si "innestano" in avanti insieme al dispositivo, ma qualsiasi ripristino del dispositivo a una versione precedente renderà i tasti inutilizzabili.
Per supportare la struttura modulare di Treble e interrompere l'associazione di system.img a boot.img, Keymaster 4 ha modificato il modello di associazione della versione chiave per avere livelli di patch separati per ciascuna partizione. Ciò consente a ciascuna partizione di essere aggiornata in modo indipendente, fornendo comunque la protezione dal rollback.
In Android 9 le partizioni di boot
, system
e del vendor
hanno ciascuna il proprio livello di patch.
- I dispositivi con Android Verified Boot (AVB) possono inserire tutti i livelli di patch e la versione del sistema in vbmeta, quindi il bootloader può fornirli a Keymaster. Per le partizioni concatenate, le informazioni sulla versione per la partizione saranno nella vbmeta concatenata. In generale, le informazioni sulla versione dovrebbero trovarsi nella
vbmeta struct
che contiene i dati di verifica (hash o hashtree) per una determinata partizione. - Su dispositivi senza AVB:
- Le implementazioni di avvio verificato devono fornire un hash dei metadati della versione al bootloader, in modo che il bootloader possa fornire l'hash a Keymaster.
-
boot.img
può continuare a memorizzare il livello di patch nell'intestazione -
system.img
può continuare a memorizzare il livello di patch e la versione del sistema operativo nelle proprietà di sola lettura -
vendor.img
memorizza il livello di patch nella proprietà di sola letturaro.vendor.build.version.security_patch
. - Il bootloader può fornire al keymaster un hash di tutti i dati convalidati dall'avvio verificato.
- In Android 9, usa i seguenti tag per fornire informazioni sulla versione per le seguenti partizioni:
-
VENDOR_PATCH_LEVEL
: partizione delvendor
-
BOOT_PATCH_LEVEL
: partizione diboot
-
OS_PATCH_LEVEL
eOS_VERSION
: partizionesystem
. (OS_VERSION
viene rimosso dall'intestazioneboot.img
.
-
- Le implementazioni di Keymaster dovrebbero trattare tutti i livelli di patch in modo indipendente. Le chiavi sono utilizzabili se tutte le informazioni sulla versione corrispondono ai valori associati a una chiave e
IKeymaster::upgradeDevice()
passa a un livello di patch superiore, se necessario.
HAL Modifiche
Per supportare il binding e l'attestazione della versione, Android 7.1 ha aggiunto i tag Tag::OS_VERSION
e Tag::OS_PATCHLEVEL
e i metodi configure
e upgradeKey
. I tag di versione vengono aggiunti automaticamente dalle implementazioni di Keymaster 2+ a tutte le chiavi appena generate (o aggiornate). Inoltre, qualsiasi tentativo di utilizzare una chiave che non ha una versione del sistema operativo o un livello di patch corrispondenti rispettivamente alla versione del sistema operativo corrente o al livello di patch, viene rifiutato con ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
è un valore UINT
che rappresenta le parti principali, secondarie e secondarie di una versione del sistema Android come MMmmss, dove MM è la versione principale, mm è la versione secondaria e ss è la versione secondaria. Ad esempio 6.1.2 sarebbe rappresentato come 060102.
Tag::OS_PATCHLEVEL
è un valore UINT
che rappresenta l'anno e il mese dell'ultimo aggiornamento del sistema come AAAAMM, dove AAAA è l'anno a quattro cifre e MM è il mese a due cifre. Ad esempio, marzo 2016 sarebbe rappresentato come 201603.
UpgradeKey
Per consentire l'aggiornamento delle chiavi alla nuova versione del sistema operativo e al livello di patch dell'immagine di sistema, Android 7.1 ha aggiunto il metodo upgradeKey
:
Maestro delle chiavi 3
upgradeKey(vec keyBlobToUpgrade, vec upgradeParams) generates(ErrorCode error, vec upgradedKeyBlob);
Maestro delle chiavi 2
keymaster_error_t (*upgrade_key)(const struct keymaster2_device* dev, const keymaster_key_blob_t* key_to_upgrade, const keymaster_key_param_set_t* upgrade_params, keymaster_key_blob_t* upgraded_key);
-
dev
è la struttura del dispositivo -
keyBlobToUpgrade
è la chiave che deve essere aggiornata -
upgradeParams
sono parametri necessari per aggiornare la chiave. Questi includerannoTag::APPLICATION_ID
eTag::APPLICATION_DATA
, necessari per decrittografare il BLOB di chiavi, se forniti durante la generazione. -
upgradedKeyBlob
è il parametro di output, utilizzato per restituire il nuovo BLOB di chiavi.
Se upgradeKey
viene chiamato con un BLOB di chiavi che non può essere analizzato o non è altrimenti valido, restituisce ErrorCode::INVALID_KEY_BLOB
. Se viene chiamato con una chiave il cui livello di patch è maggiore del valore di sistema corrente, restituisce ErrorCode::INVALID_ARGUMENT
. Se viene chiamato con una chiave la cui versione del sistema operativo è maggiore del valore di sistema corrente e il valore di sistema è diverso da zero, restituisce ErrorCode::INVALID_ARGUMENT
. Sono consentiti aggiornamenti della versione del sistema operativo da zero a zero. In caso di errori di comunicazione con il mondo sicuro, restituisce un valore di errore appropriato (es. ErrorCode::SECURE_HW_ACCESS_DENIED
, ErrorCode::SECURE_HW_BUSY
). In caso contrario, restituisce ErrorCode::OK
e restituisce un nuovo BLOB di chiavi in upgradedKeyBlob
.
keyBlobToUpgrade
rimane valido dopo la chiamata upgradeKey
e potrebbe teoricamente essere utilizzato di nuovo se il dispositivo fosse sottoposto a downgrade. In pratica, il keystore chiama generalmente deleteKey
sul BLOB keyBlobToUpgrade
subito dopo la chiamata a upgradeKey
. Se keyBlobToUpgrade
aveva il tag Tag::ROLLBACK_RESISTANT
, anche upgradedKeyBlob
dovrebbe averlo (e dovrebbe essere resistente al rollback).
Configurazione sicura
Per implementare il binding della versione, il keymaster TA ha bisogno di un modo per ricevere in modo sicuro la versione corrente del sistema operativo e il livello di patch (informazioni sulla versione) e per garantire che le informazioni che riceve corrispondano fortemente alle informazioni sul sistema in esecuzione.
Per supportare la consegna sicura delle informazioni sulla versione al TA, è stato aggiunto un campo OS_VERSION
all'intestazione dell'immagine di avvio. Lo script di creazione dell'immagine di avvio popola automaticamente questo campo. Gli OEM e gli implementatori di keymaster TA devono collaborare per modificare i bootloader del dispositivo per estrarre le informazioni sulla versione dall'immagine di avvio e passarle al TA prima che il sistema non sicuro venga avviato. Ciò garantisce che gli aggressori non possano interferire con il provisioning delle informazioni sulla versione al TA.
È inoltre necessario assicurarsi che l'immagine di sistema abbia le stesse informazioni sulla versione dell'immagine di avvio. A tal fine, il metodo configure è stato aggiunto al keymaster HAL:
keymaster_error_t (*configure)(const struct keymaster2_device* dev, const keymaster_key_param_set_t* params);
L'argomento params
contiene Tag::OS_VERSION
e Tag::OS_PATCHLEVEL
. Questo metodo viene chiamato dai client keymaster2 dopo aver aperto l'HAL, ma prima di chiamare qualsiasi altro metodo. Se viene chiamato qualsiasi altro metodo prima della configurazione, TA restituisce ErrorCode::KEYMASTER_NOT_CONFIGURED
.
La prima volta che configure
viene chiamato dopo l'avvio del dispositivo, dovrebbe verificare che le informazioni sulla versione fornite corrispondano a quelle fornite dal bootloader. Se le informazioni sulla versione non corrispondono, configure
restituisce ErrorCode::INVALID_ARGUMENT
e tutti gli altri metodi keymaster continuano a restituire ErrorCode::KEYMASTER_NOT_CONFIGURED
. Se le informazioni corrispondono, configure
restituisce ErrorCode::OK
e altri metodi keymaster iniziano a funzionare normalmente.
Le successive chiamate da configure
restituiscono lo stesso valore restituito dalla prima chiamata e non modificano lo stato di keymaster. Si noti che questo processo RICHIEDE che tutte le OTA aggiornino sia il sistema che le immagini di avvio; non possono essere aggiornati separatamente per mantenere sincronizzate le informazioni sulla versione.
Poiché configure
verrà chiamato dal sistema di cui intende convalidare il contenuto, c'è una stretta finestra di opportunità per un utente malintenzionato di compromettere l'immagine del sistema e costringerlo a fornire informazioni sulla versione che corrispondono all'immagine di avvio, ma che non è l'effettiva versione del sistema. La combinazione di verifica dell'immagine di avvio, convalida dm-verity del contenuto dell'immagine di sistema e il fatto che configure
sia chiamato molto presto nell'avvio del sistema dovrebbe rendere difficile sfruttare questa finestra di opportunità.