La disponibilidad de un entorno de ejecución confiable en un sistema en un chip (SoC) ofrece una oportunidad para que los dispositivos Android brinden servicios de seguridad sólidos respaldados por hardware al sistema operativo Android, a los servicios de la plataforma e incluso a las aplicaciones de terceros. Los desarrolladores que busquen las extensiones específicas de Android deben ir a android.security.keystore .
Antes de Android 6.0, Android ya tenía una API de servicios criptográficos simple respaldada por hardware, proporcionada por las versiones 0.2 y 0.3 de Keymaster Hardware Abstraction Layer (HAL). Keystore proporcionó operaciones de verificación y firma digital, además de generación e importación de pares de claves de firma asimétricas. Esto ya está implementado en muchos dispositivos, pero hay muchos objetivos de seguridad que no se pueden lograr fácilmente con solo una API de firma. Keystore en Android 6.0 amplió la API de Keystore para proporcionar una gama más amplia de capacidades.
En Android 6.0, Keystore agregó primitivas criptográficas simétricas , AES y HMAC, y un sistema de control de acceso para claves respaldadas por hardware. Los controles de acceso se especifican durante la generación de claves y se aplican durante la vigencia de la clave. Las claves se pueden restringir para que solo se puedan usar después de que el usuario haya sido autenticado, y solo para propósitos específicos o con parámetros criptográficos específicos. Para obtener más información, consulte las páginas Etiquetas y funciones de autorización.
Además de expandir el rango de primitivas criptográficas, Keystore en Android 6.0 agregó lo siguiente:
- Un esquema de control de uso para permitir que se limite el uso de claves, para mitigar el riesgo de compromiso de seguridad debido al uso indebido de claves
- Un esquema de control de acceso para habilitar la restricción de claves para usuarios específicos, clientes y un rango de tiempo definido
En Android 7.0, Keymaster 2 agregó soporte para atestación de clave y enlace de versión. La atestación de clave proporciona certificados de clave pública que contienen una descripción detallada de la clave y sus controles de acceso, para que la existencia de la clave en hardware seguro y su configuración sean verificables de forma remota.
El enlace de versión vincula las claves al sistema operativo y la versión de nivel de parche. Esto garantiza que un atacante que descubra una debilidad en una versión anterior del sistema o el 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. A medida que se actualiza el dispositivo, las teclas avanzan junto con el dispositivo, pero cualquier reversión del dispositivo a una versión anterior hace que las teclas queden inutilizables.
En Android 8.0, Keymaster 3 pasó de la capa de abstracción de hardware (HAL) de estructura C de estilo antiguo a la interfaz HAL de C++ generada a partir de una definición en el nuevo lenguaje de definición de interfaz de hardware (HIDL). Como parte del cambio, muchos de los tipos de argumento cambiaron, aunque los tipos y métodos tienen una correspondencia uno a uno con los tipos antiguos y los métodos de estructura HAL. Consulte la página Funciones para obtener más detalles.
Además de esta revisión de la interfaz, Android 8.0 amplió la función de atestación de Keymaster 2 para admitir la atestación de ID . La atestación de ID proporciona un mecanismo limitado y opcional para atestiguar fuertemente los identificadores de hardware, como el número de serie del dispositivo, el nombre del producto y la ID del teléfono (IMEI/MEID). Para implementar esta adición, Android 8.0 cambió el esquema de atestación ASN.1 para agregar atestación de ID. Las implementaciones de Keymaster deben encontrar alguna forma segura de recuperar los elementos de datos relevantes, así como definir un mecanismo para deshabilitar la función de forma segura y permanente.
En Android 9, las actualizaciones incluyeron:
- Actualizar a Keymaster 4
- Compatibilidad con elementos seguros integrados
- Compatibilidad con la importación de claves seguras
- Compatibilidad con el cifrado 3DES
- Cambios en el enlace de versiones para que boot.img y system.img tengan versiones establecidas por separado para permitir actualizaciones independientes
Glosario
Aquí hay una descripción general rápida de los componentes del almacén de claves y sus relaciones.
AndroidKeystore es la API de Android Framework y el componente utilizado por las aplicaciones para acceder a la funcionalidad Keystore. Se implementa como una extensión de las API estándar de la arquitectura criptográfica de Java y consta de código Java que se ejecuta en el propio espacio de proceso de la aplicación. AndroidKeystore
cumple con las solicitudes de aplicaciones para el comportamiento del almacén de claves al reenviarlas al demonio del almacén de claves.
El demonio del almacén de claves es un demonio del sistema Android que brinda acceso a todas las funciones del almacén de claves a través de una API Binder . Es responsable de almacenar "blobs de claves", que contienen el material de la clave secreta real, encriptado para que Keystore pueda almacenarlos pero no usarlos ni revelarlos.
keymasterd es un servidor HIDL que proporciona acceso a Keymaster TA. (Este nombre no está estandarizado y tiene fines conceptuales).
Keymaster TA (aplicación de confianza) es el software que se ejecuta en un contexto seguro, con mayor frecuencia en TrustZone en un SoC ARM, que proporciona todas las operaciones seguras del almacén de claves, tiene acceso al material de clave sin procesar, valida todas las condiciones de control de acceso en las claves , etc.
LockSettingsService es el componente del sistema Android responsable de la autenticación del usuario, tanto de contraseña como de huella digital. No es parte de Keystore, pero es relevante porque muchas operaciones de claves de Keystore requieren autenticación de usuario. LockSettingsService
interactúa con Gatekeeper TA y Fingerprint TA para obtener tokens de autenticación, que proporciona al daemon del almacén de claves y que finalmente son consumidos por la aplicación Keymaster TA.
Gatekeeper TA (aplicación confiable) es otro componente que se ejecuta en el contexto seguro, que es responsable de autenticar las contraseñas de los usuarios y generar tokens de autenticación que se utilizan para demostrarle al Keymaster TA que se realizó una autenticación para un usuario en particular en un momento determinado.
Fingerprint TA (aplicación confiable) es otro componente que se ejecuta en el contexto seguro que es responsable de autenticar las huellas dactilares del usuario y generar tokens de autenticación que se utilizan para demostrarle al Keymaster TA que se realizó una autenticación para un usuario en particular en un momento determinado.
Arquitectura
La API del almacén de claves de Android y el Keymaster HAL subyacente proporcionan un conjunto básico pero adecuado de primitivas criptográficas para permitir la implementación de protocolos utilizando claves respaldadas por hardware con control de acceso.
Keymaster HAL es una biblioteca de carga dinámica proporcionada por el OEM que utiliza el servicio Keystore para proporcionar servicios criptográficos respaldados por hardware. Para mantener las cosas seguras, las implementaciones de HAL no realizan ninguna operación confidencial en el espacio del usuario, ni siquiera en el espacio del kernel. Las operaciones confidenciales se delegan a un procesador seguro al que se accede a través de alguna interfaz del núcleo. La arquitectura resultante se ve así:

Figura 1. Acceso a Keymaster
Dentro de un dispositivo Android, el "cliente" de Keymaster HAL consta de varias capas (por ejemplo, aplicación, marco, demonio Keystore), pero eso puede ignorarse a los fines de este documento. Esto significa que la API Keymaster HAL descrita es de bajo nivel, utilizada por componentes internos de la plataforma y no expuesta a los desarrolladores de aplicaciones. La API de nivel superior se describe en el sitio para desarrolladores de Android .
El propósito de Keymaster HAL no es implementar algoritmos sensibles a la seguridad, sino solo clasificar y desclasificar solicitudes al mundo seguro. El formato de conexión está definido por la implementación.
Compatibilidad con versiones anteriores
Keymaster 1 HAL es completamente incompatible con las HAL publicadas anteriormente, por ejemplo, Keymaster 0.2 y 0.3. Para facilitar la interoperabilidad en dispositivos que ejecutan Android 5.0 y versiones anteriores que se lanzaron con las HAL de Keymaster anteriores, Keystore proporciona un adaptador que implementa la HAL de Keymaster 1 con llamadas a la biblioteca de hardware existente. El resultado no puede proporcionar la gama completa de funciones en Keymaster 1 HAL. En particular, solo es compatible con los algoritmos RSA y ECDSA, y el adaptador realiza toda la aplicación de autorización de clave, en el mundo no seguro.
Keymaster 2 simplificó aún más la interfaz HAL al eliminar los métodos get_supported_*
y permitir que el método finish()
acepte entradas. Esto reduce la cantidad de viajes de ida y vuelta al TEE en los casos en que la entrada está disponible de una sola vez y simplifica la implementación del descifrado AEAD.
En Android 8.0, Keymaster 3 pasó de la estructura C HAL de estilo antiguo a la interfaz HAL de C++ generada a partir de una definición en el nuevo Lenguaje de definición de interfaz de hardware (HIDL). Se crea una implementación de HAL de estilo nuevo mediante la creación de subclases de la clase IKeymasterDevice
generada e implementando los métodos virtuales puros. Como parte del cambio, muchos de los tipos de argumento han cambiado, aunque los tipos y métodos tienen una correspondencia uno a uno con los tipos antiguos y los métodos de estructura HAL.
Descripción general de HIDL
El lenguaje de definición de interfaz de hardware (HIDL) proporciona un mecanismo de implementación independiente del lenguaje para especificar interfaces de hardware. Las herramientas HIDL actualmente admiten la generación de interfaces C++ y Java. Se espera que la mayoría de los implementadores de Trusted Execution Environment (TEE) encuentren las herramientas de C++ más convenientes, por lo que este documento analiza solo la representación de C++.
Las interfaces HIDL consisten en un conjunto de métodos, expresados como:
methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);
Hay varios tipos predefinidos, y las HAL pueden definir nuevos tipos enumerados y de estructura. Para obtener más detalles sobre HIDL, consulte la sección Referencia .
Un método de ejemplo de Keymaster 3 IKeymasterDevice.hal
es:
generateKey(vec<KeyParameter> keyParams) generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
Este es el equivalente de lo siguiente del keymaster2 HAL:
keymaster_error_t (*generate_key)( const struct keymaster2_device* dev, const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics);
En la versión HIDL, el argumento dev
se elimina porque está implícito. El argumento params
ya no es una estructura que contiene un puntero que hace referencia a una matriz de objetos key_parameter_t
, sino un vec
(vector) que contiene objetos KeyParameter
. Los valores devueltos se enumeran en la cláusula " generates
", incluido un vector de valores uint8_t
para el blob de claves.
El método virtual C++ generado por el compilador HIDL es:
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams, generateKey_cb _hidl_cb) override;
Donde generateKey_cb
es un puntero de función definido como:
std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob, const KeyCharacteristics& keyCharacteristics)>
Es decir, generateKey_cb
es una función que toma los valores devueltos enumerados en la cláusula de generación. La clase de implementación HAL anula este método generateKey
y llama al puntero de función generateKey_cb
para devolver el resultado de la operación a la persona que llama. Tenga en cuenta que la llamada del puntero de función es síncrona . La persona que llama llama generateKey
y generateKey
llama al puntero de función proporcionado, que se ejecuta hasta el final, devolviendo el control a la implementación generateKey
, que luego regresa a la persona que llama.
Para obtener un ejemplo detallado, consulte la implementación predeterminada en hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
. La implementación predeterminada proporciona compatibilidad con versiones anteriores para dispositivos con HALS keymaster0, keymaster1 o keymaster2 de estilo antiguo.
Control de acceso
La regla más básica del control de acceso de Keystore es que cada aplicación tiene su propio espacio de nombres. Pero para cada regla hay una excepción. Keystore tiene algunos mapas codificados que permiten que ciertos componentes del sistema accedan a otros espacios de nombres. Este es un instrumento muy contundente ya que le da a un componente control total sobre otro espacio de nombres. Y luego está la cuestión de los componentes del proveedor como clientes de Keystore. Actualmente no tenemos forma de establecer un espacio de nombres para componentes de proveedores, por ejemplo, suplicante WPA.
Para acomodar los componentes de los proveedores y generalizar el control de acceso sin excepciones codificadas, Keystore 2.0 introduce dominios y espacios de nombres SELinux.
Dominios de almacén de claves
Con los dominios Keystore, podemos desacoplar los espacios de nombres de los UID. Los clientes que acceden a una clave en Keystore deben especificar el dominio, el espacio de nombres y el alias al que desean acceder. Según esta tupla y la identidad de la persona que llama, podemos determinar a qué clave desea acceder la persona que llama y si tiene los permisos adecuados.
Presentamos cinco parámetros de dominio que rigen cómo se puede acceder a las claves. Controlan la semántica del parámetro de espacio de nombres del descriptor clave y cómo se realiza el control de acceso.
-
DOMAIN_APP
: el dominio de la aplicación cubre el comportamiento heredado. Java Keystore SPI utiliza este dominio de forma predeterminada. Cuando se usa este dominio, el argumento del espacio de nombres se ignora y, en su lugar, se usa el UID de la persona que llama. El acceso a este dominio está controlado por la etiqueta Keystore de la clasekeystore_key
en la política de SELinux. -
DOMAIN_SELINUX
: este dominio indica que el espacio de nombres tiene una etiqueta en la política de SELinux. El parámetro del espacio de nombres se busca y se traduce a un contexto de destino, y se realiza una verificación de permisos para el contexto de SELinux que llama para la clasekeystore_key
. Cuando se ha establecido el permiso para la operación dada, la tupla completa se usa para la búsqueda de clave. -
DOMAIN_GRANT
: el dominio de concesión indica que el parámetro de espacio de nombres es un identificador de concesión. El parámetro de alias se ignora. Las comprobaciones de SELinux se realizan cuando se crea la concesión. El control de acceso adicional solo verifica si el UID de la persona que llama coincide con el UID de los beneficiarios de la subvención solicitada. -
DOMAIN_KEY_ID
: este dominio indica que el parámetro de espacio de nombres es una identificación de clave única. La clave en sí puede haber sido creada conDOMAIN_APP
oDOMAIN_SELINUX
. La verificación de permisos se realiza después de que eldomain
y elnamespace
se hayan cargado desde la base de datos de claves de la misma manera que si el dominio, el espacio de nombres y la tupla de alias cargaran el blob. La justificación del dominio de identificación de clave es la continuidad. Al acceder a una clave por alias, las llamadas subsiguientes pueden operar en diferentes claves, ya que es posible que se haya generado o importado una clave nueva y vinculada a este alias. Sin embargo, la identificación de la clave nunca cambia. Entonces, cuando se usa una clave por ID de clave después de que se haya cargado desde la base de datos del almacén de claves usando el alias una vez, uno puede estar seguro de que es la misma clave siempre que la ID de clave aún exista. Esta funcionalidad no está expuesta a los desarrolladores de aplicaciones. En cambio, se usa dentro de Android Keystore SPI para brindar una experiencia más consistente, incluso cuando se usa simultáneamente de manera insegura. -
DOMAIN_BLOB
: el dominio del blob indica que la persona que llama administra el blob por sí mismo. Esto se usa para clientes que necesitan acceder al almacén de claves antes de que se monte la partición de datos. El blob de clave se incluye en el campoblob
del descriptor de clave.
Usando el dominio SELinux, podemos otorgar a los componentes del proveedor acceso a espacios de nombres de Keystore muy específicos que pueden ser compartidos por componentes del sistema, como el cuadro de diálogo de configuración.
Política de SELinux para keystore_key
Las etiquetas de espacios de nombres se configuran mediante el archivo keystore2_key_context
.
Cada línea en estos archivos asigna una identificación de espacio de nombres numérico a una etiqueta SELinux. Por ejemplo,
# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and # Settings to share keystore keys. 102 u:object_r:wifi_key:s0
Después de haber configurado un nuevo espacio de nombres clave de esta manera, podemos darle acceso agregando una política apropiada. Por ejemplo, para permitir que wpa_supplicant
obtenga y use claves en el nuevo espacio de nombres, agregaríamos la siguiente línea a hal_wifi_supplicant.te
:
allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };
Después de configurar el nuevo espacio de nombres, AndroidKeyStore se puede usar casi como de costumbre. La única diferencia es que se debe especificar el ID del espacio de nombres. Para cargar e importar claves desde y hacia Keystore, la identificación del espacio de nombres se especifica mediante AndroidKeyStoreLoadStoreParameter
. Por ejemplo,
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import java.security.KeyStore; KeyStore keystore = KeyStore.getInstance("AndroidKeyStore"); keystore.load(new AndroidKeyStoreLoadStoreParameter(102));
Para generar una clave en un espacio de nombres dado, la identificación del espacio de nombres debe proporcionarse mediante KeyGenParameterSpec.Builder#setNamespace():
import android.security.keystore.KeyGenParameterSpec; KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(); specBuilder.setNamespace(102);
Los siguientes archivos de contexto se pueden usar para configurar los espacios de nombres Keystore 2.0 SELinux. Cada partición tiene un rango reservado diferente de 10 000 ID de espacios de nombres para evitar colisiones.
Dividir | Rango | Archivos de configuración |
---|---|---|
Sistema | 0 ... 9,999 | /system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts |
Sistema Extendido | 10.000 ... 19.999 | /system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts |
Producto | 20.000 ... 29.999 | /product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts |
Proveedor | 30.000 ... 39.999 | /vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts |
El cliente solicita la clave solicitando el dominio SELinux y el espacio de nombres virtual deseado, en este caso "wifi_key"
, por su id numérico.
Por encima de eso, se han definido los siguientes espacios de nombres. Si reemplazan reglas especiales, la siguiente tabla indica el UID al que solían corresponder.
Id. de espacio de nombres | Etiqueta de política SE | UID | Descripción |
---|---|---|---|
0 | su_clave | N / A | Clave de superusuario. Solo se usa para probar en compilaciones de depuración de usuario y eng. No relevante en las compilaciones de los usuarios. |
1 | shell_key | N / A | Espacio de nombres disponible para shell. Se usa principalmente para pruebas, pero también se puede usar en compilaciones de usuarios desde la línea de comandos. |
100 | llave_vold | N / A | Destinado a ser utilizado por vold. |
101 | odsing_key | N / A | Utilizado por el daemon de firma en el dispositivo. |
102 | clave_wifi | AYUDA_WIFI(1010) | Utilizado por el sistema Wifi de Android, incluido wpa_supplicant. |
120 | resume_on_reboot_key | SISTEMA_AYUDA(1000) | Utilizado por el servidor del sistema de Android para admitir la reanudación al reiniciar. |
Vectores de acceso
La clase keystore_key
de SELinux ha envejecido bastante y algunos de los permisos, como verify
o sign
han perdido su significado. Aquí está el nuevo conjunto de permisos, keystore2_key
, que Keystore 2.0 aplicará.
Permiso | Significado |
---|---|
delete | Comprobado al eliminar claves del almacén de claves. |
get_info | Marcado cuando se solicitan los metadatos de una clave. |
grant | La persona que llama necesita este permiso para crear una concesión a la clave en el contexto de destino. |
manage_blob | La persona que llama puede usar DOMAIN_BLOB en el espacio de nombres de SELinux dado, y así administrar los blobs por sí mismo. Esto es especialmente útil para vold. |
rebind | Este permiso controla si un alias se puede volver a vincular a una clave nueva. Esto es necesario para la inserción e implica que se eliminará la clave enlazada anteriormente. Es básicamente un permiso de inserción, pero captura mejor la semántica del almacén de claves. |
req_forced_op | Los clientes con este permiso pueden crear operaciones que no se pueden podar, y la creación de operaciones nunca falla a menos que todas las ranuras de operación estén ocupadas por operaciones que no se pueden podar. |
update | Necesario para actualizar el subcomponente de una clave. |
use | Marcado al crear una operación de Keymint que utiliza el material clave, por ejemplo, para firmar, cifrar/descifrar. |
use_dev_id | Obligatorio al generar información de identificación del dispositivo, como la atestación de identificación del dispositivo. |
Además, dividimos un conjunto de permisos de almacén de claves no específicos de clave en la clase de seguridad keystore2
de SELinux:
Permiso | Significado |
---|---|
add_auth | Requerido por el proveedor de autenticación como Gatekeeper o BiometricsManager para agregar tokens de autenticación. |
clear_ns | Anteriormente clear_uid, este permiso permite que una persona que no sea propietaria de un espacio de nombres elimine todas las claves en ese espacio de nombres. |
list | Requerido por el sistema para enumerar claves por varias propiedades, como la propiedad o el límite de autenticación. Este permiso no es necesario para los llamantes que enumeran sus propios espacios de nombres. Esto está cubierto por el permiso get_info . |
lock | Este permiso permite bloquear Keystore, es decir, desalojar la clave maestra, de modo que las claves vinculadas a la autenticación se vuelvan inutilizables e imposibles de crear. |
reset | Este permiso permite restablecer Keystore a los valores predeterminados de fábrica, eliminando todas las claves que no son vitales para el funcionamiento del sistema operativo Android. |
unlock | Este permiso es necesario para intentar desbloquear la clave maestra para claves enlazadas de autenticación. |
La disponibilidad de un entorno de ejecución confiable en un sistema en un chip (SoC) ofrece una oportunidad para que los dispositivos Android brinden servicios de seguridad sólidos respaldados por hardware al sistema operativo Android, a los servicios de la plataforma e incluso a las aplicaciones de terceros. Los desarrolladores que busquen las extensiones específicas de Android deben ir a android.security.keystore .
Antes de Android 6.0, Android ya tenía una API de servicios criptográficos simple respaldada por hardware, proporcionada por las versiones 0.2 y 0.3 de Keymaster Hardware Abstraction Layer (HAL). Keystore proporcionó operaciones de verificación y firma digital, además de generación e importación de pares de claves de firma asimétricas. Esto ya está implementado en muchos dispositivos, pero hay muchos objetivos de seguridad que no se pueden lograr fácilmente con solo una API de firma. Keystore en Android 6.0 amplió la API de Keystore para proporcionar una gama más amplia de capacidades.
En Android 6.0, Keystore agregó primitivas criptográficas simétricas , AES y HMAC, y un sistema de control de acceso para claves respaldadas por hardware. Los controles de acceso se especifican durante la generación de claves y se aplican durante la vigencia de la clave. Las claves se pueden restringir para que solo se puedan usar después de que el usuario haya sido autenticado, y solo para propósitos específicos o con parámetros criptográficos específicos. Para obtener más información, consulte las páginas Etiquetas y funciones de autorización.
Además de expandir el rango de primitivas criptográficas, Keystore en Android 6.0 agregó lo siguiente:
- Un esquema de control de uso para permitir que se limite el uso de claves, para mitigar el riesgo de compromiso de seguridad debido al uso indebido de claves
- Un esquema de control de acceso para habilitar la restricción de claves para usuarios específicos, clientes y un rango de tiempo definido
En Android 7.0, Keymaster 2 agregó soporte para atestación de clave y enlace de versión. La atestación de clave proporciona certificados de clave pública que contienen una descripción detallada de la clave y sus controles de acceso, para que la existencia de la clave en hardware seguro y su configuración sean verificables de forma remota.
El enlace de versión vincula las claves al sistema operativo y la versión de nivel de parche. Esto garantiza que un atacante que descubra una debilidad en una versión anterior del sistema o el 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. A medida que se actualiza el dispositivo, las teclas avanzan junto con el dispositivo, pero cualquier reversión del dispositivo a una versión anterior hace que las teclas queden inutilizables.
En Android 8.0, Keymaster 3 pasó de la capa de abstracción de hardware (HAL) de estructura C de estilo antiguo a la interfaz HAL de C++ generada a partir de una definición en el nuevo lenguaje de definición de interfaz de hardware (HIDL). Como parte del cambio, muchos de los tipos de argumento cambiaron, aunque los tipos y métodos tienen una correspondencia uno a uno con los tipos antiguos y los métodos de estructura HAL. Consulte la página Funciones para obtener más detalles.
Además de esta revisión de la interfaz, Android 8.0 amplió la función de atestación de Keymaster 2 para admitir la atestación de ID . La atestación de ID proporciona un mecanismo limitado y opcional para atestiguar fuertemente los identificadores de hardware, como el número de serie del dispositivo, el nombre del producto y la ID del teléfono (IMEI/MEID). Para implementar esta adición, Android 8.0 cambió el esquema de atestación ASN.1 para agregar atestación de ID. Las implementaciones de Keymaster deben encontrar alguna forma segura de recuperar los elementos de datos relevantes, así como definir un mecanismo para deshabilitar la función de forma segura y permanente.
En Android 9, las actualizaciones incluyeron:
- Actualizar a Keymaster 4
- Compatibilidad con elementos seguros integrados
- Compatibilidad con la importación de claves seguras
- Compatibilidad con el cifrado 3DES
- Cambios en el enlace de versiones para que boot.img y system.img tengan versiones establecidas por separado para permitir actualizaciones independientes
Glosario
Aquí hay una descripción general rápida de los componentes del almacén de claves y sus relaciones.
AndroidKeystore es la API de Android Framework y el componente utilizado por las aplicaciones para acceder a la funcionalidad Keystore. Se implementa como una extensión de las API estándar de la arquitectura criptográfica de Java y consta de código Java que se ejecuta en el propio espacio de proceso de la aplicación. AndroidKeystore
cumple con las solicitudes de aplicaciones para el comportamiento del almacén de claves al reenviarlas al demonio del almacén de claves.
El demonio del almacén de claves es un demonio del sistema Android que brinda acceso a todas las funciones del almacén de claves a través de una API Binder . Es responsable de almacenar "blobs de claves", que contienen el material de la clave secreta real, encriptado para que Keystore pueda almacenarlos pero no usarlos ni revelarlos.
keymasterd es un servidor HIDL que proporciona acceso a Keymaster TA. (Este nombre no está estandarizado y tiene fines conceptuales).
Keymaster TA (aplicación de confianza) es el software que se ejecuta en un contexto seguro, con mayor frecuencia en TrustZone en un SoC ARM, que proporciona todas las operaciones seguras del almacén de claves, tiene acceso al material de clave sin procesar, valida todas las condiciones de control de acceso en las claves , etc.
LockSettingsService es el componente del sistema Android responsable de la autenticación del usuario, tanto de contraseña como de huella digital. No es parte de Keystore, pero es relevante porque muchas operaciones de claves de Keystore requieren autenticación de usuario. LockSettingsService
interactúa con Gatekeeper TA y Fingerprint TA para obtener tokens de autenticación, que proporciona al daemon del almacén de claves y que finalmente son consumidos por la aplicación Keymaster TA.
Gatekeeper TA (aplicación confiable) es otro componente que se ejecuta en el contexto seguro, que es responsable de autenticar las contraseñas de los usuarios y generar tokens de autenticación que se utilizan para demostrarle al Keymaster TA que se realizó una autenticación para un usuario en particular en un momento determinado.
Fingerprint TA (aplicación confiable) es otro componente que se ejecuta en el contexto seguro que es responsable de autenticar las huellas digitales del usuario y generar tokens de autenticación que se utilizan para demostrarle al Keymaster TA que se realizó una autenticación para un usuario en particular en un momento determinado.
Arquitectura
La API del almacén de claves de Android y el Keymaster HAL subyacente proporcionan un conjunto básico pero adecuado de primitivas criptográficas para permitir la implementación de protocolos utilizando claves respaldadas por hardware con control de acceso.
Keymaster HAL es una biblioteca de carga dinámica proporcionada por el OEM que utiliza el servicio Keystore para proporcionar servicios criptográficos respaldados por hardware. Para mantener las cosas seguras, las implementaciones de HAL no realizan ninguna operación confidencial en el espacio del usuario, ni siquiera en el espacio del kernel. Las operaciones confidenciales se delegan a un procesador seguro al que se accede a través de alguna interfaz del kernel. La arquitectura resultante se ve así:

Figura 1. Acceso a Keymaster
Dentro de un dispositivo Android, el "cliente" de Keymaster HAL consta de varias capas (por ejemplo, aplicación, marco, demonio Keystore), pero eso puede ignorarse a los fines de este documento. Esto significa que la API Keymaster HAL descrita es de bajo nivel, utilizada por componentes internos de la plataforma y no expuesta a los desarrolladores de aplicaciones. La API de nivel superior se describe en el sitio para desarrolladores de Android .
El propósito de Keymaster HAL no es implementar algoritmos sensibles a la seguridad, sino solo clasificar y desclasificar solicitudes al mundo seguro. El formato de conexión está definido por la implementación.
Compatibilidad con versiones anteriores
Keymaster 1 HAL es completamente incompatible con las HAL publicadas anteriormente, por ejemplo, Keymaster 0.2 y 0.3. Para facilitar la interoperabilidad en dispositivos que ejecutan Android 5.0 y versiones anteriores que se lanzaron con las HAL de Keymaster anteriores, Keystore proporciona un adaptador que implementa la HAL de Keymaster 1 con llamadas a la biblioteca de hardware existente. El resultado no puede proporcionar la gama completa de funciones en Keymaster 1 HAL. En particular, solo es compatible con los algoritmos RSA y ECDSA, y el adaptador realiza toda la aplicación de autorización de clave, en el mundo no seguro.
Keymaster 2 simplificó aún más la interfaz HAL al eliminar los métodos get_supported_*
y permitir que el método finish()
acepte entradas. Esto reduce la cantidad de viajes de ida y vuelta al TEE en los casos en que la entrada está disponible de una sola vez y simplifica la implementación del descifrado AEAD.
En Android 8.0, Keymaster 3 pasó de la estructura C HAL de estilo antiguo a la interfaz HAL de C++ generada a partir de una definición en el nuevo Lenguaje de definición de interfaz de hardware (HIDL). Se crea una implementación de HAL de estilo nuevo mediante la creación de subclases de la clase IKeymasterDevice
generada e implementando los métodos virtuales puros. Como parte del cambio, muchos de los tipos de argumento han cambiado, aunque los tipos y métodos tienen una correspondencia uno a uno con los tipos antiguos y los métodos de estructura HAL.
Descripción general de HIDL
El lenguaje de definición de interfaz de hardware (HIDL) proporciona un mecanismo de implementación independiente del lenguaje para especificar interfaces de hardware. Las herramientas HIDL actualmente admiten la generación de interfaces C++ y Java. Se espera que la mayoría de los implementadores de Trusted Execution Environment (TEE) encuentren las herramientas de C++ más convenientes, por lo que este documento analiza solo la representación de C++.
Las interfaces HIDL consisten en un conjunto de métodos, expresados como:
methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);
Hay varios tipos predefinidos, y las HAL pueden definir nuevos tipos enumerados y de estructura. Para obtener más detalles sobre HIDL, consulte la sección Referencia .
Un método de ejemplo de Keymaster 3 IKeymasterDevice.hal
es:
generateKey(vec<KeyParameter> keyParams) generates(ErrorCode error, vec<uint8_t> keyBlob, KeyCharacteristics keyCharacteristics);
Este es el equivalente de lo siguiente del keymaster2 HAL:
keymaster_error_t (*generate_key)( const struct keymaster2_device* dev, const keymaster_key_param_set_t* params, keymaster_key_blob_t* key_blob, keymaster_key_characteristics_t* characteristics);
En la versión HIDL, el argumento dev
se elimina porque está implícito. El argumento params
ya no es una estructura que contiene un puntero que hace referencia a una matriz de objetos key_parameter_t
, sino un vec
(vector) que contiene objetos KeyParameter
. Los valores devueltos se enumeran en la cláusula " generates
", incluido un vector de valores uint8_t
para el blob de claves.
El método virtual C++ generado por el compilador HIDL es:
Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams, generateKey_cb _hidl_cb) override;
Donde generateKey_cb
es un puntero de función definido como:
std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob, const KeyCharacteristics& keyCharacteristics)>
Es decir, generateKey_cb
es una función que toma los valores devueltos enumerados en la cláusula de generación. La clase de implementación HAL anula este método generateKey
y llama al puntero de función generateKey_cb
para devolver el resultado de la operación a la persona que llama. Tenga en cuenta que la llamada del puntero de función es síncrona . La persona que llama llama generateKey
y generateKey
llama al puntero de función proporcionado, que se ejecuta hasta el final, devolviendo el control a la implementación generateKey
, que luego regresa a la persona que llama.
Para obtener un ejemplo detallado, consulte la implementación predeterminada en hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp
. La implementación predeterminada proporciona compatibilidad con versiones anteriores para dispositivos con HALS keymaster0, keymaster1 o keymaster2 de estilo antiguo.
Control de acceso
La regla más básica del control de acceso de Keystore es que cada aplicación tiene su propio espacio de nombres. Pero para cada regla hay una excepción. Keystore tiene algunos mapas codificados que permiten que ciertos componentes del sistema accedan a otros espacios de nombres. Este es un instrumento muy contundente ya que le da a un componente control total sobre otro espacio de nombres. Y luego está la cuestión de los componentes del proveedor como clientes de Keystore. Actualmente no tenemos forma de establecer un espacio de nombres para componentes de proveedores, por ejemplo, suplicante WPA.
Para acomodar los componentes de los proveedores y generalizar el control de acceso sin excepciones codificadas, Keystore 2.0 introduce dominios y espacios de nombres SELinux.
Dominios de almacén de claves
Con los dominios Keystore, podemos desacoplar los espacios de nombres de los UID. Los clientes que acceden a una clave en Keystore deben especificar el dominio, el espacio de nombres y el alias al que desean acceder. Según esta tupla y la identidad de la persona que llama, podemos determinar a qué clave desea acceder la persona que llama y si tiene los permisos adecuados.
Presentamos cinco parámetros de dominio que rigen cómo se puede acceder a las claves. Controlan la semántica del parámetro de espacio de nombres del descriptor clave y cómo se realiza el control de acceso.
-
DOMAIN_APP
: el dominio de la aplicación cubre el comportamiento heredado. Java Keystore SPI utiliza este dominio de forma predeterminada. Cuando se usa este dominio, el argumento del espacio de nombres se ignora y, en su lugar, se usa el UID de la persona que llama. El acceso a este dominio está controlado por la etiqueta Keystore de la clasekeystore_key
en la política de SELinux. -
DOMAIN_SELINUX
: este dominio indica que el espacio de nombres tiene una etiqueta en la política de SELinux. El parámetro del espacio de nombres se busca y se traduce a un contexto de destino, y se realiza una verificación de permisos para el contexto de SELinux que llama para la clasekeystore_key
. Cuando se ha establecido el permiso para la operación dada, la tupla completa se usa para la búsqueda de claves. -
DOMAIN_GRANT
: el dominio de concesión indica que el parámetro de espacio de nombres es un identificador de concesión. The alias parameter is ignored. SELinux checks are performed when the grant is created. Further access control only checks if the caller UID matches the grantees UID of the requested grant. -
DOMAIN_KEY_ID
: This domain indicates that the namespace parameter is a unique key id. The key itself may have been created withDOMAIN_APP
orDOMAIN_SELINUX
. The permission check is performed after thedomain
and thenamespace
have been loaded from the key database in the same way as if the blob was loaded by the domain, namespace, and alias tuple. The rationale for the key id domain is continuity. When accessing a key by alias, subsequent calls may operate on different keys, because a new key may have been generated or imported and bound to this alias. The key id, however, never changes. So when using a key by key id after it has been loaded from the Keystore database using the alias once, one can be certain that it is the same key as long as the key id still exists. This functionality is not exposed to app developers. Instead, it is used within the Android Keystore SPI to provide a more consistent experience even when used concurrently in an unsafe way. -
DOMAIN_BLOB
: The blob domain indicates that the caller manages the blob by itself. This is used for clients that need to access the Keystore before the data partition is mounted. The key blob is included in theblob
field of the key descriptor.
Using the SELinux domain, we can give vendor components access to very specific Keystore namespaces which can be shared by system components such as the settings dialog.
SELinux policy for keystore_key
Namespace labels are configured using the keystore2_key_context
file.
Each line in these files maps a numeric namespace id to an SELinux label. For example,
# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and # Settings to share keystore keys. 102 u:object_r:wifi_key:s0
After having set up a new key namespace in this way, we can give access to it by adding an appropriate policy. For example, to allow wpa_supplicant
to get and use keys in the new namespace we would add the following line to hal_wifi_supplicant.te
:
allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };
After setting up the new namespace, AndroidKeyStore can be used almost as usual. The only difference is that the namespace ID must be specified. For loading and importing keys from and into Keystore, the namespace id is specified using the AndroidKeyStoreLoadStoreParameter
. For example,
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter; import java.security.KeyStore; KeyStore keystore = KeyStore.getInstance("AndroidKeyStore"); keystore.load(new AndroidKeyStoreLoadStoreParameter(102));
To generate a key in a given namespace, the namespace id must be given using KeyGenParameterSpec.Builder#setNamespace():
import android.security.keystore.KeyGenParameterSpec; KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(); specBuilder.setNamespace(102);
The following context files may be used to configure Keystore 2.0 SELinux namespaces. Each partition has a different reserved range of 10,000 namespace ids to avoid collisions.
Partition | Range | Config files |
---|---|---|
System | 0 ... 9,999 | /system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts |
Extended System | 10,000 ... 19,999 | /system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts |
Product | 20,000 ... 29,999 | /product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts |
Vendor | 30,000 ... 39,999 | /vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts |
The client requests the key by requesting the SELinux domain and the desired virtual namespace, in this case "wifi_key"
, by its numeric id.
Above that, the following namespaces have been defined. If they replace special rules, the following table indicates the UID they used to correspond to.
Namespace ID | SEPolicy Label | UID | Descripción |
---|---|---|---|
0 | su_key | N/A | Super user key. Only used for testing on userdebug and eng builds. Not relevant on user builds. |
1 | shell_key | N/A | Namespace available to shell. Mostly used for testing, but can be used on user builds as well from the command line. |
100 | vold_key | N/A | Intended for use by vold. |
101 | odsing_key | N/A | Used by the on-device signing daemon. |
102 | wifi_key | AID_WIFI(1010) | Used by Android's Wifi sybsystem including wpa_supplicant. |
120 | resume_on_reboot_key | AID_SYSTEM(1000) | Used by Android's system server to support resume on reboot. |
Access Vectors
The SELinux class keystore_key
has aged quite a bit and some of the permissions, such as verify
or sign
have lost their meaning. Here is the new set of permissions, keystore2_key
, that Keystore 2.0 will enforce.
Permiso | Meaning |
---|---|
delete | Checked when removing keys from Keystore. |
get_info | Checked when a key's metadata is requested. |
grant | The caller needs this permission to create a grant to the key in the target context. |
manage_blob | The caller may use DOMAIN_BLOB on the given SELinux namespace, thereby managing blobs by itself. This is specifically useful for vold. |
rebind | This permission controls if an alias may be rebound to a new key. This is required for insertion and implies that the previously bound key will be deleted. It is basically an insert permission, but it captures the semantic of keystore better. |
req_forced_op | Clients with this permission may create unpruneable operations, and operation creation never fails unless all operation slots are taken by unpruneable operations. |
update | Required to update the subcomponent of a key. |
use | Checked when creating a Keymint operation that uses the key material, eg, for signing, en/decryption. |
use_dev_id | Required when generating device identifying information, such as device id attestation. |
Additionally, we split out a set of non key specific keystore permissions in the SELinux security class keystore2
:
Permiso | Meaning |
---|---|
add_auth | Required by authentication provider such as Gatekeeper or BiometricsManager for adding auth tokens. |
clear_ns | Formerly clear_uid, this permission allows a non owner of a namespace to delete all keys in that namespace. |
list | Required by the system for enumerating keys by various properties, such as ownership or auth boundedness. This permission is not required by callers enumerating their own namespaces. This is covered by the get_info permission. |
lock | This permission allows to lock Keystore, that is, evict the master key, such that auth bound keys become unusable and uncreatable. |
reset | This permission allows to reset Keystore to factory default, deleting all keys that are not vital to the functioning of the Android OS. |
unlock | This permission is required to attempt to unlock the master key for auth bound keys. |