Skip to content

Instantly share code, notes, and snippets.

@chrisx8
Last active February 25, 2024 01:05
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisx8/cda23e2d1fa3dcda0d739bc74f600175 to your computer and use it in GitHub Desktop.
Save chrisx8/cda23e2d1fa3dcda0d739bc74f600175 to your computer and use it in GitHub Desktop.

Decrypt LUKS2-encrypted root partitions with TPM2

This guide is written for Arch Linux.

Requirements

  • systemd version 248 or newer
  • Use mkinitcpio for initramfs generation

Preparation

You need to find out what kernel module/driver is needed for your TPM.

# Note the DRIVER (`tpm_*`, e.g. `tpm_tis`) for your TPM
systemd-cryptenroll --tpm2-device=list

Configure mkinitcpio

You need to edit /etc/mkinitcpio.conf. Basically, you'll switch to systemd hooks and add tpm_X (replace with your actual TPM driver name) to MODULES.

For more information on configuring mkinitcpio, check out the mkinitcpio page on ArchWiki.

Add TPM module

Look for a line that starts with MODULES= in /etc/mkinitcpio.conf, and add tpm_X (replace with your actual TPM driver name) inside the parentheses.

For example, if you have this line in /etc/mkinitcpio.conf

MODULES=(i915)

Change it to this

MODULES=(i915 tpm_tis)

Switch to systemd hooks

Replace the following HOOKS:

Original Replace with
udev, usr, resume systemd
keymap, consolefont sd-vconsole
encrypt sd-encrypt

Place the replacement HOOKS in the original place of the original HOOKS.

For example, if you have this line in /etc/mkinitcpio.conf

HOOKS=(base udev autodetect modconf block keyboard encrypt filesystems fsck)

Change it to this

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

At the minimum, you should have these hooks:

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

Rebuild initramfs

sudo mkinitcpio -P

Edit kernel cmdline

At the minimum, you should have

rd.luks.uuid=your-uuid rd.luks.options=tpm2-device=auto

Notes:

  • You may use rd.luks.name=your-uuid=name instead of rd.luks.uuid=your-uuid to mount the LUKS volume with a specific name
  • You may include other options after tpm2-device=auto. Options should be comma-separated.

Enroll a TPM-based key to the LUKS volume

See man systemd-cryptenroll for a full list of available options.

# Auto-detect the available TPM and use PCR 0,2,4,7 for verification
# Replace /dev/block-device with your LUKS volume
sudo systemd-cryptenroll /dev/block-device --tpm2-device=auto --tpm2-pcrs=0,2,4,7

With PCR 0,2,4,7, these are validated at boot time:

  • System firmware executable (0)
  • Kernel (2)
  • Bootloader (4)
  • Secure boot state (7)

If you're using PCR 2 and multiple kernels, you need to enroll a key within each kernel.

Reboot and test

Your LUKS volume should automatically decrypt.


Upgraded firmware, kernel, or bootloader?

  • PCR 0,2,4,7 validates the firmware, kernel, and bootloader before releasing the decryption key.
  • If you upgraded firmware, kernel, or bootloader, TPM will not release the key. As a result, auto decryption will fail, and you'll be prompted for a passphrase.
  • You need to wipe the old key and enroll a new key.

Update TPM-based key

# wipe all TPM2 keys and enroll a new key with PCR 0,2,4
systemd-cryptenroll /dev/block-device --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=0,2,4,7

This will ask for your volume's passphrase. If you'd like to automate this, you may set the PASSWORD environment variable to your passphrase.

