on
Safe automatic decryption of LUKS partition using TPM2
In this article I demonstrate and explain how to safely decrypt a LUKS encrypted disk automatically using a TPM2 chip, the clevis package and initramfs.
This article is meant for and tested only on Fedora Linux. It assumes using initramfs, dracut and a set of Fedora packages.
This article contains optional sections on background and explains stuff in detail to make debugging easier. If you just want to get it done just jump to the “Prerequisites” and “Configure clevis” sections - but be aware of the security implications.
Motivation
Disk encryption protects your data (private keys and critical documents) through direct access of your hardware. Think of selling your notebook / smartphone or getting it stolen with an opportunistic evil actor. Any data, even if it was “deleted”, may be recoverable and hence fall into the hands of an unknown third party.
Disk encryption does not protect your data from access on the running system, e.g. by malware running in your user or kernel space. It’s already decrypted at that point.
Entering the passphrase to decrypt the disk at boot can become quite tedious. On modern systems a secure hardware chip called “TPM” (Trusted Platform Module) can store a secret to decrypt your disk automatically. This is considered an alternative factor opposed to a second factor. Keep that in mind! If done right it’s a valid alternative with a similar level of security compared to entering the passphrase on every boot. I’ll elaborate a bit more on possible pitfalls in the Security Implications.
Background
A TPM2 chip is a little hardware module inside your device which basically provides APIs for either WRITE-only or READ-only information. This way you might write a secret onto it, but you can never read it out later (but the TPM may use it later internally). Or you can write info at one point which can only be read later. The TPM2 provides something called PCRs (Platform Configuration Registers). These registers can be filled with SHA1 or SHA256 hashes and are used to measure and assert integrity of let’s say your UEFI configuration.
Secure Boot can be enabled in a systems UEFI. Among other things it computes hashes of every component in the boot-chain (UEFI and it’s configuration, bootloader, etc.) which are chained together such that a change in one of those components changes the computed and stored hashes in all following PCRs. This way you can build up trust about the environment you are in, when it comes to decrypting your disk for example. The UEFI Secure Boot specification defines PCRs 0 - 7. Everything beyond that is free for the OS and applications to use. Here is a little summary what is measured in the PCRs according to the spec:
- PCR 0: the EFI Firmware info like it’s version
- PCR 1: additional config and info related to the EFI Firmware
- PCR 2: EFI drives from hardware components (like RAID controller)
- PCR 3: additional config and info to drivers stored in 2
- PCR 4: pre-OS diagnostics and the EFI OS Loader
- PCR 5: config of the EFI OS Loader and GPT table
- PCR 6: is reserved for host platform manufacturer variables and is not used by EFI
- PCR 7: stores secure boot policy configuration
Some examples on what is measured into which PCR:
- Changes to the initramfs are computed into PCRs 9 and 10. So if you regenerate dracut using
dracut -f
you have to rebind. This will happen on every update the kernel - Changes to the grub configuration, like adding kernel arguments, kernels, etc. are measured into PCRs 8, 9 and 10
- Storage devices are measured into PCRs 8 and 10. Hubs and YubiKeys do not seem to be measured though
- Additional operating systems are measured into PCR 1. E.g. when attaching a USB stick before boot with a Live Fedora Linux image
- Booting into a live image changes PCRs 1, 4, 5, 8, 9 and 10
A tool called clevis generates a new decryption secret for the LUKS encrypted disk, stores it in the TPM2 chip and configures the TPM2 to only return the secret if the PCR state matches the one at configuration time. At boot clevis then attempts to retrieve the secret and decrypt the disk which it only can if the state is trusted.
Security Implications
You read a lot about trust in this article. This is because trust has to be established. At it’s root we trust the manufacturer of the TPM2 and the root PCR to be immutable. Next we trust that if the UEFI, bootloader, kernel, initramfs, etc. are all unmodified we expect a trustworthy environment where it is OK to e.g. decrypt the disk. In terms of Secure Boot this means all the measurements stored in the PCRs do not change from one boot to another. This trust would be violated if one alters the UEFI settings, the boot partition, or boot the system from a live image instead of our main operating system.
You kinda have to trust or better verify that the manufacturer did not f* anything up in the overall platform design for this to be considered fairly safe. That being said there are a range of cases where they f* things up. E.g. when security researches showed that BitLocker on a Lenovo notebook would use unencrypted SPI communication with the TPM2 leaking the LUKS passphrase in plain text without even altering the system. Or that BitLocker may use native encryption features of SSD drives which may be by-passed through factory reset.
These examples are all about BitLocker but it should become clear that if the overall design is broken, then the secret is accessible and this alternative less secure than a passphrase only present in your head (and somewhere safe like a password manager). On the other hand keep in mind that in most cases elaborate research and attacks to access a drives data are not worth the effort for an opportunistic bad actor. Additionally not having to enter a passphrase on every boot should help adoption of this technology as it is transparent but adds additional hurdles to unwanted access.
Prerequisites
First check that:
- Secure Boot is enabled and working
- A TPM2 chip is available
- The clevis package is installed
Clevis is where the magic happens. It’s a tool we use in the running OS to bind the TPM2 as an alternative decryption method and use it inside the initramfs to read the decryption secret from the TPM2.
In the running system check if secure boot is enabled. The output of dmesg
should look like this:
$ dmesg | grep Secure
[ 0.000000] secureboot: Secure boot enabled
[ 0.000000] Kernel is locked down from EFI Secure Boot mode; see man kernel_lockdown.7
[ 0.005537] secureboot: Secure boot enabled
[ 1.582598] integrity: Loaded X.509 cert 'Fedora Secure Boot CA: fde32599c2d61db1bf5807335d7b20e4cd963b42'
[ 35.382910] Bluetooth: hci0: Secure boot is enabled
If a TPM2 chip is available it is also logged in dmesg
:
$ dmesg | grep TPM
[ 0.005598] ACPI: TPM2 0x000000005D757000 00004C (v04 DELL Dell Inc 00000002 01000013)
Install the clevis dependencies and regenerate your initramfs using dracut.
sudo dnf install clevis clevis-luks clevis-dracut clevis-udisks2 clevis-systemd
sudo dracut -fv --regenerate-all
sudo systemctl reboot
The reboot is important as only then the new PCR values based on the initramfs will be available for the next step.
Configure clevis
Command to bind the LUKS-encrypted partition with the TPM2 chip. Point it to your (root) LUKS partition and specify the PCRs it should use.
Enter your current LUKS passphrase when asked. The process uses this to generate a new independent secret, tying your LUKS partition to the TPM2 as an alternative decryption method. So if it does not work you may still just enter your decryption passphrase as usual.
sudo clevis luks bind -d /dev/nvme... tpm2 '{"pcr_ids":"1,4,5,7,9"}'
As mentioned in the Background section PCRs 1, 4 and 5 change when booting into another system such as a live disk. PCR 7 tracks the current UEFI Secure Boot policy and PCR 9 changes if the initramfs loaded via EFI changes.
Note: if you just want to protect the LUKS passphrase from live images but don’t care about more “elaborate” attacks such as altering the unsigned initramfs on the unencrypted boot partition, then you might omit PCR 9 and safe yourself the trouble of rebinding on updates.
Updating your system
On every update of your system that makes changes to the kernel, grub2 or initramfs you’ll have to rebind the TPM2, if you opted to use PCR 9. This is because this PCR will change after such an update and rightly so as this is protection against unwanted changes.
Rebind the TPM with the following command, entering your primary LUKS encryption passphrase:
sudo clevis luks regen -d /dev/nvme0n1... -s 1
More encrypted disks
In case of secondary encrypted partitions use /etc/crypttab
:
Use systemd-cryptenroll
to register the disk for systemd to unlock:
sudo systemd-cryptenroll /dev/nvme0n1... --tpm2-device=auto --tpm2-pcrs=1,4,5,7,9
Then reflect that config in your /etc/crypttab
by appending the options tpm2-device=auto,tpm2-pcrs=1,4,5,7,9
.
Unbind, rebind, and edit
List all current bindings of a device:
$ sudo clevis luks list -d /dev/nvme0n1... tpm2
1: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"0,1,2,3,4,5,7,9"}'
Unbind a device:
sudo clevis luks unbind -d /dev/nvme0n1... -s 1 tpm2
The -s
param specifies the slot of the alternative secret for this disk stored in the TPM. It should be 1 if you always unbind before binding again. If in doubt print alternative bindings with:
Regenerate binding, in case the PCRs have changed:
sudo clevis luks regen -d /dev/nvme0n1... -s 1 tpm2
Edit the configuration of a device:
sudo clevis luks edit -d /dev/nvme0n1... -s 1 -c '{"pcr_ids":"0,1,2,3,4,5,7,9"}'
Troubleshooting
Disk decryption passphrase prompt shows at boot, but goes away after a while
To avoid mounting the drives before the TPM is loaded slow down systemd by adding a sleep command to the systemd-ask-password-playmouth.service
file using systemd edit
:
[Service]
ExecStartPre=/bin/sleep 10
Add the following to the config file /etc/dracut.conf.d/systemd-ask-password-plymouth.conf
:
install_items+=" /etc/systemd/system/systemd-ask-password-plymouth.service.d/override.conf "
Then regenerate dracut
via sudo dracut -fv --regenerate-all
Reboot and then regenerate the binding:
sudo systemctl reboot
...
sudo clevis luks regen -d /dev/nvme0n1... -s 1
Resources
- Automatic LUKS volume unlocking using a TPM2 chip
- Automatically decrypt with TPM2 on Silverblue (Discussion)
- Right way to use the TPM for full disk encryption (Security StackExchange)
- How does the TPM perform integrity measurements on a system (Security StackExchange)
- Configuring SecureBoot + TPM2
- Switch PCR banks on TPM2 devices
- tpm2-luks project on Github
- Understanding TPM PCRs, PCR banks and their relations
- From a stolen laptop to inside the company network
- TPM library specification
- TCG EFI Platform specification
Any thoughts of your own?
Feel free to raise a discussion with me on Mastodon or drop me an email.