Gatekeeper

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 de IGateKeeperService, según una implementación subyacente específica del proveedor de IGatekeeper.
  • 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:

Flujo del guardián de acceso

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 enroll toma un BLOB de contraseña, lo firma y devuelve la firma como un identificador. El BLOB que se muestra (desde una llamada a enroll) debe tener la estructura que se muestra en system/gatekeeper/include/gatekeeper/password_handle.h.
  • La función verify debe 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 en HardwareAuthToken.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).

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.