Gatekeeper

Il sottosistema Gatekeeper esegue l'autenticazione tramite pattern/password del dispositivo in un Trusted Execution Environment (TEE). Gatekeeper registra e verifica le password utilizzando una chiave segreta basata sull'hardware. Inoltre, Gatekeeper riduce la velocità dei tentativi di verifica consecutivi non riusciti e deve rifiutare di soddisfare le richieste in base a un determinato timeout e a un determinato numero di tentativi consecutivi non riusciti.

Quando gli utenti verificano le loro password, Gatekeeper emette un token di autenticazione firmato con una chiave HMAC per ogni avvio disponibile solo per i componenti protetti. Questo token viene inviato al Keystore basato su hardware. In altre parole, un token di autenticazione di Gatekeeper comunica a Keystore che le chiavi legate all'autenticazione (ad esempio le chiavi create dalle app) possono essere utilizzate dalle app.

Architettura

Gatekeeper prevede tre componenti principali:

  • gatekeeperd (demone Gatekeeper): un servizio Binder C++ in Android che contiene logica indipendente dalla piattaforma che implementa l'interfaccia AIDL IGateKeeperService, in base a un'implementazione di IGatekeeper specifica del fornitore sottostante.
  • Servizio HAL (Hardware Abstraction Layer) del gatekeeper: un'implementazione specifica del fornitore dell'interfaccia AIDL IGatekeeper. Questo servizio HAL viene eseguito in Android, ma la funzionalità di base di Gatekeeper deve essere eseguita in un ambiente sicuro, quindi in genere comunica con il TA Gatekeeper.
  • Gatekeeper Trusted Application (TA): un'implementazione specifica del fornitore che viene eseguita nel TEE ed esegue la verifica effettiva della password o del pattern.

LockSettingsService invia una richiesta (tramite Binder) che raggiunge il demone gatekeeperd nel sistema operativo Android. Il daemon gatekeeperd invia quindi una richiesta al servizio HAL IGatekeeper, che a sua volta raggiunge la controparte TA Gatekeeper nel TEE:

Flusso di Gatekeeper

Figura 1. Flusso di dati di alto livello per l'autenticazione da parte di GateKeeper.

Il daemon gatekeeperd concede alle API del framework Android l'accesso all'HAL e partecipa alla generazione di report sulle autenticazioni del dispositivo nell'archivio chiavi. Il daemon gatekeeperd viene eseguito in un processo separato e distinto dal server di sistema.

Implementazione HAL

Il daemon gatekeeperd utilizza l'HAL IGatekeeper per interagire con il TA Gatekeeper sottostante per l'autenticazione tramite password. L'implementazione della TA Gatekeeper deve essere in grado di firmare (registrare) e verificare i blob. Tutte le implementazioni devono rispettare il formato standard per il token di autenticazione (HardwareAuthToken) generato a ogni verifica della password andata a buon fine. Per informazioni dettagliate sui contenuti e sulla semantica di HardwareAuthToken, consulta la definizione di HardwareAuthToken.aidl.

Le implementazioni dell'HAL IGatekeeper da parte dei fornitori devono implementare le funzioni enroll e verify:

  • Il metodo enroll prende un blob di password, lo firma e restituisce la firma come handle. Il blob restituito (da una chiamata a enroll) deve avere la struttura mostrata in system/gatekeeper/include/gatekeeper/password_handle.h.
  • La funzione verify deve confrontare la firma prodotta dalla password fornita e assicurarsi che corrisponda all'handle della password registrato.

La chiave utilizzata per la registrazione e la verifica non deve mai cambiare e deve essere ricavabile nuovamente a ogni avvio del dispositivo.

Trusty e altre implementazioni

Il sistema operativo Trusty è il sistema operativo attendibile open source di Google per gli ambienti TEE e contiene un'implementazione approvata di Gatekeeper. Tuttavia, qualsiasi sistema operativo TEE può implementare Gatekeeper a condizione che il TEE abbia accesso a una chiave hardware-backed persistente e a un orologio sicuro e monotono che batte in sospensione.

