# Trusted Platform Module

The [Trusted Platform Module](https://en.wikipedia.org/wiki/Trusted_Platform_Module) (TPM) is an international standard for a secure cryptoprocessor, which is a dedicated microprocessor designed to secure hardware by integrating cryptographic keys into devices. 

In practice a TPM can be used for various different security applications, for example in conjunction with [Secure Boot](/books/arch-linux/page/secure-boot), encryption key storage and random number generation.

To start using a TPM, it has to be present on your hardware (an fTPM or a dedicated device connected to a header on your mainboard) and enabled in the device's firmware settings.

## List of Platform Configuration Registers

Platform Configuration Registers (PCRs) hold cryptographic hashes that record the precise state of the system. While these registers can be read at any time, they can only be modified via an "extend" operation, blending new data with the preceding hashes to create an unalterable, cumulative cryptographic chain.

By verifying these measurements between boots, the system can detect unauthorized hardware or software tampering, for example in the scenario of an [Evil Maid attack](https://en.wikipedia.org/wiki/Evil_maid_attack). If any component has been tampered with (i.e. the hash chain deviates from a previously known good configuration), the TPM will not release its secrets. Once the PCRs prove that the correct, untampered operating system has booted, they can be safely trusted to automatically unlock your disk encryption keys.

The [UAPI Group] describes the TPM PCRs as follows: 

[UAPI Group]: https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/

| PCR   | Measured Component           | Notes                                                             |
| ----- | ---------------------------- | ----------------------------------------------------------------- |
| PCR0  | Core System Firmware         | BIOS/UEFI executable code; changes on firmware update.            |
| PCR1  | System Firmware Config       | Firmware settings, hardware config, and boot order.               |
| PCR2  | Extended Executable Code     | [Option ROMs] of PCIe cards, GPUs, RAID controllers, etc.         |
| PCR3  | Extended Configuration       | Device data from the UEFI Boot Device Select (BDS) phase.         |
| PCR4  | Boot Manager Code            | Bootloader binary (e.g. GRUB, `systemd-boot`) and boot attempts.  |
| PCR5  | Boot Manager Config          | Bootloader configuration and the GPT partition table.             |
| PCR6  | State Transitions            | ACPI power states and S4/S5 suspend/resume events.                |
| PCR7  | Secure Boot State            | Secure Boot status and database keys (PK, KEK, db, dbx).          |
| PCR8  | Kernel Command Line          | Literal kernel parameters passed by the bootloader.               |
| PCR9  | Initramfs / Initrd           | External initramfs/initrd image passed by the bootloader.         |
| PCR10 | IMA                          | Linux [Integrity Measurement Architecture] (IMA) log.             |
| PCR11 | Unified Kernel Image (UKI)   | UKI components (kernel, initrd, cmdline) via `systemd-stub`.      |
| PCR12 | Boot Credentials / Overrides | Systemd boot credentials and UKI command-line extensions.         |
| PCR13 | System Extensions            | System extension (`sysext`) and config (`confext`) overlays.      |
| PCR14 | Shim / MOK                   | Shim bootloader binary and Machine Owner Key (MOK) certificates.  |
| PCR15 | System Identity              | Operating system state (machine ID, LUKS keys, root UUID).        |

For further details on how PCR 11-13 are used, see [`systemd-stub(7)`][systemd-stub].

[Option ROMs]: https://en.wikipedia.org/wiki/Option_ROM
[Integrity Measurement Architecture]: https://ima-doc.readthedocs.io/en/latest/ima-concepts.html
[systemd-stub]: https://www.freedesktop.org/software/systemd/man/systemd-stub.html

## Usage
### TPM2-based LUKS key

The most common practical application for a TPM on Linux is securing LUKS disk encryption. Integrating the TPM chip directly into a LUKS setup ties the security of the encrypted volume to the physical integrity of the hardware. This integration is handled via `systemd-cryptenroll`, which offers the option to either fully automate decryption at boot or require a hardware-backed PIN.

During enrollment, `systemd-cryptenroll` generates a unique, secure unlock key and sends it to the TPM alongside a policy tied to specific PCR states which record the boot configuration of the system. The TPM encrypts (or "seals") this key using its own unique hardware, producing a protected data blob. This blob is then stored directly inside the LUKS volume's metadata header.

At boot, `systemd-cryptsetup` passes this blob back to the TPM. The TPM immediately measures the current, real-time PCR values to ensure no firmware, bootloader files, or core configurations have been altered. If the current PCR measurements match the policy bound to the blob, the TPM decrypts the blob and unlocks the LUKS partition; either instantly or upon the entry of a designated PIN. If a mismatch is detected, indicating the boot path has been tampered with, the TPM will deny the decryption request, making the system fall back to other configured methods of unlocking the volume.

<div class="callout danger">
  <p><strong>WARNING:</strong> When using this method on your root volume, there are a few caveats to be aware of.</p>

  <p>Provided the PCR banks you chose to seal against are considered valid by the system, the TPM will automatically unlock the LUKS volume at boot without the need to enter a passphrase.</p>

  <p>However, this also means that in case of theft, it's as if you didn't encrypt the system to begin with. Since the computer just has to be booted up to gain access to the decrypted data, all benefits of data-at-rest encryption are nullified.</p>

  <p>It is therefore <strong>strongly recommended</strong> to at least pass the <code>--tpm2-with-pin=yes</code> option to <code>systemd-cryptenroll</code> to still have a mechanism for user authentication. Despite the name of the option, alphanumeric values can be supplied during interactive enrollment.</p>

  <p>Also, make sure the system is in the exact state it should be for normal booting, i.e. the entire system is exactly set up the way you want it and there will be no further changes to core components, especially Secure Boot keys.</p>
</div>

#### Enrolling a new key

Certain preconditions are necessary to use TPM2 in conjunction with a LUKS encrypted volume:

* `tpm2-tss` must be installed
* the volume uses LUKS2 encryption (default when using `cryptsetup`)
* the initramfs must be systemd-based (`mkinitcpio` hooks: `systemd` and `sd-encrypt`)

Install the `tpm2-tss` package from the repositories to provide the necessary TPM software stack tooling:

~~~bash
pacman -S tpm2-tss
~~~

List the TPM2 devices available on your machine:

~~~bash
systemd-cryptenroll --tpm2-device=list
~~~

<p class="callout info"><strong>NOTE:</strong> If no devices are listed, make sure the TPM is enabled in your device's firmware settings. Devices from 2016 onwards usually have a TPM 2.0, as it is a <a href="https://www.computerworld.com/article/3101427/microsoft-mandates-windows-10-hardware-change-for-pc-security.html" target="_blank">requirement from Microsoft</a> for Windows 10 certification for hardware manufacturers.</p>

To enroll a new TPM-based key into a LUKS slot, specify the TPM device and the PCRs to seal against, followed by the LUKS volume to save a new slot to:

<p class="callout warning"><strong>ATTENTION:</strong> While binding a LUKS key to a broader set of PCRs increases system security by verifying more stages of the boot process, this hardening comes at the direct expense of system flexibility. On a rapid rolling-release distribution like Arch Linux, an overly restrictive PCR policy is virtually guaranteed to cause regular boot-time breakages. For instance, in a standard modern setup utilizing systemd-boot, UKIs, and Secure Boot, binding to volatile registers like PCR 4 (bootloader binary) or PCR 11 (hash of UKI) means that routine system updates will constantly alter these measurements, invalidating the automated decryption policy and reverting the boot process to a manual passphrase prompt.</p>

<p class="callout info"><strong>NOTE:</strong> In the unlikely event that your machine has more than 1 TPM device, supply its resource manager device node, e.g. <code>--tpm2-device=/dev/tpmrm0</code>.</p>

~~~bash
systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 --tpm2-with-pin=yes /dev/disk/by-label/cryptroot
~~~

`systemd-cryptenroll` will require to authenticate the addition of a TPM2 token by prompting for an existing passphrase, followed by asking to enter a new TPM2 PIN. This PIN will become the new authentication method at boot. If validation fails for whatever reason, it will fall back to a regular passphrase.

#### Recovery key

When planning to rely only on TPM unlocking of LUKS volumes, it is also generally advised to add a recovery key, in case TPM validation fails for whatever reason.

A recovery key is generated automatically with a character set that's easy to type in while still having high entropy (brute force mitigation).

To generate a recovery key and have it saved to a slot in the LUKS device:

~~~bash
systemd-cryptenroll --recovery-key /dev/disk/by-label/cryptroot
~~~

#### Unlocking at boot

Making systemd use the TPM to unlock the volume can be done in one of two ways:

1. add kernel parameters, telling systemd which device to unlock with the TPM
1. use a `/etc/crypttab.initramfs` file to be included in the initramfs to point systemd to the correct volume

For the kernel command line, add the following:

<p class="callout info"><strong>NOTE:</strong> In the unlikely event that your machine has more than 1 TPM device, supply its resource manager device node, e.g. <code>tpm2-device=/dev/tpmrm0</code>.</p>

~~~
rd.luks.options=tpm2-device=auto
~~~

If you'd rather use a `crypttab.initramfs` file, add `tpm2-device=auto` to the `<options>` field:

~~~
# <name>	<device>									<passphrase>	<options>
root		UUID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX	none			tpm2-device=auto
~~~