Gatekeeper

El subsistema de Gatekeeper realiza la autenticación de patrones o contraseñas del dispositivo en un entorno de ejecución confiable (TEE). Gatekeeper inscribe y verifica las contraseñas con una clave secreta respaldada por hardware. Además, Gatekeeper limita los intentos de verificación consecutivos fallidos y debe rechazar las solicitudes de servicio según 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 está firmado con una clave HMAC por inicio que solo está disponible para componentes seguros, y este token se envía al almacén de claves con copia de seguridad en el hardware. Es decir, un token de autenticación de Gatekeeper notifica a Keystore que las apps pueden usar claves vinculadas a la autenticación (por ejemplo, claves que crearon las apps).

Arquitectura

El gatekeeper incluye tres componentes principales:

  • gatekeeperd (daemon de Gatekeeper): Es un servicio de Binder de C++ en Android que contiene una lógica independiente de la plataforma que implementa la interfaz de AIDL de IGateKeeperService, basada en 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, por lo general, se comunica con el TA de Gatekeeper.
  • Aplicación de confianza (TA) del 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.

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 al servicio de HAL IGatekeeper, que a su vez llega a su contraparte de TA de Gatekeeper en el TEE:

Flujo de gatekeeper

Figura 1: Flujo de datos de alto nivel para la autenticación de GateKeeper

El daemon gatekeeperd les otorga a las APIs del framework de Android acceso al HAL y participa en el informe de las autenticaciones del dispositivo al almacén de claves. El daemon gatekeeperd se ejecuta en su propio proceso y es independiente del servidor del sistema.

Implementación de HAL

El daemon de gatekeeperd usa el HAL de IGatekeeper para interactuar con el TA subyacente de Gatekeeper para la autenticación de contraseñas. La implementación de la TA de Gatekeeper debe poder firmar (inscribir) y verificar los 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 correcta. 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 muestra la firma como un controlador. El blob que se muestra (de 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 la inscripción y la verificación nunca debe cambiar y se debe poder derivar nuevamente cada vez que se inicia el dispositivo.

Trusty y otras implementaciones

El sistema operativo Trusty es el SO de código abierto de confianza de Google para entornos de TEE y contiene una implementación aprobada de Gatekeeper. Sin embargo, cualquier SO de TEE puede implementar Gatekeeper, siempre y cuando el TEE tenga acceso a una clave persistente respaldada por hardware y un reloj monótono y seguro que marque en suspensión.

Trusty usa un sistema IPC interno para comunicar un secreto compartido directamente entre KeyMint y la implementación de Gatekeeper de Trusty (el Gatekeeper de Trusty). Este secreto compartido se usa para firmar los tokens de autenticación que se envían al almacén de claves para proporcionar certificaciones de verificación de contraseñas. El gatekeeper de confianza solicita la clave de 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 mantiene ú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. Entre las responsabilidades principales de una implementación conforme, se incluyen las siguientes:

  • Cumplimiento del HAL de IGatekeeper
  • Los tokens de autenticación que se muestran deben tener el formato que se especifica en HardwareAuthToken (que se describe en HardwareAuthToken.aidl).
  • El gatekeeper de TEE debe poder compartir una clave HMAC con KeyMint mediante una de las siguientes opciones:
    • Acuerdo de secreto compartido: El gatekeeper puede participar en la negociación por inicio de la clave HMAC implementando el HAL de ISharedSecret. Esto requiere que Gatekeeper y KeyMint tengan acceso a un secreto previamente compartido común.
    • Acceso directo: El 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 (almacenado en caché a partir de entonces).

IDs seguros del usuario (SID)

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 pseudoaleatorios (PRNG) criptográfico cada vez que un usuario inscribe una contraseña nueva sin proporcionar una anterior. Esto se conoce como una nueva inscripción no confiable y, por lo general, solo ocurre cuando un usuario establece una contraseña o un patrón por primera vez.

Una reenrollación confiable se produce cuando un usuario proporciona una contraseña válida anterior, 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 de HMAC junto con la contraseña en el control de contraseña cuando esta se inscribe.

Los SID del usuario se incluyen en el HardwareAuthToken que muestra la función verify() y se asocian a todas las claves del almacén de claves vinculadas a la autenticación (para obtener detalles sobre el formato HardwareAuthToken y el almacén de claves, 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 el administrador en el proceso.

Limitación de solicitudes

Gatekeeper debe poder limitar de forma segura los intentos de fuerza bruta en una credencial del usuario. Como se muestra en GatekeeperVerifyResponse.aidl, el HAL proporciona un tiempo de espera en milisegundos. El tiempo de espera le informa al cliente que no vuelva a llamar a Gatekeeper hasta que transcurra el tiempo de espera. Gatekeeper no debe entregar solicitudes si hay un tiempo de espera pendiente.

El gatekeeper debe escribir un contador de fallas antes de verificar la contraseña de un usuario. Si la verificación de contraseña se realiza correctamente, se debe borrar el contador de fallas. Esto evita los ataques que impiden la limitación, ya que inhabilita la MMC integrada (eMMC) después de emitir una llamada verify. La función enroll también verifica la contraseña del usuario (si se proporciona) y debe limitarse de la misma manera.

Si el dispositivo lo admite, se recomienda que el contador de fallas se escriba en el 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 protegido contra la repetición (RPMB) directamente.