Vincolo della versione

In Keymaster 1, tutte le chiavi Keymaster erano legate in modo crittografico al 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 un dispositivo alla versione vulnerabile 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ù recenti, viene eseguito l'upgrade della chiave prima che possa essere utilizzata e la versione precedente della chiave viene invalidata. In questo modo, durante l'upgrade del dispositivo, le chiavi vengono aggiornate insieme al dispositivo, ma qualsiasi ripristino del dispositivo a una release 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 delle versioni delle chiavi in modo da avere livelli di patch distinti per ogni partizione. In questo modo, ogni partizione può essere aggiornata indipendentemente, garantendo al contempo la protezione di rollback.

In Android 9 le partizioni boot, system e vendor hanno ciascuna il proprio livello patch.

  • I dispositivi con avvio verificato di Android (AVB) possono inserire tutti i livelli di patch e la versione di sistema in vbmeta, in modo che il bootloader possa fornirli a Keymaster. Per le partizioni in catena, le informazioni sulla versione della partizione si trovano nel file vbmeta in catena. 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 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 della patch e la versione del sistema operativo nelle proprietà di sola lettura
    • vendor.img memorizza il livello del patch nella proprietà di sola lettura ro.vendor.build.version.security_patch.
    • Il bootloader può fornire un hash di tutti i dati convalidati dal boot verificato a Keymaster.
  • In Android 9, utilizza i seguenti tag per fornire informazioni sulla versione per le seguenti partizioni:
    • VENDOR_PATCH_LEVEL: partizione vendor
    • BOOT_PATCH_LEVEL: partizione boot
    • OS_PATCH_LEVEL e OS_VERSION: partizione system. OS_VERSION viene rimosso dall'intestazione boot.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 se IKeymaster::upgradeDevice() esegue il roll a un livello di patch superiore, se necessario.

Modifiche all'HAL

Per supportare l'associazione 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 e versioni successive a tutte le chiavi appena generate (o aggiornate). Inoltre, qualsiasi tentativo di utilizzare una chiave che non ha una versione o un livello di patch del sistema operativo corrispondente rispettivamente alla versione o al livello di patch del sistema operativo corrente viene rifiutato con ErrorCode::KEY_REQUIRES_UPGRADE.

Tag::OS_VERSION è un valore UINT che rappresenta le parti principali, secondarie e secondarie di una versione di sistema Android come MMmmss, dove MM è la versione principale, mm è la versione secondaria e ss è la versione 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 come 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 dispositivo
  • keyBlobToUpgrade è la chiave di cui è necessario eseguire l'upgrade
  • upgradeParams sono i parametri necessari per eseguire l'upgrade della chiave. Sono inclusi Tag::APPLICATION_ID e Tag::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 è altrimenti non valido, restituisce ErrorCode::INVALID_KEY_BLOB. Se viene chiamato con una chiave il cui livello del 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 corrente 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 nella 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 essere teoricamente riutilizzato se il dispositivo venisse sottoposto a downgrade. In pratica, il 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 vincolo della versione, il TA Keymaster ha bisogno di un modo per ricevere in modo sicuro la versione e il livello di patch correnti del sistema operativo (informazioni sulla versione) e per assicurarsi che le informazioni ricevute corrispondano strettamente a quelle sul sistema in esecuzione.

Per supportare il caricamento sicuro delle informazioni sulla versione al TA, è stato aggiunto un OS_VERSION campo all'intestazione dell'immagine di avvio. Lo script di compilazione dell'immagine di avvio compila automaticamente questo campo. Gli OEM e gli implementatori di TA keymaster devono collaborare per modificare i bootloader dei dispositivi in modo da estrarre le informazioni sulla versione dall'immagine di avvio e trasmetterle al TA prima dell'avvio del sistema non sicuro. In questo modo, gli utenti malintenzionati non possono interferire con il provisioning delle informazioni sulla versione per l'AT.

È inoltre necessario assicurarsi che l'immagine di sistema abbia le stesse informazioni sulla versione dell'immagine di avvio. A tal fine, il metodo di configurazione è stato aggiunto 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, l'API di analisi del testo restituisce ErrorCode::KEYMASTER_NOT_CONFIGURED.

La prima volta che configure viene chiamato 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 restituireErrorCode::KEYMASTER_NOT_CONFIGURED. Se le informazioni corrispondono,configure restituisce ErrorCode::OK e gli altri metodi configure 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 è prevista la convalida dei contenuti, esiste una breve finestra di opportunità per un malintenzionato per compromettere l'immagine di sistema e forzarla a fornire informazioni sulla versione che corrispondono all'immagine di avvio, ma che non sono 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 venga chiamato molto all'inizio dell'avvio del sistema dovrebbe rendere questa finestra di opportunità difficile da sfruttare.