En Keymaster 1, todas las claves maestras de claves estaban vinculadas criptográficamente a la raíz de confianza del dispositivo o la clave de arranque verificada. En Keymaster 2 y 3, todas las claves también están vinculadas al sistema operativo y al nivel de parche de la imagen del sistema. Esto garantiza que un atacante que descubra una debilidad en una versión anterior del sistema o del software TEE no pueda revertir un dispositivo a la versión vulnerable y usar claves creadas con la versión más nueva. Además, cuando se usa una clave con una versión y un nivel de parche dados en un dispositivo que se ha actualizado a una versión o nivel de parche más reciente, la clave se actualiza antes de que se pueda usar y la versión anterior de la clave se invalida. De esta manera, a medida que se actualiza el dispositivo, las teclas avanzarán junto con el dispositivo, pero cualquier reversión del dispositivo a una versión anterior hará que las teclas queden inutilizables.
Para soportar la estructura modular de Treble y romper el enlace de system.img con boot.img, Keymaster 4 cambió el modelo de enlace de versión clave para tener niveles de parche separados para cada partición. Esto permite que cada partición se actualice de forma independiente, al mismo tiempo que proporciona protección de reversión.
En Android 9, las particiones de boot
, system
y vendor
tienen cada una su propio nivel de parche.
- Los dispositivos con Android Verified Boot (AVB) pueden poner todos los niveles de parches y la versión del sistema en vbmeta, de modo que el cargador de arranque pueda proporcionárselos a Keymaster. Para las particiones encadenadas, la información de la versión de la partición estará en el vbmeta encadenado. En general, la información de la versión debe estar en la
vbmeta struct
que contiene los datos de verificación (hash o hashtree) para una partición determinada. - En dispositivos sin AVB:
- Las implementaciones de arranque verificadas deben proporcionar un hash de los metadatos de la versión al cargador de arranque, de modo que el cargador de arranque pueda proporcionar el hash a Keymaster.
-
boot.img
puede continuar almacenando el nivel de parche en el encabezado -
system.img
puede continuar almacenando el nivel de parche y la versión del sistema operativo en propiedades de solo lectura -
vendor.img
almacena el nivel de parche en la propiedad de solo lecturaro.vendor.build.version.security_patch
. - El cargador de arranque puede proporcionar un hash de todos los datos validados por un arranque verificado para el maestro de claves.
- En Android 9, use las siguientes etiquetas para proporcionar información de versión para las siguientes particiones:
-
VENDOR_PATCH_LEVEL
: partición devendor
-
BOOT_PATCH_LEVEL
: partición deboot
-
OS_PATCH_LEVEL
yOS_VERSION
: partición delsystem
. (OS_VERSION
se elimina del encabezadoboot.img
.
-
- Las implementaciones de Keymaster deben tratar todos los niveles de parche de forma independiente. Las claves se pueden usar si toda la información de la versión coincide con los valores asociados con una clave, y
IKeymaster::upgradeDevice()
pasa a un nivel de parche superior si es necesario.
Cambios HAL
Para admitir el enlace de versión y la atestación de versión, Android 7.1 agregó las etiquetas Tag::OS_VERSION
y Tag::OS_PATCHLEVEL
y los métodos configure
y upgradeKey
. Las etiquetas de versión se agregan automáticamente mediante las implementaciones de Keymaster 2+ a todas las claves recién generadas (o actualizadas). Además, cualquier intento de usar una clave que no tenga una versión del sistema operativo o un nivel de parche que coincida con la versión del sistema operativo actual o el nivel de parche, respectivamente, se rechaza con ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
es un valor UINT
que representa las partes principal, secundaria y secundaria de una versión del sistema Android como MMmmss, donde MM es la versión principal, mm es la versión secundaria y ss es la versión secundaria. Por ejemplo, 6.1.2 se representaría como 060102.
Tag::OS_PATCHLEVEL
es un valor UINT
que representa el año y el mes de la última actualización del sistema como AAAAMM, donde AAAA es el año de cuatro dígitos y MM es el mes de dos dígitos. Por ejemplo, marzo de 2016 se representaría como 201603.
UpgradeKey
Para permitir que las claves se actualicen a la nueva versión del sistema operativo y al nivel de parche de la imagen del sistema, Android 7.1 agregó el método upgradeKey
a la HAL:
Maestro de llaves 3
upgradeKey(vec keyBlobToUpgrade, vec upgradeParams) generates(ErrorCode error, vec upgradedKeyBlob);
Maestro de llaves 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
es la estructura del dispositivo -
keyBlobToUpgrade
es la clave que debe actualizarse -
upgradeParams
son parámetros necesarios para actualizar la clave. Estos incluiránTag::APPLICATION_ID
yTag::APPLICATION_DATA
, que son necesarios para descifrar el blob de claves, si se proporcionaron durante la generación. -
upgradedKeyBlob
es el parámetro de salida, que se usa para devolver el nuevo blob de claves.
Si se llama a upgradeKey
con un blob de claves que no se puede analizar o que no es válido, devuelve ErrorCode::INVALID_KEY_BLOB
. Si se llama con una clave cuyo nivel de parche es mayor que el valor actual del sistema, devuelve ErrorCode::INVALID_ARGUMENT
. Si se llama con una clave cuya versión del sistema operativo es mayor que el valor actual del sistema y el valor del sistema es distinto de cero, devuelve ErrorCode::INVALID_ARGUMENT
. Se permiten actualizaciones de la versión del sistema operativo de cero a cero. En caso de errores al comunicarse con el mundo seguro, devuelve un valor de error apropiado (p. ej., ErrorCode::SECURE_HW_ACCESS_DENIED
, ErrorCode::SECURE_HW_BUSY
). De lo contrario, devuelve ErrorCode::OK
y devuelve un nuevo blob de claves en upgradedKeyBlob
.
keyBlobToUpgrade
sigue siendo válido después de la llamada upgradeKey
y, en teoría, podría volver a utilizarse si se degradara el dispositivo. En la práctica, el almacén de claves generalmente llama a deleteKey
en el blob keyBlobToUpgrade
poco después de la llamada a upgradeKey
. Si keyBlobToUpgrade
tenía la etiqueta Tag::ROLLBACK_RESISTANT
, entonces la upgradedKeyBlob
de KeyBlob también debería tenerla (y debería ser resistente a la reversión).
Configuración segura
Para implementar el enlace de versiones, el keymaster TA necesita una forma de recibir de forma segura la versión actual del sistema operativo y el nivel de parche (información de la versión) y garantizar que la información que recibe coincida con la información sobre el sistema en ejecución.
Para respaldar la entrega segura de información de la versión al TA, se agregó un campo OS_VERSION
al encabezado de la imagen de arranque. El script de creación de la imagen de arranque rellena automáticamente este campo. Los OEM y los implementadores de Keymaster TA deben trabajar juntos para modificar los cargadores de arranque del dispositivo para extraer la información de la versión de la imagen de arranque y pasarla al TA antes de que se inicie el sistema no seguro. Esto asegura que los atacantes no puedan interferir con el suministro de información de versión al TA.
También es necesario asegurarse de que la imagen del sistema tenga la misma información de versión que la imagen de arranque. Con ese fin, el método de configuración se ha agregado al keymaster HAL:
keymaster_error_t (*configure)(const struct keymaster2_device* dev, const keymaster_key_param_set_t* params);
El argumento params
contiene Tag::OS_VERSION
y Tag::OS_PATCHLEVEL
. Los clientes keymaster2 llaman a este método después de abrir HAL, pero antes de llamar a cualquier otro método. Si se llama a cualquier otro método antes de configurar, el TA devuelve ErrorCode::KEYMASTER_NOT_CONFIGURED
.
La primera vez que se llama a configure
después de que se inicia el dispositivo, debe verificar que la información de la versión proporcionada coincida con la proporcionada por el gestor de arranque. Si la información de la versión no coincide, configure
devuelve ErrorCode::INVALID_ARGUMENT
, y todos los demás métodos keymaster continúan devolviendo ErrorCode::KEYMASTER_NOT_CONFIGURED
. Si la información coincide, configure
devuelve ErrorCode::OK
y otros métodos keymaster comienzan a funcionar normalmente.
Las llamadas posteriores a configure
devuelven el mismo valor devuelto por la primera llamada y no cambian el estado de keymaster. Tenga en cuenta que este proceso REQUIERE que todas las OTA actualicen tanto el sistema como las imágenes de arranque; no se pueden actualizar por separado para mantener sincronizada la información de la versión.
Debido a que el sistema llamará a configure
cuyo contenido pretende validar, existe una pequeña ventana de oportunidad para que un atacante comprometa la imagen del sistema y lo obligue a proporcionar información de la versión que coincida con la imagen de arranque, pero que no es la real. versión del sistema. La combinación de la verificación de la imagen de arranque, la validación dm-verity del contenido de la imagen del sistema y el hecho de que configure
se llama muy temprano en el arranque del sistema debería hacer que esta ventana de oportunidad sea difícil de aprovechar.