Vincolo della versione

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'intestazione
    • system.img può continuare a memorizzare il livello di patch e la versione del sistema operativo in proprietà di sola lettura
    • vendor.img memorizza il livello di patch nella proprietà di sola lettura ro.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: 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 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 dispositivo
  • keyBlobToUpgrade è la chiave di cui è necessario eseguire l'upgrade
  • upgradeParams sono i parametri necessari per eseguire l'upgrade della chiave. Questi includono 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 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à.