Fingerprint-based full-disk encryption on linux

Using Secure Boot and TPM 2.0

I encrypt my laptop to have the peace of mind -- if it were to get stolen my data would mostly be safe. It would still be stressfull, a loss of money and a bit of a mess, but at least a thief wouldn't open the laptop and find a bunch of payment information, emails and sensitive information. So far I have used a password, but now that my new laptop has a fingerprint reader I wanted to see if I can use my fingerprint instead.

Caveat: Of course fingerprints are much less secure than a password (e.g. it can be stolen from a photograph), but its all about defending against the right threat levels (relevant xkcd) and picking low-hanging fruits. So consider this just a convinient way to project your data against simple "start the laptop and check" or "take out the hard drive and look" scenarios.

So why is this not super easy? Basically you cannot "read off" an encryption key from a fingerprint, because the reading will look slightly different every time. You can only compare a fingerprint against stored fingerprints and check if they match. The problem now is, where do you put those comparison fingerprints on your laptop? You can't put them into the unencrypted part, then the "attacker" could simply access and replace them with their own. But you can't simply put them into the encrypted part because you cannot access it before decrypting it.

I can think of two solutions here, both invole using and trusting your laptop's UEFI/BIOS software:

  • Store the hard drive encryption key in the UEFI and configure it so that it requires a fingerprint when the computer starts up ("power on password"). Assuming the UEFI is secure (which it might not be, but it probably is sufficient for the simple theft model from above), one cannot unlock the UEFI without the fingerprint and cannot unlock the hard drive without the UEFI. Unfortunately this functionality seems to be only available via Lenovo's Windows fingerprint sofrware.
  • Store the hard drive encryption key in the UEFI and configure it (via Secure Boot) so that it only boots into a "secure" operating system (your Linux installation). Then your computer boots the OS and it asks you for your password / fingerprint. The first part means that your computer won't boot (or at least won't give away your hard drive encryption key) if it is not booting your OS. Afterwards your hard drive will actually be decrypted without having checked your fingerprint, but it is running your Linux installation which you have password- or fingerprint-protected.

Of course both methods have their weaknesses, in particular bugs or mistakes in the UEFI could break either method, and bugs in Linux / your login manager break the 2nd option. But again, we're just trying to protect our data from a thief who steals your laptop in the subway and wants to resell it, not a government attack. If you need better security really use a password. PS: If you don't have a fingerprint reader and use a password to login anyway, don't use this method. I would instead recommend a normal password-based disk encryption and autologin on Linux as opposed to automatic decryption and password-based login, to avoid additional points of weakness.

So for our method we need 3 steps

  1. Set up secure boot to only boot the operating system we want.
  2. Set up our Trusted Platform Module (TPM) to give out the encryption key if the previous criterion is fulfilled, and decrypt the harddrive.
  3. Set up Linux to ask for a fingerprint on login.

I will walk through my steps below. Keep in mind that this worked for me using the ThinkPad X1 Carbon Generation 9, unfortunately not all UEFI implementations work similar. In particular some very early laptop models had bugs that could brick the UEFI, and many Windows-laptops come pre-encrypted and might or might not unlock after you made changes in the UEFI (in that case I recommend exporting the encryption key or disabling BitLocker encryption).


Secure Boot setup

To enable Secure Boot and make boot only our "signed" operating systems, we first need to sign it. By default most UEFI configurations trust keys from Microsoft, Canonical, various others, and the manufacturer. As we do not have those keys we generate our own keys and add them to the UEFI. I am basically following this guide and use this script by Rod Smith. Obviously give that script a read before running code downloaded from the internet! At the time of writing (2021) the script matches with the explanations in the ArchWiki article and seems to make sense.

curl -L -O
chmod +x

Then we can use these keys to sign various binaries. Be aware that anything you sign (e.g. debugging images) can boot and obtain your hard drive decryption keys from the UEFI. PS: What is referred to as "Common Name" is really just a name that you later see e.g. in your UEFI's list of keys, choose something (e.g. your name) to later make it obvious that this is your key and not one of the many pre-installed ones. Here I sign the

sbsign --key db.key --cert db.crt --output /boot/vmlinuz-linux /boot/vmlinuz-linux
sbsign --key db.key --cert db.crt --output esp/EFI/BOOT/BOOTx64.EFI esp/EFI/BOOT/BOOTx64.EFI

