Authors: pmalani@google.com, norvez@google.com
Reviewers: semenzato@google.com, apronin@google.com, mnissler@google.com and others
Last Updated: 2018-11-01
Increase security for Fingerprint (FP) templates by using a TPM-sourced seed in addition to internal FPMCU entropy while encrypting FP templates. The TPM-sourced seed will be derived from the system key which is loaded from the TPM during boot in mount-encrypted.
Fingerprint authorization in ChromeOS, relies on encrypted FP templates which are stored in each user’s mount directory. These templates are created and encrypted by the FPMCU during FP enrollment, before being sent back to the AP (Application Processor). When the user logs in, these templates are sent to the FPMCU where they get decrypted and loaded.
The encryption is performed in the FPMCU using entropy which is internal to the MCU and never leaves the MCU. That way, even if the templates are somehow obtained by and attacker from the user mount directory, they cannot be decrypted, since the attacker will not have access to the MCU entropy. This entropy gets reset on every powerwash/recovery.
The complete design doc is Fingerprint Authentication on ChromeOS.
The solution proposed should exhibit the following attributes:
In addition to FPMCU entropy, we include a TPM-sourced seed (derived from the system key) while performing template encryption. The TPM system key gets regenerated during powerwash/recovery, so in the event that the FP templates are accessed due to a runtime exploit, a power-wash / recovery from the user will ensure:
The overall design consists of two components:
The TPM seed generation would proceed as follows:
During mount-encrypted execution, after the System_key is loaded, the TPM-backed system key will be HMAC-ed with a simple salt (the string biod):
TPM_Seed = HMAC_SHA256(System_key, "biod")
The resulting 256-bit seed (called TPM_Seed) will be maintained in a brillo::SecureBlob.
The TPM_seed will be saved into a tmpfs file (/run/biod_crypto_init/seed) for consumption by bio_crypto_init. This file's ownership will be set up such that only user/group biod can access it.
bio_crypto_init (the binary which sends the seed to the FPMCU) will be spawned after mount-encrypted completes. This is ensured by setting the bio_crypto_init upstart rules to depend on starting boot-services
On the bio_crypto_init side, the TPM_seed will be retrieved from the tmpfs file and forwarded to the FP MCU via the various BiometricManagers. Immediately after reading from the tmpfs file, bio_crypto_init will nuke (write all 0’s and then delete) the tmpfs file.
The upstart rules of biod will be modified such that it will start after bio_crypto_init stops (this modification can be made in the .conf file of biod)
(For a discussion of various alternatives, please see the Alternatives Considered section)
The IPC mechanism selected should have the following features:
TPM_seed between mount-encrypted and bio_crypto_init.TPM_seed buffer in kernel and memory. This is crucial to minimize the risk of access to this seed.The currently proposed method of passing the TPM_seed is to use a file in tmpfs. The sequence would be as follows:
TPM_Seed to a file in /run (/run/bio_crypto_init/seed). /run is a tmpfs created by the OS for use by various system services.bio_crypto_init will read the TPM_Seed from the known tmpfs file.bio_crypto_init reads the TPM_Seed, it will first overwrite (/run/biod_crypto_init/seed) with all 0s, and immediately after will delete /run/biod_crypto_init/seed.bio_crypto_init can then instantiate its BiometricManager classes and send the data to the FP MCU. This way, even if the sending of data fails, there will not be any stray copy of the TPM_seed in a process’s memory, or in tmpfs.TPM_Seed in kernel.bio_crypto_init.bio_crypto_init crashes / fails to start, the tmpfs file remains in the system, i.e cleanup of tmpfs is reliant on bio_crypto_init.When a device boots up for the first time after going through recovery/powerwash, biod will force an “Add Entropy” step. This involves:
Unfortunately, since the TPM_Seed will be stored in MCU RAM, the reboot of the FPMCU will lead to the TPM_Seed being lost until the next boot. In the absence of a TPM_Seed, all FPMCU operations will fail (until the next boot). There is no opportunity to reprogram the TPM_Seed, because that must take place during mount-encrypted, which must necessarily run before biod starts.
There are two proposals to work around this issue. The one eventually selected has been included here, and the other alternative has been placed in the Alternatives Considered section.
In this method, bio_crypto_init will not perform any reboot on the MCU, and solely program the TPM_Seed. This would mean that if a device was to boot for a first time without having done any previous powerwash/recovery, the first boot would not have FP functionality. FP functionality would be regained on all subsequent boot (since the entropy would have been added/initialized by then).
The downside of this approach is a poor user experience.
The benefit is a simple implementation of the bio_crypto_init tool, which will consequently also take less time to execute (booting to RO/RW are time consuming operations).
In practice all devices leaving the factory floor would have bio_wash --factory_init done on them during finalisation to initialise the entropy, and so this shouldn't affect a large majority of end users.
In order to avoid races which might occur because both bio_crypto_init and biod will try to access the BiometricManagers' hardware. We need to ensure that biod only starts after bio_crypto_init ends.
To accomplish this, biod.conf will be modified to include a dependency on bio_crypto_init to start the daemon. So, the relevant portion of biod.conf will now be:
start on started system-services and started uinput and stopped bio_crypto_init
In the FPMCU we will use the concatenation of TPM_Seed and SBP_Src_Key as Input Key Material (IKM) to derive an encryption key. Combined with a random salt, the pseudo random key (PRK) would be derived as:
PRK = HMAC_SHA256(Random_Salt, SBP_Src_Key || TPM_Seed)
A few alternatives are being considered for the IPC Mechanism
bio_crypto_init crashing, this will leave a copy in the internal kernel buffers. Question: How long before those internal buffers get cleared in the case of the pipe not being read from?There was also another alternative considered for the sequence of programming the TPM seed and initializing the FPMCU: make bio_crypto_init add entropy and then set TPM.
bio_crypto_init) is run when starting boot-services is signaled.bio_crypto_init via a tmpfs file. The reading and deletion of the tmpfs file is detailed in the IPC Mechanism section.bio_crypto_init runs minijail-ed and runs with user/group biod. Only the files required for its functioning (i.e., the tmpfs file /run/, the devnode to access the FPMCU, log directory inside /var/log/bio_crypto_init) are mounted and visible inside the sandbox. See the minijail0 arguments for a full explanation.System_key which is retrieved from the TPM anyway during mount-encrypted execution. Thus, no additional or new input is being fed to the feature.biod so only users root or biod can access the file. Since bio_crypto_init runs only during starting boot-services and the process along with the conf file ensures that the file is deleted after execution, there is no additional threat of the /tmp file being corrupted.TPM_Seed derived from the System_key from TPM, in a file on tmpfs (the file is zeroed and deleted once read by bio_crypto_init).TPM_Seed would not be of much use to the attacker, since the use of HMAC_SHA256 means the attacker would still not have access to the system key (brute force trial of HMAC 256 would be required to guess the system salt required to produce the TPM-seed).TPM_seed as well as the internal SBP_Src_Key combined with a random salt, and since only the encrypted templates are stored on the rootfs, the templates would simply be rendered useless. A powerwash/recovery can help restore functionality of FP unlock, but new templates would have to be registered.bio_crypto_init uses two processes. A child process is spawned by bio_crypto_init and the FPMCU programming is done on the child process. The parent process waits for the child process to complete, or kills the process if it exceeds a timeout limit. This ensures that the process doesn't hang indefinitely./run/bio_crypto_init/seed) as an IPC mechanism to transfer the TPM_Seed between mount-encrypted and bio_crypto_init. Please see the Alternatives Considered and Design Ideas section regarding rationale behind choosing tmpfs vis a vis socketpair/pipe.HMAC_SHA256 is used to derived TPM_Seed from the System_key as described in section TPM seed generation.
HMAC_SHA256 is also used to derive the FPMCU’s encryption key. This is the same as it was earlier; the only change is that source key has been updated to also include the TPM_Seed.
This implementation should not have any adverse implications on Privacy (over and above existing functionality on ChromeOS). This provides security hardening for the fingerprint templates to prevent their retrieval and mis-use.