vmovupd blog

HomeMastodonCodeberg

Notes on secure boot, FDE and TPM2 on GNU/Linux systems

Secure boot can be used to validate that the boot loader, Linux kernel image and initrd were not tampered with during the boot process. Verification is performed with cryptographic signatures, whose public keys to verify them are stored in UEFI's DB (allow list database). Some distributions, like Ubuntu and Fedora, sign kernel image and boot loader using their keys and verify the signature with signed shim. Other distros, like Arch Linux and Gentoo, do not ship secure boot by default as it is not required/not a priority, but users can choose to implement it themselves.

As for FDE a.k.a Full Disk Encryption, it can be enabled during installation in the aforementioned distributions. The most common and feature-complete solution for FDE is LUKS, usually in combination with LVM. To unlock the volume (if LVM is used), LUKS permits simple passphrase, FIDO2 tokens, TPM (Trusted Platform Module, specification 2 is currently the one being used) or PKCS#11 tokens and smartcards. On top of thos emethods, other forms of authentication can be added, like a PIN for TPM, so that the secret used to encrypt LUKS volume is only unsealed from the TPM if the PIN is correct. TPM has dictionary lockout feature that prevents brute force attacks which allows users to set shorter, perhaps easier-to-remember passwords compared to a passphrase option.

But TPM can also be used to measure different components during boot process with its Platform Configuration Registers (PCRs). Measuring basically means that for every component that is loaded during boot process, a hash value is calculated and written to corresponding PCR. Moreover, TPM can have several PCR banks which is simply different hash algorithms (SHA256, SHA512 and SHA3-256/384 are commonly used) to reduce the risk of hash collision. Naturally, PCRs are initialized with zeros at the start of the boot process and it is not possible to reset PCR values back to zero during runtime. However, it is possible to "predict" those values in advance and use them to bind TPM to current values of PCRs or create a PCR policy, so that the secret for LUKS is unsealed only if the hash value of firmware executable code/secure boot state/etc is the one, specified during enrollment / in the policy.

TPM has also volatile nvindexes which are persistent memory registers for applications to allocate and store data in (small chunks). systemd-tpm2-setup.service early boot service can allocate additional PCRs using thos nvindexes which are called NvPCRs. In v259 NvPCRs are used to measure SMBIOS/Devicetree product UUID and cryptsetup unlock mechanism used, but it will possibly be extended for more use-cases.

When it comes to implementing secure boot, the default Arch Linux installation has separate files for all three components to be verified with systemd-boot used as a default boot loader. A better solution is UKI (Unified Kernel Image) which is a combination of a Linux kernel image, initrd and a UEFI boot stub (systemd-stub in that case) into single EFI file that can be directly invoked by firmware. As it is one EFI (and by extension PE) file, it can then be signed for the use in secure boot, just like boot loader. UKI contains also kernel command line string and, optionally, other stuff, such as OS kernel version, boot splash, devicetree (describes hardware components, not used on x86 as it has ACPI, but helpful on embedded systems, SoCs), public key used to sign PCR policy, a JSON file which contains an expected value of PCR 11 (all components of UKI) along with the signature (can also contain several values and signatures for each PCR bank).

PCR 11 measures all the sections inside UKI, except for .pcrsig which contains expected value for PCR 11 and signature of it.

List of tools used in connection with LUKS, TPM and UKI:

References:

Additional info

Linux Integrity Measurement Architecture can be used to extend verification to userland programs in secure boot chain.

On Fedora, it is possible to use out-of-tree modules (when module.sig_enforce=1 is used) when they are signed with MOK key, so the kernel is not tainted. See guide on Fedora Project. There are, however, patches that have to be applied to Linux kernel in order for this to work. Those patches are included in Fedora, but not in Arch Linux. Currently, each time a kernel is built in Arch Linux, a random key pair is generated and thats it. It should also be noted that MOK will not work without Shim. See also Signed kernel modules on arch wiki. If signing of out-of-tree kernel modules is desired, then the best solution is to compile own kernel, including own keys, but this is highly impractical to individual user.