Next: Signing certificate and hash files, Previous: Lockdown when booting on a secure setup, Up: Security [Contents][Index]
TPM2 key protector extends measured boot to unlock the encrypted partition
without user intervention. It uses the TPM Storage Root Key (SRK) to seal
the disk key with a given set of PCR values. If the system state matches,
i.e. PCR values match the sealed PCR set, TPM2 key protector unseals the
disk key for cryptomount (see cryptomount) to unlock the
encrypted partition. In case the unsealed key fails to unlock the
partition, cryptomount falls back to the passphrase prompt.
Please note that TPM2 key protector uses the SRK in the owner hierarchy without authorization. If the owner hierarchy is password-protected, TPM2 key protector may fail to unseal the key due to the absence of the password. For the systems that already enable the password protection for the owner hierarchy, the following command removes the password protection with the existing password.
# tpm2_changeauth -c owner -p password
There are two supported modes to store the sealed key, SRK and NV index. The details will be addressed in later sections.
TPM2 key protector is currently only supported on EFI and EMU platforms.
Since TPM2 key protector relies on PCRs to check the system state, it is important to decide which PCRs to seal the key with. The following table lists uses of PCRs and the measured objects on EFI platforms.
| PCR | Used by | Measured Objects |
|---|---|---|
| 0 | Firmware | Core system firmware executable code |
| 1 | Firmware | Core system firmware data/host platform configuration; typically contains serial and model numbers |
| 2 | Firmware | Extended or pluggable executable code; includes option ROMs on pluggable hardware |
| 3 | Firmware | Extended or pluggable firmware data; includes information about pluggable hardware |
| 4 | Firmware | Boot loader and additional drivers; binaries and extensions loaded by the boot loader |
| 5 | Firmware | GPT/Partition table |
| 7 | Firmware | SecureBoot state |
| 8 | GRUB | Commands and kernel command line |
| 9 | GRUB | All files read (including kernel image) |
| 9 | Linux Kernel | All passed initrds (when the new LOAD_FILE2 initrd protocol is used) |
| 10 | Linux Kernel | Protection of the IMA measurement log |
| 14 | shim | “MOK” certificates and hashes |
PCR 0, 2, 4, and 7 can be used to check the integrity of the firmware code and bootloaders. PCR 8 and 9 are useful to check the file and data processed by GRUB. PCRs 10, 11, 12, 13, and 15 are controlled by the operating system, so those PCRs are usually still in the initial state when GRUB is running.
In general, it is nice to include PCR 0, 2, 4, and 7 to ensure the integrity of the firmware and bootloaders. For PCR 8 and 9, a sophisticated tool is required to examine the GRUB configuration files and the files to be loaded to calculate the correct PCR values.
Please note that PCRs are sensitive to any change, so an update of a component could invalidate the sealed key, due to the so-called PCR brittleness. For the bootloader update, PCR 4 may be affected. This can be mitigated by extracting the events from the TPM event log and predict the value with the updated bootloader binary. On the other hand, it is difficult to predict PCR 0~7 after a firmware update since the content of the code and the order of drivers may not follow the TPM event log from the previous firmware version, so it is necessary to reboot the system to update the measurement results of PCR 0~7 and seal or sign the sealed key again.
Reference: Linux TPM PCR Registry
Instead of using the existing password, it is recommended to seal a new random disk key and use the existing password for recovery.
Here are the sample commands to create a 128 random bytes key file and enroll the key into the target partition (sda2).
# dd if=/dev/urandom of=luks.key bs=1 count=128 # cryptsetup luksAddKey /dev/sda2 luks.key --pbkdf=pbkdf2 --hash=sha512
To unlock the partition with SRK mode, assume that the sealed key is in
(hd0,gpt1)/efi/grub/sealed.tpm, the following GRUB commands
unseal the disk key with SRK mode and supply it to cryptomount.
grub> tpm2_key_protector_init -T (hd0,gpt1)/efi/grub/sealed.tpm grub> cryptomount -u <UUID> -P tpm2
There are two programs to create the sealed key for SRK mode: grub-protect
and pcr-oracle (https://github.com/okirch/pcr-oracle).
The following sample command uses grub-protect to seal the random
key, luks.key, with PCR 0, 2, 4 and 7 in TPM 2.0 Key File format.
# grub-protect --action=add \
--protector=tpm2 \
--tpm2-pcrs=0,2,4,7 \
--tpm2key \
--tpm2-keyfile=luks.key \
--tpm2-outfile=/boot/efi/efi/grub/sealed.tpm
grub-protect only seals the key with the current PCR values.
Therefore, when a boot component, such as shim or GRUB, is updated, it is
necessary to reboot the system to update the measurement results and seal
the key again. That means the random disk key has to be stored in cleartext
for the next key sealing. Besides this, the measurement result of some PCRs
may differ between boot time and OS runtime. For example, PCR 9 measures the
files loaded by GRUB including the Linux kernel and initrd. To unlock the disk
containing the kernel and initrd, the key has to be sealed with PCR 9 value
before loading the kernel and initrd. However, PCR 9 changes after GRUB
loading the kernel and initrd, so PCR 9 at OS runtime cannot be used directly
for key sealing.
To solve these problems, pcr-oracle takes a different approach. It
reads the TPM eventlog and predicts the PCR values. Besides,
pcr-oracle also supports “authorized policy” which allows the
PCR policy to be updated with a valid signature, so that the user only seals
the random disk key once. If at some later time the PCR values change due to
an update of the system firmware, bootloader, or config file, the user just
needs to update the signature of the PCR policy.
To seal the key with the authorized policy, the first thing is to generate the RSA policy key, policy-key.pem, and the authorized policy file, authorized.policy. In this example, PCR 0, 2, 4, 7 and 9 are chosen for key sealing.
# pcr-oracle --rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9
Then, we seal the random disk key, luks.key, with the authorized policy file and save the sealed key in sealed.key.
# pcr-oracle --key-format tpm2.0 \
--auth authorized.policy \
--input luks.key \
--output sealed.key \
seal-secret
Since we now have the sealed key, we can remove the random disk key file luks.key.
The last step is to sign the predicted PCR policy and save the final key file, sealed.tpm.
# pcr-oracle --key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output /boot/efi/efi/grub/sealed.tpm \
sign 0,2,4,7,9
Here we also set a stop event for the prediction. With
--stop-event grub-file=grub.cfg --after, pcr-oracle stops
the calculation of PCR values right after GRUB loads grub.cfg.
When/After the shim or GRUB are updated, it only requires to run the last
pcr-oracle command to update the predicted PCR policy.
Instead of storing the sealed key in a file, NV index mode uses the TPM non-volatile memory to store the sealed key and could be useful when accessing the file is not possible.
However, the Linux root user must be careful who she/he gives access to the TPM (tss group) since those users will also be able to modify the NV index that’s holding the key.
There are two types of TPM handles supported by NV index mode: persistent handle and NV index handle.
The range of persistent handles is from 0x81000000 to 0x81FFFFFF.
The persistent handle is designed to make TPM objects persistent through
power cycles, and only TPM objects, such as RSA or EC keys, are accepted.
Thus, only the raw format is supported by persistent handles. The following
shows the grub-protect command to seal the disk key luks.key
into the persistent handle 0x81000000 with the PCRs 0,2,4,7.
# grub-protect \
--protector=tpm2 \
--action=add \
--tpm2-bank=sha256 \
--tpm2-pcrs=0,2,4,7 \
--tpm2-keyfile=luks.key \
--tpm2-nvindex=0x81000000
To unseal the key, we have to specify the mode nv, the persistent handle
0x81000000, and the PCRs 0,2,4,7 for the tpm2_key_protector_init
command.
grub> tpm2_key_protector_init --mode=nv --nvindex=0x81000000 --pcrs=0,2,4,7 grub> cryptomount -u <UUID> --protector tpm2
If the key in the persistent handle becomes unwanted, the following
grub-protect command removes the specified persistent handle
0x81000000.
# grub-protect \
--protector=tpm2 \
--action=remove \
--tpm2-evict \
--tpm2-nvindex=0x81000000
The range of NV index handles is from 0x1000000 to 0x1FFFFFF. Unlike the persistent handle, the NV index handle allows user-defined data, so it can easily support both the TPM 2.0 Key File format as well as the raw format.
The following grub-protect command seals the disk key luks.key into the NV index handle 0x1000000 with the PCRs 0,2,4,7 while using the TPM 2.0 Key File format.
# grub-protect \
--protector=tpm2 \
--action=add \
--tpm2key \
--tpm2-bank=sha256 \
--tpm2-pcrs=0,2,4,7 \
--tpm2-keyfile=luks.key \
--tpm2-nvindex=0x1000000
Furthermore, it is also possible to insert an existing key file, sealed.tpm, into a specific NV index handle using the following tpm2-tools (https://github.com/tpm2-software/tpm2-tools) commands.
# tpm2_nvdefine -C o \
-a "ownerread|ownerwrite" \
-s $(stat -c %s sealed.tpm) \
0x1000000
# tpm2_nvwrite -C o -i sealed.tpm 0x1000000
When unsealing the key in TPM 2.0 Key File format, only the mode nv
and the NV index handle 0x1000000 have to be specified for the
tpm2_key_protector_init command.
grub> tpm2_key_protector_init --mode=nv --nvindex=0x1000000 grub> cryptomount -u <UUID> --protector tpm2
The following grub-protect command allows to remove the specified
NV index handle 0x1000000.
# grub-protect \
--protector=tpm2 \
--action=remove \
--tpm2-evict \
--tpm2-nvindex=0x1000000
In order to test TPM2 key protector and TPM2 Software Stack (TSS2), it is useful to set up a software TPM (swtpm) instance and run the commands on the EMU platform.
Here are the commands to start a swtpm instance which provides a character
device interface. To store the TPM states, the directory, swtpm-state,
is created before the swtpm command. All the messages are stored
in swtpm.log including the name of the character device.
# mkdir swtpm-state
# swtpm chardev --vtpm-proxy --tpmstate dir=swtpm-state \
--tpm2 --ctrl type=unixio,path="swtpm-state/ctrl" \
--flags startup-clear --daemon > swtpm.log
Then, we extract the name of the character device from swtpm.log and save it to the variable, ‘tpm2dev’.
# tpm2dev=$(grep "New TPM device" swtpm.log | cut -d' ' -f 4)
Now we can start grub-emu with --tpm-device $tpm2dev to interact with the swtpm instance.
# grub-emu --tpm-device $tpm2dev
On the host, the tpm2-tools commands can interact with the swtpm instance by setting ‘TPM2TOOLS_TCTI’.
# export TPM2TOOLS_TCTI="device:$tpm2dev"
When the test is done, use swtpm_ioctl to send the shutdown command through the swtpm control channel.
# swtpm_ioctl -s --unix swtpm-state/ctrl
Next: Signing certificate and hash files, Previous: Lockdown when booting on a secure setup, Up: Security [Contents][Index]