⚠️ SECURITY WARNING: Storing secrets (such as your LUKS volume's passphrase) is not a good idea. See this article for why you shouldn't do this.

@kishorviswanathan
Copy link

kishorviswanathan commented Jun 15, 2021

Wonderful. It is a really well written guide. 👏

Previously I was using clevis for this purpose. I even wrote mkinitcpio-clevis-hook to get it working. It did the job, but clevis took a few seconds to unlock. This one is way more faster.

One suggestion I have is to make that "Replacing hooks" section a bit more easier to understand. A noobie might find it confusing whether they should add systemd where ever they find udev, usr or resume. Or should they just add it once to the list. Also an example on how to add MODULES would be helpful.

@rittet
Copy link

rittet commented Oct 6, 2021

Works great! Is there a way around having to type the existing passphrase every time we enroll the tpm(After an update etc..)?

I’ve tried to pipe it in with no luck.

@chrisx8
Copy link
Author

chrisx8 commented Oct 6, 2021

Works great! Is there a way around having to type the existing passphrase every time we enroll the tpm(After an update etc..)?

I’ve tried to pipe it in with no luck.

Hey @Ritte88,

I tried to add a keyfile to use for this purpose, but unfortunately it seems like systemd-cryptenroll doesn't read the keyfile.

Also, immediately re-enrolling the TPM after updating might not work, especially if you're using PCR 2 and 4, because the PCR values might not change until you reboot. I usually just reboot after updating, unlock with passphrase, and re-enroll the TPM after login.

@rittet
Copy link

rittet commented Oct 7, 2021

@chrisx8 This will probably work:
systemd/systemd#20955

Edit: Can confirm it works.

@chrisx8
Copy link
Author

chrisx8 commented Oct 7, 2021

@rittet, great find! I just tested it and it works well. I'll add this to my writeup with a security warning, as environment variables aren't great for storing secrets (pointed out in systemd/systemd#20955).

Thanks again for the tip!

@rittet
Copy link

rittet commented Oct 7, 2021

@chrisx8, I created a RFE for systemd to support something similar to --key-file= in cryptsetup.
Lennart seemed to agree that something similar is needed, so hopefully the $PASSWORD is just a temporary workaround.

@tashian
Copy link

tashian commented Jun 15, 2022

Has anyone been able to precompute the PCRs for a kernel upgrade before reboot, so that an unattended kernel upgrade can happen?

@chrisx8
Copy link
Author

chrisx8 commented Jun 16, 2022

@tashian - As far as I know, this is unsupported, as the TPM computes PCRS from the current running environment.

@tashian
Copy link

tashian commented Jun 16, 2022

Hi @chrisx8, from what I'm seeing, precomputing the PCRs is unsupported by systemd-cryptsetup, but it's not a limitation of the TPM itself. This can theoretically work. You can seal a TPM key using a policy that contains precomputed PCR values—you're not limited to the current PCR values. (this is how BitLocker enables Windows upgrades without reentering a disk encryption password.)

I found this repo that describes the process and has a Python script to precompute the sha256 PCR values for an upgraded kernel and bootloader image, but I wasn't able to get it working. There's some sort of feature gap here and I don't know the low-level TPM functions well enough to fill it.

@tashian
Copy link

tashian commented Jun 22, 2022

Wanted to add a note for posterity. I was able to solve the issue I mentioned above, but not by precomputing the PCRs. The answer is simpler.

Most major Linux distros have adopted the shim bootloader in order to support a UEFI Secure Boot chain of trust. Here's a list of PCRs set by this bootloader.

Are you running the shim bootloader?
If UEFI secure boot is enabled on your system, the answer is probably yes.
You can check UEFI SecureBoot state by running mokutil --sb-state,
or run sudo tpm2_pcrread to see if PCR 14 has a non-zero value (it is set by the shim bootloader).

To avoid re-enrolling your LUKS encryption TPM key on every kernel upgrade, you can seal the key to TPM values 7 and 14.

  • TPM 7 hashes several lists of code signing certificates needed for UEFI secure boot. A UEFI bootloader must be signed with one of these certificates. The major distros get the shim bootloader signed for them by Microsoft.
  • TPM 14 hashes a list of code signing certificates in the shim bootloader itself. These are the certs that your distro's maintainers use to sign the kernel image.

If you trust your distro's signed kernel and bootloader images, PCRs 7 and 14 will give you the same level of protection as you would with 0, 2 and 4, without the need for re-enrollment.

There's a much longer discussion of this in this blog post and in this PR.

@rikkaneko
Copy link

How can I make Windows+Bitlock coexist with LUKS+TPM? What action would trigger the TPM invalidation? I see other people work

@chkimes
Copy link

chkimes commented May 14, 2023

Windows will change dbx values when it wants to which will change the PCR 7 value. You can use the TPM pin option rather than binding to PCRs to unlock the encryption key with just a password to avoid issues with dual booting, however it will require entering the password on every boot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment