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 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 descubre 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 utiliza una clave con una versión y un nivel de parche determinados en un dispositivo que se ha actualizado a una versión o nivel de parche más reciente, la clave se actualiza antes de poder usarse y la versión anterior de la clave se invalida. De esta manera, a medida que se actualiza el dispositivo, las claves avanzarán junto con el dispositivo, pero cualquier reversión del dispositivo a una versión anterior hará que las claves queden inutilizables.
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 la versión clave para tener niveles de parche separados para cada partición. Esto permite que cada partición se actualice de forma independiente y, al mismo tiempo, proporciona protección contra reversiones.
En Android 9, las particiones boot
, system
y vendor
tienen cada una su propio nivel de parche.
- Los dispositivos con arranque verificado de Android (AVB) pueden colocar todos los niveles de parche y la versión del sistema en vbmeta, para que el gestor de arranque pueda proporcionárselos a Keymaster. Para particiones encadenadas, la información de la versión de la partición estará en 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 gestor de arranque, de modo que el gestor 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 gestor de arranque puede proporcionar un hash de todos los datos validados mediante el arranque verificado al keymaster.
- En Android 9, utilice 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 elimina del encabezadoboot.img
.
-
- Las implementaciones de Keymaster deben tratar todos los niveles de parches de forma independiente. Las claves se pueden utilizar 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 versiones y la atestación de versiones, 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 utilizar una clave que no tenga una versión del sistema operativo o un nivel de parche que coincida con la versión actual del sistema operativo 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 submenor 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 submenor. Por ejemplo, 6.1.2 se representaría como 060102.
Tag::OS_PATCHLEVEL
es un valor UINT
que representa el año y 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.
Clave de actualización
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 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 utiliza para devolver el nuevo blob de claves.
Si se llama 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 no es 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 (por ejemplo 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 usarse nuevamente si el dispositivo se degradara. En la práctica, el almacén de claves generalmente llama deleteKey
en el blob keyBlobToUpgrade
poco después de la llamada a upgradeKey
. Si keyBlobToUpgrade
tenía la etiqueta Tag::ROLLBACK_RESISTANT
, entonces upgradedKeyBlob
debería tenerla también (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 firmemente con la información sobre el 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 inicio. El script de creación de la imagen de arranque completa 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 garantiza 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 inicio. Para ello, se ha agregado el método de configuración 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 configure
después de que se inicia el dispositivo, se debe verificar que la información de 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 para 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 cuyo contenido se pretende validar llamará a configure
, existe una estrecha ventana de oportunidad para que un atacante comprometa la imagen del sistema y lo obligue a proporcionar información de versión que coincida con la imagen de inicio, pero que no sea la real. versión del sistema. La combinación de verificación de la imagen de arranque, validación dm-verity del contenido de la imagen del sistema y el hecho de que se llama configure
muy temprano en el arranque del sistema debería hacer que esta ventana de oportunidad sea difícil de explotar.