En Keymaster 1, todas las claves de Keymaster estaban vinculadas criptográficamente a la raíz de confianza del dispositivo o a la clave de inicio verificado. 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 vulnerabilidad en una versión antigua del sistema o del software de TEE no pueda revertir un dispositivo a la versión vulnerable y usar las claves creadas con la versión más reciente. Además, cuando se usa una clave con una versión y un nivel de parche determinados en un dispositivo que se actualizó a una versión o un nivel de parche más recientes, la clave se actualiza antes de que se pueda usar y se invalida la versión anterior. De esta manera, a medida que se actualiza el dispositivo, las claves se actualizan junto con él, pero cualquier reversión del dispositivo a una versión anterior hace que las claves no se puedan usar.
Para admitir la estructura modular de Treble y romper la vinculación de system.img a boot.img, Keymaster 4 cambió el modelo de vinculación de versión de la clave para tener niveles de parche separados para cada partición. Gracias a ello, se actualiza independientemente cada partición y, al mismo tiempo, se ofrece protección contra reversiones.
En Android 9, las particiones boot
, system
y vendor
tienen su propio nivel de parche.
- Los dispositivos con inicio verificado de Android (AVB) pueden colocar todos los niveles de parches y la versión del sistema en vbmeta, de modo que el bootloader pueda proporcionarlos a Keymaster. En el caso de las particiones encadenadas, la información de la versión de la partición se encuentra en el vbmeta encadenado. En general, la información de la versión debe estar en el
vbmeta struct
que contiene los datos de verificación (hash o hashtree) de una partición determinada. - En dispositivos sin AVB:
- Las implementaciones de inicio verificado deben proporcionar un hash de los metadatos de la versión al bootloader para que este pueda proporcionar el hash a Keymaster.
boot.img
puede seguir almacenando el nivel de parche en el encabezado.system.img
puede seguir almacenando el nivel de parche y la versión del SO en propiedades de solo lectura.vendor.img
almacena el nivel de parche en la propiedad de solo lecturaro.vendor.build.version.security_patch
.- El bootloader puede proporcionar un hash de todos los datos validados por el inicio verificado a Keymaster.
- En Android 9, usa las siguientes etiquetas para proporcionar información de versión para las siguientes particiones:
VENDOR_PATCH_LEVEL
: Particiónvendor
BOOT_PATCH_LEVEL
: Particiónboot
OS_PATCH_LEVEL
yOS_VERSION
: Particiónsystem
. (OS_VERSION
se quita del encabezadoboot.img
).
-
Las implementaciones de Keymaster deben tratar todos los niveles de parches 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()
se traslada a un nivel de parche más alto si es necesario.
Cambios en HAL
Para admitir la vinculación y la certificación de versiones, Android 7.1 agregó las etiquetas Tag::OS_VERSION
y Tag::OS_PATCHLEVEL
, y los métodos configure
y upgradeKey
. Las implementaciones de Keymaster 2 y versiones posteriores agregan automáticamente las etiquetas de versión a todas las claves generadas (o actualizadas) recientemente. Además, cualquier intento de usar una clave que no tenga una versión o un nivel de parche del SO que coincida con la versión o el nivel de parche del SO actual se rechaza con ErrorCode::KEY_REQUIRES_UPGRADE
.
Tag::OS_VERSION
es un valor UINT
que representa las partes principales, secundarias y subsecundarias 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 subsecundaria. 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, en el que 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 SO y al nivel de parche de la imagen del sistema, Android 7.1 agregó el método upgradeKey
al 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
es la estructura del dispositivo.keyBlobToUpgrade
es la clave que se debe actualizar.upgradeParams
son los parámetros necesarios para actualizar la clave. Estos incluyenTag::APPLICATION_ID
yTag::APPLICATION_DATA
, que son necesarios para desencriptar el blob de claves, si se proporcionaron durante la generación.upgradedKeyBlob
es el parámetro de salida, que se usa para mostrar el nuevo BLOB de clave.
Si se llama a upgradeKey
con un BLOB de clave que no se puede analizar o que no es válido, se muestra ErrorCode::INVALID_KEY_BLOB
. Si se llama con una clave cuyo nivel de parche es mayor que el valor del sistema actual, muestra ErrorCode::INVALID_ARGUMENT
. Si se lo llama con una clave cuya versión del SO es mayor que el valor actual del sistema y el valor del sistema es distinto de cero, muestra ErrorCode::INVALID_ARGUMENT
. Se permiten las actualizaciones de versión del SO de un valor distinto de cero a cero. En caso de errores de comunicación con el mundo seguro, muestra un valor de error adecuado (por ejemplo, ErrorCode::SECURE_HW_ACCESS_DENIED
o ErrorCode::SECURE_HW_BUSY
). De lo contrario, muestra ErrorCode::OK
y muestra un nuevo blob de clave en upgradedKeyBlob
.
keyBlobToUpgrade
sigue siendo válido después de la llamada a upgradeKey
y, en teoría, se podría volver a usar si se degradara el dispositivo. En la práctica, el almacén de claves suele llamar a deleteKey
en el blob keyBlobToUpgrade
poco después de la llamada a upgradeKey
. Si keyBlobToUpgrade
tenía la etiqueta Tag::ROLLBACK_RESISTANT
, upgradedKeyBlob
también debería tenerla (y debería ser resistente a la reversión).
Configuración segura
Para implementar la vinculación de versiones, el TA de Keymaster necesita una forma de recibir de forma segura la versión actual del SO y el nivel de parche (información de la versión) y asegurarse de que la información que recibe coincida de forma clara con la información del sistema en ejecución.
Para admitir la entrega segura de información de versión al TA, se agregó un campo OS_VERSION
al encabezado de la imagen de arranque. La secuencia de comandos de compilación de la imagen de inicio completa automáticamente este campo. Los OEM y los implementadores de la TA de Keymaster deben trabajar juntos para modificar los bootloaders del dispositivo y extraer la información de la versión de la imagen de arranque y pasarla a la TA antes de que se inicie el sistema no seguro. Esto garantiza que los atacantes no puedan interferir en el aprovisionamiento 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. Para ello, se agregó el método de configuración al HAL de Keymaster:
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 de keymaster2 llaman a este método después de abrir el HAL, pero antes de llamar a cualquier otro método. Si se llama a cualquier otro método antes de configurar, el TA muestra 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 que proporcionó el bootloader. Si la información de la versión no coincide, configure
muestra ErrorCode::INVALID_ARGUMENT
y todos los demás métodos de Keymaster siguen mostrando ErrorCode::KEYMASTER_NOT_CONFIGURED
. Si la información coincide, configure
muestra ErrorCode::OK
y otros métodos de Keymaster comienzan a funcionar con normalidad.
Las llamadas posteriores a configure
muestran el mismo valor que muestra la primera llamada y no cambian el estado de Keymaster.
Debido a que el sistema cuyo contenido se pretende validar llama a configure
, hay una ventana de oportunidad limitada para que un atacante comprometa la imagen del sistema y la obligue a proporcionar información de versión que coincida con la imagen de inicio, pero que no sea la versión real del sistema. La combinación de la verificación de la imagen de inicio, la validación de dm-verity del contenido de la imagen del sistema y el hecho de que se llame a configure
muy al principio del inicio del sistema debería dificultar el aprovechamiento de esta ventana de oportunidad.