Android 6.0 introduces the concept of user-authentication-gated cryptographic keys. To achieve this, two key components need to work together. First is the cryptographic key storage and service provider, which stores cryptographic keys and provides standard crypto routines on top of them. Second is any number of user authenticators that may attest to the user's presence and/or successful authentication.
The cryptographic key storage in Android is provided by the keystore service and Keymaster. (Also see information about the Android Keystore system, at the framework level, which is backed by the keystore service.) For Android 6.0, the two supported authentication components are Gatekeeper (for PIN/pattern/password authentication) and Fingerprint (for fingerprint authentication). These components communicate their authentication state with the keystore service via an authenticated channel.
- The hardware-backed Keystore. Cryptographic services, including hardware-backed cryptography for key storage, which might include a Trusted Execution Environment (TEE).
- Gatekeeper. Components for PIN, pattern, and password authentication.
- Fingerprint. Components for fingerprint authentication.
The Gatekeeper and Fingerprint components work with Keystore and other components to support the use of hardware-backed authentication tokens (referred to below as "AuthTokens").
Upon first boot of the device after a factory reset, all authenticators are prepared to receive credential enrollments from the user.
The user must initially enroll a PIN/pattern/password with Gatekeeper. This initial enrollment creates a randomly generated, 64-bit User SID (user secure identifier, described further below) that serves as an identifier for the user and as a binding token for the user's cryptographic material. This User SID is cryptographically bound to the user's password. As detailed below, successful authentications to Gatekeeper result in AuthTokens that contain the User SID for that password.
When a user wants to change their credential, they must present their existing credential. If the existing credential is verified successfully, the User SID associated with the existing credential is transferred to the new credential. This allows the user to keep accessing their keys after changing their credential. If a user does not present their existing credential, the new one is enrolled with a fully random User SID. The user can access the device but keys created under the old User SID are permanently lost. This is known as an "untrusted enroll."
Note that an untrusted enroll will not be allowed under normal circumstances by the Android framework, so most users won't ever see this functionality. However, forcible password resets either by a device administrator or an attacker may cause this to occur.
Now that the user has set up a credential and received a User SID, they may proceed to start authentication.
In the diagram below, authentication starts when a user provides a PIN, pattern, password, or fingerprint. All TEE components share a secret key which they use to authenticate each other's messages.
The numbers in the following steps correspond to the numbers in the diagram above, and include reference to both the Android OS and the TEE OS:
- A user provides a PIN, pattern, password, or fingerprint. The
FingerprintServicemake a request via Binder to the Gatekeeperd or fingerprintd daemon in the Android OS. Note that fingerprint authentication occurs asynchronously after the fingerprint request is sent.
- This step involves either Gatekeeperd (option 1 below)
or fingerprintd (option 2 below),
depending on whether a pin/pattern/password, or fingerprint, is provided.
- The Gatekeeperd daemon sends a pin, pattern, or password hash (received in step 1) to its counterpart (Gatekeeper) in the TEE. If authentication in the TEE is successful, Gatekeeper in the TEE sends an AuthToken containing the corresponding User SID, signed with the AuthToken HMAC key, to its counterpart in the Android OS.
- Alternatively, the fingerprintd daemon, which listens for fingerprint events, sends the data (received in step 1) to its counterpart (Fingerprint) in the TEE. If authentication in the TEE is successful, Fingerprint in the TEE sends an AuthToken, signed with the AuthToken HMAC key, to its counterpart in the Android OS.
- The Gatekeeperd or fingerprintd daemon receives a signed AuthToken and passes the AuthToken to the keystore service via an extension to the keystore service's Binder interface. Additionally, Gatekeeperd notifies the keystore service when the device is re-locked and when the device password changes.
- The keystore service passes to Keymaster the AuthTokens received from Gatekeeperd and fingerprintd, verifying the AuthTokens with the key shared with the Gatekeeper and Fingerprint trustlets. Keymaster trusts the timestamp in the token as the last authentication time and bases a key release decision (to allow an app to use the key) on the timestamp.
Note: AuthTokens are invalidated whenever a device reboots.
Authentication token format
The AuthToken format described in the
hw_auth_token.h file is
necessary for token sharing and compatibility across languages and
components. See the following file:
A simple serialization protocol with the required fields is defined in the table below. The fields are fixed size.
Field descriptions are below the table.
|Field||Type||Required or Optional|
|AuthToken Version||1 byte||Required|
|Challenge||64-bit unsigned integer||Optional|
|User SID||64-bit unsigned integer||Required|
|Authenticator ID||64-bit unsigned integer in network order||Optional|
|Authenticator type||32-bit unsigned integer in network order||Required|
|Timestamp||64-bit unsigned integer in network order||Required|
|AuthToken HMAC key (SHA-256)||256-bit blob||Required|
This section describes the fields of the AuthToken table above.
AuthToken Version: Group tag for all fields below.
Challenge: A random integer to prevent replay attacks. Usually the ID of a requested crypto operation. Currently used by transactional fingerprint authorizations. If present, the AuthToken is valid only for crypto operations containing the same challenge.
User SID: Non-repeating user identifier tied cryptographically to all keys associated with device authentication. For more information, see the Gatekeeper page.
Authenticator ID (ASID): Identifier used to bind to a specific authenticator policy. All authenticators have their own value of ASID that they can change according to their own requirements.
Authenticator Type: Either Gatekeeper or Fingerprint, as follows:
|Authenticator Type||Authenticator Name|
Timestamp: Time (in milliseconds) since the most recent system boot.
AuthToken HMAC key: Keyed SHA-256 MAC of all fields except the HMAC field.
Device boot flow
On every boot of a device, the AuthToken HMAC key must be generated and shared with all TEE components (Gatekeeper, Fingerprint, and Keymaster). Thus, the HMAC key must be randomly generated every time the device reboots, for added protection against replay attacks.
The protocol for sharing this HMAC key with all components is a platform-dependent implementation feature. The key must never be made available outside the TEE. Thus, if a TEE OS lacks an internal inter-process communication (IPC) mechanism, and the TEE needs to transfer the data through the untrusted OS, the transfer must be done via a secure key exchange protocol.
The Trusty operating system, which runs next to Android, is an example of a TEE, but other TEEs can be used instead. Trusty uses an internal IPC system to communicate directly between Keymaster and Fingerprint or Gatekeeper. The HMAC key is kept solely in Keymaster. Fingerprint and Gatekeeper request the key from Keymaster for each use, and do not persist or cache the value.
Note that no communication happens between applets in the TEE because some TEEs are lacking in IPC infrastructure. This also permits the keystore service to quickly deny requests that are bound to fail as it has knowledge of the authentication table in the system, saving a potentially costly IPC into the TEE.