Trusty utilizza un sistema IPC interno per comunicare un segreto condiviso direttamente tra KeyMint e l'implementazione di Gatekeeper in Trusty (Trusty Gatekeeper). Questo segreto condiviso viene utilizzato per firmare i token di autenticazione inviati al Keystore per fornire attestazioni di verifica della password. Trusty Gatekeeper richiede la chiave a KeyMint per ogni utilizzo e non mantiene o memorizza nella cache il valore. Le implementazioni sono libere di condividere questo secret in qualsiasi modo che non comprometta la sicurezza.

La chiave HMAC utilizzata per registrare e verificare le password viene ricavata e conservata esclusivamente in Gatekeeper.

Android fornisce un'implementazione generica di Gatekeeper in C++ che richiede solo l'aggiunta di routine specifiche del dispositivo per essere completata. L'implementazione di Trusty si basa su questo. Per implementare un TEE Gatekeeper con codice specifico per il dispositivo, consulta le funzioni e i commenti in system/gatekeeper/include/gatekeeper/gatekeeper.h. Le responsabilità principali di un'implementazione conforme includono:

  • Conformità all'HAL di IGatekeeper.
  • I token di autenticazione restituiti devono essere formattati in base alla specifica HardwareAuthToken (descritta in HardwareAuthToken.aidl).
  • TEE Gatekeeper deve essere in grado di condividere una chiave HMAC con KeyMint utilizzando uno dei seguenti metodi:
    • Contratto con segreto condiviso: Gatekeeper può partecipare alla negotiation per ogni avvio della chiave HMAC implementando l'HAL ISharedSecret. Ciò richiede che Gatekeeper e KeyMint abbiano entrambi accesso a un segreto predefinito comune.
    • Accesso diretto: Gatekeeper può recuperare la chiave HMAC da KeyMint utilizzando un meccanismo di comunicazione interprocesso interno al TEE, su richiesta o al primo utilizzo (in seguito memorizzata nella cache).

ID utente sicuri (SID)

Un SID utente è la rappresentazione TEE di un utente (senza una connessione stretta con un ID utente Android). L'SID viene generato con un generatore di numeri pseudocasuali (PRNG) ogni volta che un utente registra una nuova password senza fornirne una precedente. Si tratta di una nuova registrazione non attendibile e in genere accade solo quando un utente imposta per la prima volta una password o una sequenza.

Una nuova registrazione attendibile si verifica quando un utente fornisce una password precedente valida, ad esempio quando la modifica. In questo caso, l'SID utente viene migrato al nuovo handle della password, conservando le chiavi associate.

L'SID utente è incluso nell'autenticazione HMAC insieme alla password nell'handle della password quando la password viene registrata.

Gli SID utente sono inclusi in HardwareAuthToken restituito dalla funzione verify() e associati a tutte le chiavi del Keystore associate all'autenticazione (per dettagli sul formato HardwareAuthToken e sul Keystore, consulta Autenticazione).

Tieni presente che, poiché una chiamata non attendibile alla funzione enroll() modifica l'SID utente, la chiamata rende inutili le chiavi associate a quella password. Gli attaccanti possono cambiare la password del dispositivo se controllano il sistema operativo Android, ma distruggono le chiavi sensibili protette dal root durante la procedura.

Limitazione delle richieste

Il gatekeeper deve essere in grado di limitare in modo sicuro i tentativi di forza bruta su una credenziale dell'utente. Come mostrato in GatekeeperVerifyResponse.aidl, l'HAL prevede il ritorno di un timeout in millisecondi. Il timeout informa il client di non chiamare di nuovo Gatekeeper finché non è trascorso il tempo specificato. Il gatekeeper non deve gestire le richieste se è presente un timeout in attesa.

Il gatekeeper deve scrivere un contatore degli errori prima di verificare la password di un utente. Se la verifica della password ha esito positivo, il contatore degli errori dovrebbe essere azzerato. In questo modo, si impediscono gli attacchi che impediscono il throttling disattivando l'MMC integrata (eMMC) dopo l'emissione di una chiamata verify. La funzione enroll verifica anche la password dell'utente (se fornita) e deve essere limitata nello stesso modo.

Se supportato dal dispositivo, è vivamente consigliato di scrivere il contatore degli errori in uno spazio di archiviazione sicuro. Se il dispositivo non supporta la crittografia basata su file o se lo spazio di archiviazione sicuro è troppo lento, le implementazioni potrebbero utilizzare direttamente il blocco di memoria protetto da replay (RPMB).