dotfiles/guides/06_TPM_UNLOCKING.md

9.2 KiB

TPM Unlocking

This will explain how to set up TPM (Trusted Platform Module) based automatic unlocking of your LUKS encrypted partition(s). Encryption usually requires that you manually type the password in each time you boot. This can however be pretty annoying (especially if you use a long password, like I do). This guide aims to fix this problem, without compromising security.

Once finished, this will basically store another decryption key(s) to your encrypted partition(s) in the TPM module. During boot, while in initrd, we will request this decryption key from TPM, which will only release it under certain conditions, to ensure safety.

The guide assumes you have already a working Arch Linux system, that uses LUKS encryption, having followed the INSTALLATION guide. You will also need to set up secure-boot, as described in SECURE_BOOT. This is a requirement, as while it is possible to set up TPM unlocking without it, doing so is incredibly insecure, and might lead to unauthorized users getting TPM to release your decryption keys. Additionally, you will need to be using a SYSTEMD BASED INITRAMFS, as the default BusyBox one doesn't support TPM unlocking.

Warning

This solution will be mostly safe, however, it is technically possible to hook up wires to the motherboard, to listen to the communication coming from the TPM chip. In that case, the attacker would be able to observe the key as it gets released by the chip. They could then take out your SSD/HDD, and mount it on their machine, using these obtained keys to decrypt the contents. See: https://astralvx.com/stealing-the-bitlocker-key-from-a-tpm/

If you can't afford to be vulnerable to this type of attack, you can still follow through with this, however instead of the TPM seamlessly releasing the decryption password, you can require a password to be entered, without which TPM won't release the decryption password.

This can be useful if you use a very long encryption passwords, and you want to be able to enter a shorter passphrase instead (TPM has brute-force protection, so a short password isn't actually that unsafe to use).

Check if you actually have the TPM module

First, you will want to verify that your machine even has the TPM v2 module. To do so, you can use the following command:

bootctl status

You should see TPM2 Support: yes in the output.

Choosing PCRs

PCR stands for Platform Configuration Register, and all TPM v2 modules have a bunch of these registers, which hold hashes about the system's state. These registers are read-only, and their value is set by the TPM module itself.

The data held by the TPM module (our LUKS encryption key) can then only be accessed when all of the selected PCR registers contain the expected values. You can find a list of the PCR registers on Arch Wiki.

You can look at the current values of these registers with this command:

systemd-analyze pcrs

For our purposes, we will choose these:

  • PCR0: Hash of the UEFI firmware executable code (may change if you update UEFI)
  • PCR7: Secure boot state - contains the certificates used to validate each boot application
  • PCR12: Overridden kernel command line, credentials

Important

If you're using a boot loader (rather than booting directly from the Unified Kernel Images - EFI files), it is crucial that we choose all 3, including PCR12, as many tutorials only recommend 0 and 7, which would however lead to a security hole, where an attacker would be able to remove the drive with the (unencrypted) EFI partition, and modify the boot loader config. (With systemd-boot, this would be loaders/loader.conf).

From there, the attacker could simply add a kernel argument like init=/bin/bash, or just enable editor support, allowing them to edit the parameters from the boot menu on the fly (The editor is actually enabled by default for systemd-boot). This would then bypass systemd as the init system and instead make the kernel run bash executable as the PID=1 (init) program. This would mean you would get directly into bash console that is running as root, without any need to enter a password.

From that bash console, they could get the TPM to release the decryption password manually, as all of the selected PCRs do match.

This wouldn't violate secure boot, as the .efi image files were unchanged, and are still signed, so the attacker would be able to boot into the system without issues.

However, with PCR12, this is prevented, as it detects that the kernel cmdline arguments which were used, and if they don't match the recorded parameters during enrollment, TPM will not release the key.

The nice thing about also selecting PCR12 is that it will actually allow us to securely keep systemd-boot editor support, which can be very useful for debugging, as all that will happen if we do edit the kernel command line will be that the TPM module will not release the credentials, and the initrd will just ask us to enter the password manually.

Optionally, you may also consider these:

  • PCR1: Hash of the UEFI firmware data (changes when you change your BIOS settings)
  • PCR4: Boot manager (changes when you change the boot manager)

Note

You may be tempted to also add PCR11, which is a hash of the Unified Kernel Image, so that no other UKI can be booted, but this isn't necessary, as we're signing our UKIs, which means untrusted ones wouldn't pass secure boot, and if secure boot got disabled, PCR7 wouldn't pass.

Additionally, enabling PCR11 would mean that you'd need to update the TPM every time your kernel/microcode/initrd/... is updated, as these will change the UKI file.

Enroll a new key into TPM

The following command will enroll a new randomly generated key into the TPM module and add it as a new keyslot of the specified LUKS2 encrypted device.

We also specify --tpm2-pcrs=0+7+12, which selects the PCR registers that we decided on above.

sudo systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7+12 /dev/gpt-auto-root-luks

Note

If you already have something in the tpm2 module, you'll want to add --wipe-slot=tpm2 too.

Note that wiping the slot will also remove the LUKS key slot that was added in the partition.

Tip

If you're extra paranoid, you can also provide --tpm2-with-pin=yes, to prompt for a PIN code (passphrase) on each boot.

I have mentioned why you may want to do this in the beginning.

In case you do want to go with a PIN, you can also safely drop PCR12, as you will be asked for credentials each time anyways, and at that point, the TPM unlocking is basically just as secure as regular passphrase unlocking, which systemd would fall back to if PCR12 wasn't met.

You will now be prompted for an existing LUKS password (needed to add a new LUKS keyslot).

Reboot

All that remains now is rebooting. The system should now get unlocked automatically, without prompting for the password / prompting for the TPM PIN instead of a decryption password.

If you're using a bootloader, I'd recommend also trying to modify the kernel parameters, to make sure that TPM does not release the key anymore, and you will be prompted to enter it manually.

Moving to a recovery key

Once you have confirmed that TPM unlocking is working, you can now optionally get rid of your original LUKS key, in favor of a randomly generated recovery key.

You might want to do this as this recovery key will be guaranteed to have high entropy, likely making it a lot more secure than your original key, further improving your chances, if someone attempts a brute-force decryption of your drive.

To generate a recovery key, you can actually also just use systemd-cryptenroll (though you can also do it manually with cryptsetup):

systemd-cryptenroll /dev/gpt-auto-root-luks --recovery-key

This will give you a randomized key, using characters that are easy to type. You will even be given a QR code that can be scanned directly to save the password on your phone.

Before proceeding with removing your own key, let's first make absolutely certain that the recovery key you saved does in fact work. Without doing this, you may get locked out!

cryptsetup luksOpen /dev/gpt-auto-root-luks crypttemp  # enter the recovery key
cryptsetup luksClose crypttemp

If this worked, proceed to:

cryptsetup luksRemoveKey /dev/gpt-auto-root-luks # Enter the original key to be deleted

Removing the key from TPM

In case you'd ever want to remove the LUKS key from TPM, you can do so simply with:

csystemd-cryptenroll --wipe-slot=tpm2

This will actually also remove the LUKS key from the /dev/gpt-auto-root-luks device as well as wiping it from the TPM2 chip.

Sources / Attribution