In Keymaster 1, tutte le chiavi Keymaster erano associate in modo crittografico alla Root of Trust del dispositivo o alla chiave di Avvio verificato. In Keymaster 2 e 3, tutte le chiavi sono associate anche al sistema operativo e al livello di patch dell'immagine di sistema. In questo modo, un malintenzionato che scopre una debolezza in una vecchia versione del software di sistema o TEE non può ripristinare una versione vulnerabile del dispositivo e utilizzare le chiavi create con la versione più recente. Inoltre, quando una chiave con una determinata versione e un determinato livello di patch viene utilizzata su un dispositivo di cui è stato eseguito l'upgrade a una versione o a un livello di patch più recente, 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, le chiavi *avanzano* insieme al dispositivo, ma qualsiasi reversione del dispositivo a una versione precedente rende le chiavi inutilizzabili.
Per supportare la struttura modulare di Treble e interrompere il binding di system.img a boot.img, Keymaster 4 ha modificato il modello di binding della versione della chiave in modo da avere livelli di patch separati per ogni partizione. Ciò consente di aggiornare ogni partizione in modo indipendente, fornendo al contempo la protezione di rollback.
Per implementare questo binding della versione, l'app attendibile KeyMint (TA) deve ricevere in modo sicuro la versione del sistema operativo e i livelli di patch correnti e garantire che le informazioni ricevute corrispondano a tutte le informazioni sul sistema in esecuzione.
- I dispositivi con Android Verified Boot (AVB) possono inserire tutti i livelli di patch
e la versione del sistema in vbmeta, in modo che il bootloader possa fornirli a
Keymaster. Per le partizioni concatenate, le informazioni sulla versione della partizione
si trovano in vbmeta concatenato. In generale, le informazioni sulla versione devono trovarsi nel
vbmeta struct
che contiene i dati di verifica (hash o hashtree) per una determinata partizione. - Sui dispositivi senza AVB:
- Le implementazioni di Avvio verificato devono fornire un hash dei metadati della versione al bootloader, in modo che quest'ultimo possa fornire l'hash a Keymaster.
boot.img
può continuare a memorizzare il livello di patch nell'intestazionesystem.img
può continuare a memorizzare il livello di patch e la versione del sistema operativo in proprietà di sola letturavendor.img
memorizza il livello di patch nella proprietà di sola letturaro.vendor.build.version.security_patch
.- Il bootloader può fornire a Keymaster un hash di tutti i dati convalidati da Verified Boot.
- In Android 9, utilizza i seguenti tag per fornire le informazioni sulla versione per
le seguenti partizioni:
VENDOR_PATCH_LEVEL
: Partizionevendor
BOOT_PATCH_LEVEL
: Partizioneboot
OS_PATCH_LEVEL
eOS_VERSION
: Partizionesystem
. (OS_VERSION
viene rimosso dall'intestazioneboot.img
.
-
Le implementazioni di Keymaster devono 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()
viene eseguito il rollback a un livello di patch superiore, se necessario.
Modifiche all'HAL
Per supportare l'associazione e l'attestazione delle versioni, 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 abbia una versione del sistema operativo o un livello patch corrispondente alla versione o al livello patch del sistema operativo corrente viene rifiutato con ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
è un valore UINT
che rappresenta le parti principale, secondaria e secondaria secondaria di una versione del sistema Android come MMmmss, dove MM è la versione principale, mm è la versione secondaria e ss è la versione secondaria secondaria. Ad esempio, 6.1.2 viene rappresentato come 060102.
Tag::OS_PATCHLEVEL
è un valore UINT
che rappresenta
l'anno e il mese dell'ultimo aggiornamento del sistema nel formato AAAAMM, dove AAAA è
l'anno di quattro cifre e MM è il mese di due cifre. Ad esempio, marzo 2016
viene rappresentato come 201603.
UpgradeKey
Per consentire l'upgrade delle chiavi alla nuova versione del sistema operativo e al nuovo livello di patch dell'immagine di sistema, Android 7.1 ha aggiunto il metodo upgradeKey
all'HAL:
Keymaster 3
upgradeKey(vec keyBlobToUpgrade, vec upgradeParams) generates(ErrorCode error, vec upgradedKeyBlob);
Keymaster 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 dispositivokeyBlobToUpgrade
è la chiave di cui è necessario eseguire l'upgradeupgradeParams
sono i parametri necessari per eseguire l'upgrade della chiave. Questi includonoTag::APPLICATION_ID
eTag::APPLICATION_DATA
, necessari per decriptare il blob della chiave, se sono stati forniti durante la generazione.upgradedKeyBlob
è il parametro di output, utilizzato per restituire il nuovo blob della chiave.
Se upgradeKey
viene chiamato con un blob della chiave che non può essere analizzato o
non è valido, restituisce ErrorCode::INVALID_KEY_BLOB
. Se viene chiamato con una chiave il cui livello patch è superiore al 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 attuale e il valore di sistema
è diverso da zero, restituisce ErrorCode::INVALID_ARGUMENT
. Sono consentiti
gli upgrade della versione del sistema operativo da un valore diverso da zero a zero. In caso di errori
di comunicazione con il mondo sicuro, restituisce un valore di errore appropriato (ad esempio,
ErrorCode::SECURE_HW_ACCESS_DENIED
,
ErrorCode::SECURE_HW_BUSY
). In caso contrario, restituisce
ErrorCode::OK
e un nuovo blob della chiave in
upgradedKeyBlob
.
keyBlobToUpgrade
rimane valido dopo la chiamata upgradeKey
e potrebbe teoricamente essere riutilizzato se il dispositivo venisse sottoposto a downgrade. In
pratica, keystore chiama in genere deleteKey
sul
blob keyBlobToUpgrade
poco 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, la TA Keymaster deve ricevere in modo sicuro la versione e il livello di patch attuali del sistema operativo (informazioni sulla versione) e assicurarsi che le informazioni ricevute corrispondano in modo preciso a quelle del sistema in esecuzione.
Per supportare la distribuzione sicura delle informazioni sulla versione al TA, è stato aggiunto un OS_VERSION
campo all'intestazione dell'immagine di avvio. Lo script di build dell'immagine di avvio
compila automaticamente questo campo. Gli OEM e gli implementatori di Keymaster TA devono collaborare per modificare i bootloader dei dispositivi in modo da estrarre le informazioni sulla versione dall'immagine di avvio e passarle alla TA prima dell'avvio del sistema non sicuro. Ciò garantisce che gli autori di attacchi 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 questo scopo, è stato aggiunto il metodo configure all'HAL Keymaster:
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 l'apertura dell'HAL, ma prima di chiamare altri metodi. Se viene chiamato un altro metodo
prima di configure, TA restituisce
ErrorCode::KEYMASTER_NOT_CONFIGURED
.
La prima volta che viene chiamato configure
dopo l'avvio del dispositivo, deve
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 gli altri metodi
Keymaster iniziano a funzionare normalmente.
Le chiamate successive a configure
restituiscono lo stesso valore restituito dalla
prima chiamata e non modificano lo stato di Keymaster.
Poiché configure
viene chiamato dal sistema di cui deve convalidare i contenuti, un malintenzionato ha una finestra di opportunità ristretta per compromettere l'immagine di sistema e forzarla a fornire informazioni sulla versione che corrispondono all'immagine di avvio, ma che non è la versione effettiva del sistema. La
combinazione di verifica dell'immagine di avvio, convalida dm-verity dei contenuti
dell'immagine di sistema e il fatto che configure
viene chiamato molto presto
all'avvio del sistema dovrebbe rendere difficile sfruttare questa opportunità.