How to Pre-Calculate and Sign TPM2 PCR Measurements in Linux Using systemd-measure

Modern Linux boot security relies on measured boot architectures where UEFI firmware, systemd-boot, the Linux kernel, and the initial ramdisk extend cryptographic hashes into hardware Trusted Platform Module (TPM 2.0) Platform Configuration Registers (PCRs). Historically, binding disk encryption keys or credentials to PCR registers created severe administrative friction: every kernel update, initramfs regeneration, or security patch altered PCR 11 values, locking users out of encrypted LUKS volumes unless manual recovery passwords were entered. Introduced in systemd 252, systemd-measure solves this by allowing developers and administrators to pre-calculate expected PCR 11 values ahead of time and cryptographically sign them with an asymmetric private key.

In this guide, you will learn how to pre-calculate PCR 11 hashes for Unified Kernel Images (UKIs), sign measurements using an RSA/ECDSA key pair, embed signatures into PE binaries, and seal secrets to signed boot policies in Linux.

Understanding PCR 11 and Unified Kernel Images (UKIs)

The Linux measured boot specification designates PCR 11 specifically for Unified Kernel Images:

  • Unified Kernel Image (UKI): A single UEFI PE executable combining the UEFI boot stub (systemd-boot), Linux kernel binary, initramfs archive, microcode updates, and kernel command line parameters into a unified binary.
  • Measured Boot Sequence: When the UEFI firmware executes the UKI, systemd-stub measures each embedded section (kernel, initrd, cmdline) and extends the SHA-256 hashes into TPM2 PCR 11 before passing control to the kernel.
  • The Pre-Calculation Challenge: Because the exact byte order and hash extensions are standardized, the final PCR 11 state can be computed mathematically before the system ever boots.
  • Signed PCR Policies: Instead of sealing disk encryption keys to a static, fragile PCR value, keys are sealed to a trusted public key. As long as the newly calculated PCR 11 value is signed by your private key, the TPM unseals disk keys seamlessly across kernel upgrades.

Generating an Asymmetric Signing Key Pair

To sign measured boot policies, generate an RSA or ECDSA private key and extract its public key component using OpenSSL:

# Create a secure directory for cryptographic keys
sudo mkdir -p /etc/systemd/tpm2-pcr-keys
sudo chmod 700 /etc/systemd/tpm2-pcr-keys

# Generate a 3072-bit RSA private key
sudo openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out /etc/systemd/tpm2-pcr-keys/tpm2-pcr-private.pem

# Extract the matching public key in PEM format
sudo openssl rsa -pubout -in /etc/systemd/tpm2-pcr-keys/tpm2-pcr-private.pem -out /etc/systemd/tpm2-pcr-keys/tpm2-pcr-public.pem

The public key (tpm2-pcr-public.pem) will be embedded into the boot environment and used by the TPM policy engine, while the private key remains protected on an offline build machine or root filesystem.

Pre-Calculating PCR 11 Values Using systemd-measure

To pre-calculate the expected PCR 11 value for a UKI or individual boot components (kernel, initrd, command line), invoke systemd-measure calculate:

systemd-measure calculate \
  --linux=/boot/vmlinuz-6.8.0-generic \
  --initrd=/boot/initrd.img-6.8.0-generic \
  --cmdline=/etc/kernel/cmdline \
  --pcr-bank=sha256

If you have already built a unified .efi UKI binary using ukify or objcopy, pass the binary directly:

systemd-measure calculate --uki=/boot/efi/EFI/Linux/ubuntu-6.8.0.efi --pcr-bank=sha256

The command outputs the exact 64-character hexadecimal SHA-256 hash that the TPM’s PCR 11 register will hold once this kernel is loaded by the bootloader.

Signing the Calculated Measurements

Once calculated, sign the measurement hash using your private key to generate a cryptographic signature file:

systemd-measure sign \
  --uki=/boot/efi/EFI/Linux/ubuntu-6.8.0.efi \
  --private-key=/etc/systemd/tpm2-pcr-keys/tpm2-pcr-private.pem \
  --public-key=/etc/systemd/tpm2-pcr-keys/tpm2-pcr-public.pem \
  --pcr-bank=sha256 \
  --output=/boot/efi/EFI/Linux/ubuntu-6.8.0.efi.sig

The resulting signature file contains a standardized JSON payload structure (systemd.pcr-signature) packaging the signed PCR bank, the expected digest, and the RSA signature.

Embedding Signatures into the UKI Binary

Rather than distributing separate .sig files on the EFI System Partition (ESP), modern UKIs embed the signature directly into a specialized PE executable section named .pcrsig.

You can embed the signature using objcopy:

sudo objcopy \
  --add-section .pcrsig=/boot/efi/EFI/Linux/ubuntu-6.8.0.efi.sig \
  --change-section-vma .pcrsig=0x40000 \
  /boot/efi/EFI/Linux/ubuntu-6.8.0.efi

When the system boots, systemd-stub reads the embedded .pcrsig section and passes the signature to systemd-cryptsetup.

Sealing LUKS Encryption Keys to the Signed Policy

With signed PCR measurements in place, enroll your LUKS-encrypted root volume against the public key rather than volatile raw PCR values using systemd-cryptenroll:

sudo systemd-cryptenroll /dev/nvme0n1p3 \
  --tpm2-device=auto \
  --tpm2-public-key=/etc/systemd/tpm2-pcr-keys/tpm2-pcr-public.pem \
  --tpm2-signature-path=/boot/efi/EFI/Linux/ubuntu-6.8.0.efi.sig

Now, during system boot:

  1. The TPM verifies that PCR 11 matches the digest in the signature.
  2. The TPM verifies that the digest was signed by the enrolled public key.
  3. If both checks succeed, the disk unlocks automatically.

When you install a new kernel, your packaging pipeline simply signs the new measurement with the same private key, allowing seamless, zero-touch automated updates without prompting users for manual LUKS recovery passphrases.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.