El subsistema Gatekeeper realiza la autenticación de patrón o contraseña del dispositivo en un entorno de ejecución confiable (TEE). Gatekeeper registra y verifica las contraseñas con una clave secreta respaldada por hardware. Además, Gatekeeper limita los intentos de verificación fallidos consecutivos y debe rechazar las solicitudes de servicio en función de un tiempo de espera determinado y una cantidad determinada de intentos fallidos consecutivos.
Cuando los usuarios verifican sus contraseñas, Gatekeeper emite un token de autenticación que se firma con una clave HMAC por inicio que solo está disponible para los componentes seguros, y este token se envía al Almacén de claves respaldado por hardware. Es decir, un token de autenticación de Gatekeeper notifica a Keystore que las apps pueden usar las claves vinculadas a la autenticación (por ejemplo, las claves que crearon las apps).
Arquitectura
Gatekeeper incluye tres componentes principales:
gatekeeperd(daemon de Gatekeeper): Es un servicio de Binder de C++ en Android que contiene lógica independiente de la plataforma que implementa la interfaz de AIDL deIGateKeeperService, según una implementación subyacente específica del proveedor deIGatekeeper.- Servicio de capa de abstracción de hardware (HAL) de Gatekeeper: Es una implementación específica del proveedor de la interfaz AIDL
IGatekeeper. Este servicio de HAL se ejecuta en Android, pero la funcionalidad principal de Gatekeeper debe ejecutarse en un entorno seguro, por lo que suele comunicarse con la TA de Gatekeeper. - Aplicación de confianza (TA) de Gatekeeper: Es una implementación específica del proveedor que se ejecuta en el TEE y realiza la verificación real de la contraseña o el patrón.
El LockSettingsService realiza una solicitud (a través de Binder) que
llega al daemon gatekeeperd en el SO Android. Luego, el daemon gatekeeperd realiza una solicitud del servicio HAL IGatekeeper, que, a su vez, llega a su contraparte de Gatekeeper TA en el TEE:
Figura 1: Flujo de datos de alto nivel para la autenticación por GateKeeper.
El daemon gatekeeperd otorga a las APIs del framework de Android acceso a la HAL y participa en la generación de informes de autenticaciones de dispositivos en Keystore.
El daemon gatekeeperd se ejecuta en su propio proceso y es independiente del servidor del sistema.
Implementación de HAL
El daemon gatekeeperd usa la HAL IGatekeeper para interactuar con la TA de Gatekeeper subyacente para la autenticación con contraseña. La implementación de la TA de Gatekeeper debe poder firmar (inscribir) y verificar BLOBs. Se espera que todas las implementaciones cumplan con el formato estándar del token de autenticación (HardwareAuthToken) que se genera en cada verificación de contraseña exitosa. Para obtener detalles sobre el contenido y la semántica de HardwareAuthToken, consulta la definición de HardwareAuthToken.aidl.
Las implementaciones de proveedores de la HAL de IGatekeeper deben implementar las funciones enroll y verify:
- El método
enrolltoma un BLOB de contraseña, lo firma y devuelve la firma como un identificador. El BLOB que se muestra (desde una llamada aenroll) debe tener la estructura que se muestra ensystem/gatekeeper/include/gatekeeper/password_handle.h. - La función
verifydebe comparar la firma que produce la contraseña proporcionada y asegurarse de que coincida con el identificador de contraseña inscrito.
La clave que se usa para inscribir y verificar nunca debe cambiar y debe poder volver a derivarse en cada inicio del dispositivo.
Trusty y otras implementaciones
El sistema operativo Trusty es el SO de confianza de código abierto de Google para entornos de TEE y contiene una implementación aprobada de Gatekeeper. Sin embargo, cualquier SO de TEE puede implementar Gatekeeper, siempre que el TEE tenga acceso a una clave guardada en hardware persistente y un reloj seguro y monótono que funcione en suspensión.
Trusty usa un sistema de IPC interno para comunicar un secreto compartido directamente entre KeyMint (antes Keymaster) y la implementación de Trusty de Gatekeeper (Trusty Gatekeeper). Este secreto compartido se usa para firmar los tokens de autenticación que se envían al Keystore para proporcionar certificaciones de verificación de contraseñas. Trusty Gatekeeper solicita la clave a KeyMint para cada uso y no conserva ni almacena en caché el valor. Las implementaciones pueden compartir este secreto de cualquier manera que no comprometa la seguridad.
La clave HMAC que se usa para inscribir y verificar contraseñas se deriva y se conserva únicamente en Gatekeeper.
Android proporciona una implementación genérica de Gatekeeper en C++ que solo requiere la adición de rutinas específicas del dispositivo para completarse. La implementación de Trusty se basa en esto. Para implementar un Gatekeeper de TEE con
código específico del dispositivo para tu TEE, consulta las funciones y los comentarios
en system/gatekeeper/include/gatekeeper/gatekeeper.h. Las responsabilidades principales de una implementación que cumple con los requisitos incluyen las siguientes:
- Cumplimiento de la HAL de
IGatekeeper - Los tokens de autenticación que se devuelven deben tener el formato de acuerdo con la especificación de
HardwareAuthToken(que se describe enHardwareAuthToken.aidl). - El Gatekeeper de TEE debe poder compartir una clave HMAC con KeyMint, usando una de las siguientes opciones:
- Acuerdo de secreto compartido: Gatekeeper puede participar en la negociación por inicio de la clave HMAC implementando la HAL
ISharedSecret. Esto requiere que Gatekeeper y KeyMint tengan acceso a un secreto compartido previamente común. - Acceso directo: Gatekeeper puede recuperar la clave HMAC de KeyMint con un mecanismo de comunicación entre procesos interno de TEE, ya sea a pedido o en el primer uso (se almacena en caché a partir de ese momento).
- Acuerdo de secreto compartido: Gatekeeper puede participar en la negociación por inicio de la clave HMAC implementando la HAL
ID de seguridad (SID) de usuario
Un SID de usuario es la representación de TEE de un usuario (sin una conexión sólida con un ID de usuario de Android). El SID se genera con un generador de números seudoaleatorios criptográfico (PRNG) cada vez que un usuario registra una contraseña nueva sin proporcionar una anterior. Esto se conoce como una reinscripción no confiable y, por lo general, solo ocurre cuando un usuario establece una contraseña o un patrón por primera vez.
Una reinscripción de confianza se produce cuando un usuario proporciona una contraseña anterior válida, como cuando cambia la contraseña. En este caso, el SID del usuario se migra al nuevo identificador de contraseña, lo que conserva las claves que estaban vinculadas a él.
El SID del usuario se incluye en la autenticación HMAC junto con la contraseña en el identificador de contraseña cuando se registra la contraseña.
Los SID de usuario se incluyen en el HardwareAuthToken que devuelve la función verify() y se asocian a todas las claves de Keystore vinculadas a la autenticación (para obtener detalles sobre el formato HardwareAuthToken y Keystore, consulta Autenticación).
Ten en cuenta que, como una llamada no confiable a la función enroll() cambia el SID del usuario, la llamada hace que las claves vinculadas a esa contraseña sean inútiles. Los atacantes pueden cambiar la contraseña del dispositivo si controlan el
SO Android, pero destruyen las claves sensibles protegidas por raíz en el
proceso.
Limitación de solicitudes
Gatekeeper debe poder limitar de forma segura los intentos de fuerza bruta en una credencial de usuario. Como se muestra en GatekeeperVerifyResponse.aidl, la HAL permite mostrar un tiempo de espera en milisegundos. El tiempo de espera informa
al cliente que no vuelva a llamar a Gatekeeper hasta que haya transcurrido el tiempo de espera.
Gatekeeper no debería entregar solicitudes de servicio si hay un tiempo de espera pendiente.
Gatekeeper debe escribir un contador de fallas antes de verificar la contraseña de un usuario. Si
la verificación de la contraseña se realiza correctamente, se debe borrar el contador de fallas. Esto
evita los ataques que impiden la limitación inhabilitando la MMC incorporada (eMMC)
después de emitir una llamada verify. La función enroll también
verifica la contraseña del usuario (si se proporciona) y se debe limitar de la misma manera.
Si el dispositivo lo admite, se recomienda que el contador de fallas se escriba en un almacenamiento seguro. Si el dispositivo no admite la encriptación basada en archivos o si el almacenamiento seguro es demasiado lento, las implementaciones pueden usar el bloque de memoria protegida contra reproducción (RPMB) directamente.