Finally we "enroll" the key to tell it to trust this key. Here are some important caveats:

  • The encryption software (luks) will check the Platform Configuration Registers (PCRs) before unlocking the drive, to check that Secure Boot is still enabled and no relevant settings have changed. Depending on how your TPM / Secure Boot works, booting images signed with a different key willl produce different PCRs or not. If it does create different PCRs, you do not have to worry about the other (e.g. preinstalled) keys on your laptop. If the PCRs are identical you might want to, if possible, remove preinstalled keys.
  • Warning: I heard of some reports that (a) enrolling your own keys or especially (b) removing the other keys could "brick" (make unusable) your laptop. As a rule of thumb, this problem mostly affected older early UEFI laptops but I highly recommend to check online first. Also remember the warning about BitLocker above.
I enrolled my db key (as far as I can see that is the only one needed) by copying it to the (unencrypted, FAT32 formatted) EFI partition of my hard drive (a USB stick probably works too). My UEFI was fine with .auth or .cer formats, I used the former as recommended in the ArchWiki.
mkdir /boot/keys && cp db.auth /boot/keys
Then I went through the UEFI menu to "enroll" this new Signature Database (db) key. I did not enroll the Platform Key (PK), Key Exchange Key (KEK) or add a Forbidden Signature Database (dbx), not sure why they would need to be so I left them out for now (maybe if one wanted to update keys without manually going into the UEFI menu).

TPM 2.0 to decrypt disk

I encrypted my disk using luks2, set up roughly following this ArchWiki guide. I have one small unencrypted 2 GB partition (nvme0n1p1) formatted as FAT32 and one large encrypted 1 TB ext4-formatted partition ((nvme0n1p2)). Now I follow this ArchWiki guide to add my tpm device as an additional option to unlock the disk (keeping the password as a backup).

systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0,1,7 /dev/nvme0n1p2

Note that I use --tpm2-device=auto since I have only one tpm device. I use the Platform Configuration Registers (PCRs) 0,1, and 7 to check the UEFI version, UEFI settings, and "Secure Boot State", respectively. This basically means the TPM will release the key to decrypt the disk if and only if all these registers contain the correct values. The important one is PCR7 which, in my system, changes if the booted binary is signed with a different key. You can look at these values using sudo tpm2_pcrread and observe when they change.

I also need to make sure the initramfs has the tools to decrypt this (the sd-encrypt hook is the relevant one, as well as systemd and keyboard).

HOOKS=(base systemd autodetect keyboard modconf block sd-encrypt filesystems fsck)

And add the appropriate options to my bootloader. I am using systemd-boot so I added the following line to the config file

options rd.luks.options=adda8e-62.......a84f=tpm2-device=auto root=dev/mapper/cr

where the 1st and 3rd part is just the usual encryption setup, the middle part tells luks to check the tpm for a key. The long number is just the UUID of my disk, /dev/nvme0n1p2.

Fingerprint login in Linux

This step is actually quite easy! I spend a long time searching for compatible login managers until realizing I don't need one. You can of course use one if you like.

Anyway, back to the ArchWiki. The first step is to install fprintd and enroll at least one of your fingerprints. Note that for this enroll process, and only for the enroll process, you need a authentication agent running. I usually don't use one (i.e. just use the text-based pre-installed fallback "pkttyagent") but for this I installed lxsession-gtk3 (and executed lxsession). I enrolled two fingers to easily reach my fingerprint reader:

fprintd-enroll -f right-ring-finger

To now use those fingerprints to log in, simply edit /etc/pam.d/system-local-login and add the line

auth      sufficient

at the top (note that this might be different for your Linux distribution). I added the same in /etc/pam.d/{sudo,polkit-1} to authenticate sudo with a fingerprint, and also answer all those polkit checks (e.g. when you use "systemctl" to do something) with my fingerprint, and you can find a lost of other programs in that directory as well (from login managers such as sddm and lightdm to xscreensaver and i3lock).

Acknowledgements: I want to mention that a lot of this was inspired by this medium post. I didn't end up using that method but it helped me a lot to see what is possible and what I need. As you probably noticed, I got most of this from various pages in the ArchWiki so huge credits to all the volunteers maintaining this. I also owe credit to Roderick Smith for the Secure Boot key generation information and script.