This post will walk through the process of automatically decrypting a LUKS encrypted drive on boot using a chain of trust implemented via Secure Boot and TPM 2.
Warning: This post does not discuss initramfs configuration. Configuration of the initramfs is distribution specific. Effort needs to be taken to ensure that the initramfs does not have a recovery shell or similar functionality.
Background
The Tevora Threat Team uses deployable devices for remote testing. The current generation of these devices consist of commercial off the shelf mini PCs with the Unified Extensible Firmware Interface (UEFI), Secure Boot and a Trusted Platform Module(TPM) available.
In order to better protect these systems during transit and while deployed, as they can potentially contain sensitive information, the use of technology available on these devices was evaluated. Initial evaluations showed that making use of the on-board TPM and Secure Boot capabilities were viable, if possibly reliant on bleeding-edge software. This post will discuss a simple, best-effort setup with custom Secure Boot, and encrypted storage unlocked via the platform's TPM, touching only briefly on the details of distribution-specific implementation.
The Plan
The idea is to use a custom Secure Boot configuration to provide complete boot chain authentication, and to use the TPM to restrict access to filesystem cryptography keys unless the device is booted with a proper boot configuration. The devices Tevora uses have no functionality available for freezing or restricting access to Secure Boot configuration: Even with a BIOS management password set, it can still be reset via onboard jumpers, allowing a potential attacker to easily disable Secure Boot, thus requiring both Secure Boot and TPM policy in order to maintain platform security.
Secure Boot Setup
As it turns out, setup of a quick and simple custom Secure Boot configuration can be made relatively straightforward. This requires the efitools
package. First, we generate the Secure Boot keys. Make sure to treat these keys with caution, as with these keys, a potential attacker could perform decryption of all devices. The Secure Boot private keys should only ever be used to sign new boot configurations if a kernel/initramfs update is required.
Generating Secure Boot Keys[1]
#Create Platform Key
uuidgen --random > GUID.txt
openssl req -newkey rsa:2048 -nodes -keyout PK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Platform Key/" -out PK.crt
openssl x509 -outform DER -in PK.crt -out PK.cer
cert-to-efi-sig-list -g "$(< GUID.txt)" PK.crt PK.esl
sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt PK PK.esl PK.auth
#Create Key Exchange Key
openssl req -newkey rsa:2048 -nodes -keyout KEK.key -new -x509 -sha256 -days 3650 -subj "/CN=my Key Exchange Key/" -out KEK.crt
openssl x509 -outform DER -in KEK.crt -out KEK.cer
cert-to-efi-sig-list -g "$(< GUID.txt)" KEK.crt KEK.esl
sign-efi-sig-list -g "$(< GUID.txt)" -k PK.key -c PK.crt KEK KEK.esl KEK.auth
#Creating Signature Database Key
openssl req -newkey rsa:2048 -nodes -keyout db.key -new -x509 -sha256 -days 3650 -subj "/CN=my Signature Database key/" -out db.crt
openssl x509 -outform DER -in db.crt -out db.cer
cert-to-efi-sig-list -g "$(< GUID.txt)" db.crt db.esl
sign-efi-sig-list -g "$(< GUID.txt)" -k KEK.key -c KEK.crt db db.esl db.auth
Next, we will generate and sign a secure boot chain. One of the quickest ways to do this with all bases covered is to use the shim efi binary available as part of systemd-boot. This binary can be used to generate a single EFI binary which contains the kernel, initramfs, and command-line, disallowing the user from making any boot configuration changes assuming a secure configuration of the installed OS.
- Preparation
- Configure the target device in a "release" secured configuration
- Creating Signed EFI Binary
- Save Desired Kernel Command Line to
cmdline.txt
- Generate and sign loader:
objcopy \
--add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \
--add-section .cmdline=cmdline.txt --change-section-vma .cmdline=0x30000 \
--add-section .linux="/boot/vmlinuz" --change-section-vma .linux=0x40000 \
--add-section .initrd="/boot/initrd.img" --change-section-vma .initrd=0x3000000 \
/usr/lib/systemd/boot/efi/linuxx64.efi.stub /boot/EFI/BOOT/BOOTX64.EFI
sbsign --key ${SBKEYS}/db.key --cert ${SBKEYS}/db.crt --output /boot/EFI/BOOT/BOOTX64.EFI /boot/EFI/BOOT/BOOTX64.EFI
Finally, we can set the device up:
- Copy *.cer, *.esl, *.auth files to a FAT32 formatted flash drive
- Clear the device's Secure Boot configuration and place Secure Boot in custom mode (device dependant) with Secure Boot enabled
- Boot keytool from the
efitools
package available at/usr/lib/efitools/x86_64-linux-gnu/KeyTool.efi
to load the corresponding .auth files into the key slots - Reboot. At this point, the device should only accept binaries signed via the above method
TPM2
The devices in question contain a TPM 2, which is not supported very well in common Linux distributions. For instance, on the latest Kali rolling at the time of this post, tpm2-tools and associated software are available, but the versions provided are incapable of interacting with the TPM of our devices.
The latest tools are available at https://github.com/tpm2-software/tpm2-tools/, and appear to have undergone rapid development since the versions available in the repositories.
While the software is straightforward to set up, documentation is somewhat lacking on usage at a higher level beyond low-level interactions with the TPM, and command syntax has been changed many times since tutorials and documentation were written.
Software Installation
- Follow https://github.com/tpm2-software/tpm2-tools/wiki/Getting-Started#installing with the following configure commands:
- To Configure tpm2-tss:
./configure --with-udevrulesdir=/etc/udev/rules.d/
- To Configure tpm2-tools:
./configure
- To Configure tpm2-abrmd:
./configure --with-dbuspolicydir=/etc/dbus-1/system.d
- To Configure tpm2-tss:
- At the time of writing, the following versions of tpm2 were used
- tpm2-tss: 2.0.0_rc3
- tpm2-tools: e25e9ee704d293fd3f1ad58004f3a3a3136cc97a (3.1.0_rc0 tpm2_evictcontrol seems to be broken)
- tpm2-abrmd: 2.0.0_rc0
TPM Usage
Now for the interesting question, can we effectively use the TPM to suit our needs for unlocking encrypted storage?
It would appear that there are essentially two simple methods of accomplishing this:
- Storing key data in TPM NVRAM, and restricting access to the NVRAM object with a policy
- Storing as a persistent TPM Object with an a policy restricting access
Of these options, Tevora chose the later as the current iteration of tpm2-tools does not seem to support policy based access to nvram.
The TPM2 contains a set of PCRs, or Platform Configuration Registers, which contain hashes of boot-time configuration.
Example (Dummy PCR Values):
TPM2 has the ability to create policies based off of PCRs: If the PCR contents do not match expectations, the policy will not authorize the action. The individual PCR values have various meanings and may be platform specific. For the devices in question, we determined that locking PCRs 0,2,3, and 7 would be suitable, with the option of locking PCR 1 to freeze the BIOS configuration. PCR 7 contains a hash of secure boot configuration. For the tpm2-tools, this PCR list is represented as: sha1:0,2,3,7
Using this information, we can create a sequence of TPM commands to initialize the TPM and store our secret, protected by a policy locking the PCRs:
- Reset TPM If Required
- Our devices come from the factory with the TPM locked using an unknown password. In addition, the factory specified procedure for resetting the TPM does not appear to work on these devices. The best solution we discovered for our device was to perform a recovery BIOS flash, which would reset the TPM to a blank state with no password.
- In theory, this can be accomplished from software via the tpm2_clear command, if the required password is known
- Store secret to TPM
- Create secret, example:
dd if=/dev/urandom of=secret.bin bs=32 count=1
- Load Secret into the TPM: Follow the snippet below
- Verify
- Run
tpm2_listpersistant
to list persistent objects:persistent-handle: 0x81000000
Example: Loading secret into TPM
tpm2_pcrlist -L sha1:0,2,3,7 -o pcrs.bin # Save our list of PCRs
tpm2_createpolicy -P -L sha1:0,2,3,7 -F pcrs.bin -f policy.digest # Create PCR Policy
tpm2_createprimary -a e -g sha1 -G rsa -o primary.context # Create primary TPM object
tpm2_create -g sha256 -G keyedhash -u obj.pub -r obj.priv -C primary.context -L policy.digest -A "noda|adminwithpolicy|fixedparent|fixedtpm" -I /tmp/secret.bin # Create TPM Object with Secret
tpm2_load -C primary.context -u obj.pub -r obj.priv -o load.context # Load object into the TPM
tpm2_evictcontrol -c load.context # Make TPM Object Persistant
#### Encrypted Partition Unlocking Via TPM To unseal our secret only requires a single command, using the object's persistent handle and our list of PCRs: ```language-bash tpm2_unseal -c 0x81000000 -L sha1:0,2,3,7 ```
This can be integrated to provide decryption depending on the distribution and requirements. For instance, in Kali and other debian derived distributions, TPM2 functionality can be added to the initramfs via hooks for initramfs-tools:
/etc/initramfs-tools/hooks/tpm2:
#!/bin/sh -e
if [ "$1" = "prereqs" ]; then exit 0; fi
. /usr/share/initramfs-tools/hook-functions
copy_exec /usr/local/bin/tpm2_unseal
copy_exec /usr/local/lib/libtss2-tcti-device.so
Use update-initramfs -u
to regenerate the initramfs. Remember, we will need to regenerate our combined boot chain and resign after changing the initramfs.
Using TPM from initramfs
Due to the lack of a full system environment, for this purpose, it was chosen not to run the tpm2 resource manager, tpm2-abrmd in the initramfs, as only a single TPM command is needed to perform the unseal, without the use of context memory.
The TPM2 resource manager is required to perform sequences of multiple TPM2 commands in many cases, as the TPM has limited available memory.
To bypass the resource manager, change the interfaced used by tpm2-tools to the device file:
export TPM2TOOLS_TCTI="device:/dev/tpm0"
Unlocking Volume
Naturally, multiple options are available across various distributions for performing disk unlock. The following is a simple example configuration for Kali Linux, and likely other Debian derived distributions using the TPM to unlock a LUKS encrypted partition:
/etc/crypttab, replace UUID with the UUID of your LUKS partition:
cryptname UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx none luks,keyscript=/usr/local/bin/passphrase-from-tpm
/usr/local/bin/passphrase-from-tpm, replace 0x81000000 if your persistant handle is different:
#!/bin/sh
set -e
echo "Unlocking via TPM" >&2
export TPM2TOOLS_TCTI="device:/dev/tpm0"
/usr/local/bin/tpm2_unseal -c 0x81000000 -L sha1:0,2,3,7
if [ $? -eq 0 ]; then
exit
fi
/lib/cryptsetup/askpass "Unlocking the disk fallback $CRYPTTAB_SOURCE ($CRYPTTAB_NAME)\nEnter passphrase: "
After installing these files, regenerate the initramfs:
update-initramfs -u
Further Development
Some of the inspiration for this post was drawn from several projects making an attempt to integrate TPM2 and encryption solutions, but are currently incompatible with the latest TPM2 tools. Developing these tools further would be of aid to the community:
https://github.com/rqou/tpm2-luks
https://github.com/WindRiver-OpenSourceLabs/cryptfs-tpm2
For use in a production system, the latest versions of the tpm2 tools should be properly packaged. This step is distribution specific, and is left as an exercise to the reader. Hopefully more recent tpm2-tools can be up-streamed soon, but with the command syntax seemingly changing from week to week, it is evident why later versions have not gone into standard use.
Caveats and Potential Improvements
- Using a single object can only store key sizes of 128 bits, possible workarounds:
- Use multiple objects to store parts of key
- Use NVRAM based key storage (pending on tpm2-tools policy support)
- TPM Security
- TPM2 has not been analyzed to the level as TPM1 has been
- Full validation of our methods has not been performed
- Additional TPM functionality
- Only minimal TPM functionality is discussed in this post
- Plenty of additional capabilities/features not expored
- Key Safety after Unseal
- Ideally, measures to prevent the key from being kept in memory should be implemented
- Additional Authentication Layers
- Implement password unlock in addition to Secure Boot verification
Sources and Further Reading
- TPM2 Command Specification: https://trustedcomputinggroup.org/wp-content/uploads/TPM-Rev-2.0-Part-3-Commands-01.38.pdf
- TPM2 Tools Wiki: https://github.com/tpm2-software/tpm2-tools/wiki
- "UEFI SecureBoot on Arch Linux" https://gist.github.com/zaxebo1/a17577390512bdea35a00d111dac8aa2
Sourced from ArchWiki:Secure_Boot. ↩︎