Trusted Platform Module
The Trusted Platform Module, or TPM for short, is a secure cryptoprocessor that is available on most modern computers. Its purpose is to securely store decryption keys outside of RAM to prevent attackers from reading the keys from the RAM itself.
The two most common versions of the TPM are 1.2 and 2.0. Both versions are supported on Linux, but this article covers TPM 2.0. There are a few ways to use TPM under Linux: storing SSH and GPG keys in the TPM, generating random numbers and/or decrypting LUKS-encrypted drives.
Installation
Kernel
Device Drivers ---> Character devices ---> [*] TPM Hardware Support ---> <*/M> TPM HW Random Number Generator support <*/M> TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface <*/M> TPM 2.0 CRB Interface
USE flags
USE flags for app-crypt/tpm2-tss TCG Trusted Platform Module 2.0 Software Stack
doc
|
Add extra documentation (API, Javadoc, etc). It is recommended to enable per package instead of globally |
fapi
|
Enable feature API (requires openssl as crypto backend) |
mbedtls
|
Use net-libs/mbedtls as crypto engine |
openssl
|
Use dev-libs/openssl as crypto engine |
policy
|
Enable policy library (requires openssl as crypto backend) |
static-libs
|
Build static versions of dynamic libraries as well |
test
|
Enable dependencies and/or preparations necessary to run tests (usually controlled by FEATURES=test but can be toggled independently) |
Emerge
root #
emerge --ask app-crypt/tpm2-tss
Usage
SSH Private Key
Using a TPM for a private key makes it much harder for an attacker to steal a SSH private key. Filesystem permissions typically protect private keys. Unfortunately, this still means any malicious program running as a user can read related SSH private keys. A TPM stores keys in isolated hardware, in order to use it but not read or copy it.
Emerge
root #
emerge --ask app-crypt/tpm2-pkcs11
Configuration
Add the user to the tss group (example uses larry as the user):
root #
gpasswd -a larry tss
Then, as the user, create a new primary, token & private key. There is debate about whether it is better to create the private key in software and import it (easier to audit and trust the creation). Or create the private key on the TPM so it never touches the disk. For this example, create the private key via the TPM.
user $
tpm2_ptool init
user $
tpm2_ptool addtoken --pid=1 --label=ssh --userpin=PasswordRequiredToUsekey --sopin=AdminPasswordForUncommonModifications
user $
tpm2_ptool addkey --label=ssh --userpin=PasswordrequiredToUseKey --algorithm=ecc256
RSA and different key sizes are available. Find a complete list in the source.
--userpin
can be set to an empty string to mimic the behavior of an SSH key that doesn't have password protection. But leaving it empty means the physical theft of the computer can allow an attacker to use the SSH private key through possession of the TPM alone. Setting a password achieves two factors of authentication, something you have (TPM) and something you know (password).
Reading a public key
To retrieve the public key from the TPM, run:
user $
ssh-keygen -D /usr/lib64/pkcs11/libtpm2_pkcs11.so
Next, copy the public key to the authorized_keys file on a remote machine. At this time, ssh-copy-id does not work for this type of key.
Using a key
To use the TPM key for a single SSH connection:
user $
ssh -I /usr/lib64/pkcs11/libtpm2_pkcs11.so user@remote.host.tld
To use the TPM key for all SSH connections, add this to a the ssh config:
~/.ssh/config
PKCS11Provider /usr/lib64/pkcs11/libtpm2_pkcs11.so
For ssh-agent to remember the password for an active session bypassing the need to type it for each connection, run:
user $
ssh-add -s /usr/lib64/pkcs11/libtpm2_pkcs11.so
This command is necessary every time the system reboots or the ssh-agent session expires if configured to do so.
Random Number Generation
Using a hardware random number generator gives more entropy to the system and can therefore give faster random numbers. When the TPM drivers compiled in the kernel, there will be a new device named /dev/hwrng. This is the TPM random number generator.
To use it, first, emerge the package sys-apps/rng-tools which will be used to redirect /dev/hwrng into /dev/random.
root #
emerge --ask sys-apps/rng-tools
Then, simply start the service. By default, rng-tools looks for /dev/hwrng so it does not need any configuration to work.
root #
rc-service rngd start
rngd | * Caching service dependencies ... [ ok ] rngd | * Starting rngd ... [ ok ]
root #
rc-update add rngd default
Decrypting Root
The TPM can be used to decrypt LUKS drives using programs like Clevis. Clevis supports many methods to encrypt and decrypt data, but this guide will focus on using TPM to decrypt LUKS-encrypted drives.
Using this method to unlock a root partition means that if the conditions mentioned below are met, the system's root partition will automatically decrypt itself on boot! Without having a secure login/lock screen, or if this does not fit the required threat model, then proceed no further.
When app-crypt/tpm2-tss is installed, tpm2_pcrread will list the PCR values of the TPM. Each number at the beginning of the rows represents a different parameter that is calculated by the TPM chip to check the integrity of the system. If the system's configuration changes (for instance, if secure boot is disabled, if CMOS is reset, or if any UEFI settings are changed), then these values will be different.
root #
tpm2_pcrread
sha1: sha256: 0 : 0x181210f8f9c779c26da1d9b2075bde0127302ee0e3fca38c9a83f5b1dd8e5d3b 1 : 0xfc68f3b1c9b809ce39d3142d79d18a22df73914008f0378eb23a487f12c895de 2 : 0x0c47cda934d53d7ca29d822a59531dcf6d36cbd9740a4fd0b867a0343910a715 3 : 0xcdfba543ee8ef7fdb3d8b587648cc22dd792bbd6272cc5447307c7c106c2374c 4 : 0x5834860c74b368a6922b8064b557a48518455990c94e56993f04de1ff4803ac9 5 : 0x121ec7fc388e3fd084f8d46536adc3b2077d866839b5f5dc6248e6790bb3488e 6 : 0xd928266e89b1da2263838e86df3a430548ca1768bedbc9d4b20f9e370d5518df 7 : 0xb2caf463818e33587218f354ad3cf0e75a34a6e6124c4e3fddd1c4ef255b7b1f 8 : 0x0000000000000000000000000000000000000000000000000000000000000000 9 : 0x0000000000000000000000000000000000000000000000000000000000000000 10: 0x0000000000000000000000000000000000000000000000000000000000000000 11: 0x0000000000000000000000000000000000000000000000000000000000000000 12: 0x0000000000000000000000000000000000000000000000000000000000000000 13: 0x0000000000000000000000000000000000000000000000000000000000000000 14: 0x0000000000000000000000000000000000000000000000000000000000000000 15: 0x0000000000000000000000000000000000000000000000000000000000000000 16: 0x0000000000000000000000000000000000000000000000000000000000000000 17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 23: 0x0000000000000000000000000000000000000000000000000000000000000000
This functionally means that TPM can be used to check the integrity of the system at boot, and if it hasn't been tampered with, then it unlocks the root partition without any user input. If on the other hand, the PCR values are different, then the root partition will not be unlocked and will require the passphrase to be entered.
The numbers 0 through 7 are parameters fetched from various parameters of the UEFI:
PCR Index | PCR Usage |
---|---|
0 | SRTM, BIOS, Host Platform Extensions, Embedded Options ROMs and PI Drivers |
1 | Host Platform Configuration |
2 | UEFI driver and application code |
3 | UEFI driver and application configuration and data |
4 | UEFI Boot Manager Code and boot attempts |
5 | Boot Manager Code configuration and data and GPT partition table |
6 | Host Platform Manufacturer Specific |
7 | Secure Boot Policy |
For more information on the PCR IDs and their usage, refer to the official Trusted Computing Group's documentation, specifically section 2.3.4, table 1, on page 26.
app-crypt/clevis is available in the GURU repository which can be added with Eselect/Repository.
root #
eselect repository enable guru
Then app-crypt/clevis can be emerged:
root #
emerge --ask app-crypt/clevis
Adding a TPM LUKS key
First, this guide assumes that a LUKS-encrypted drive already exists and is in a usable state. For more information on this topic, refer to dm-crypt.
Use the cryptsetup luksDump command to list the keys associated to a drive. Keep at least one passphrase in the LUKS header, otherwise if the system configuration changes, access to all data will be lost.
root #
cryptsetup luksDump /dev/nvme0n1p3
LUKS header information Version: 2 Epoch: 3 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 15 Memory: 1048576 Threads: 4 Salt: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee AF stripes: 4000 AF hash: sha256 Area offset:111111 [bytes] Area length:111111 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 111111 Salt: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee Digest: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee
Now, using clevis, add a key to our LUKS header that will be stored in the TPM:
root #
clevis luks bind -d /dev/nvme0n1p3 tpm2 '{"pcr_bank":"sha256","pcr_ids":"0,2,3,5,6,7"}'
Enter existing LUKS password: Warning: Value 512 is outside of the allowed entropy range, adjusting it.
Adding too many PCR IDs in the clevis command might do more harm than good : some parameters change often, depending on the system's UEFI, resulting in a system that won't boot without user intervention. 0,2,3,5,6,7 seem to be a good configuration.
Finally, run cryptsetup luksDump again to confirm that the new key has been added to the LUKS header:
root #
cryptsetup luksDump /dev/nvme0n1p3
LUKS header information Version: 2 Epoch: 5 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: af9789b4-39fc-4e3c-aeba-4b9542a5d4e7 Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 15 Memory: 1048576 Threads: 4 Salt: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee AF stripes: 4000 AF hash: sha256 Area offset:111111 [bytes] Area length:111111 [bytes] Digest ID: 0 1: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 16 Memory: 1048576 Threads: 4 Salt: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee AF stripes: 4000 AF hash: sha256 Area offset:111111 [bytes] Area length:111111 [bytes] Digest ID: 0 Tokens: 0: clevis Keyslot: 1 Digests: 0: pbkdf2 Hash: sha256 Iterations: 111111 Salt: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee Digest: 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee 00 11 22 33 44 55 66 77 88 99 00 aa bb cc dd ee
Now, a second keyslot is shown : the one that's been created by clevis.
Rebuilding the initramfs
Dracut
Dracut natively supports clevis. When it's installed, dracut will detected it and automatically add the clevis module to the initramfs. Therefore, this is as simple as running the usual dracut command. You can check the output to confirm it added the clevis module:
root #
dracut
... dracut: *** Including module: clevis *** dracut: *** Including module: clevis-pin-sss *** dracut: *** Including module: clevis-pin-tpm2 *** dracut: *** Including module: crypt *** dracut: *** Including module: dm *** ...
Once the initramfs is built and deployed, the system is ready to reboot and automatically decrypt the root partition, as long as the system has not been tampered with.
Troubleshooting
pcr-input-file filesize does not match pcr set-list
If the TPM has multiple banks, such as SHA1 and SHA256, clevis will fail to encrypt data when given only the pcr_ids:
root #
echo "Super Secret Password" | clevis encrypt tpm2 '{"pcr_ids":"1,7"}' > pass.jwe
ERROR: pcr-input-file filesize does not match pcr set-list ERROR: Could not build pcr policy ERROR: Unable to run tpm2_createpolicy
To remedy to this, specify which bank to read the pcr_ids from. For example :
root #
echo "Super Secret Password" | clevis encrypt tpm2 '{"pcr_bank":"sha256","pcr_ids":"1,7"}' > pass.jwe
TPM is in DA lockout mode
If the TPM fails to boot and dracut repeatedly logs some errors about being unable to unseal:
WARNING:esys:src/tss2-esys/api/Esys_Unseal.c:295:Esys_Unseal_Finish() Received TPM Error ERROR:esys:src/tss2-esys/api/Esys_Unreal.c:98:Esys_Unseal_Finish() Received TPM Error ERROR: Esys_Unseal(0x99D) - tpm:session(1):a policy check failed ERROR: Unable to run tpm2_unseal Unsealing jwk from TPM failed! /dev/nvme0n1p3 could not be opened. Unable to unlock /dev/nvme0n1p3 (UUID=...)
This means dracut failed to unseal the key for one reason or another (most likely, the PCR IDs returned different values), and the TPM locked itself to prevent tampering. The easiest way to recover from this error is to power off the computer and wait for 10 minutes until the TPM unlocks itself. Another alternative is to reboot on a live CD and rebuild the initramfs without the clevis module, though that may very well take more than 10 minutes.
To avoid this error reoccurring in the future, try to figure out which PCR ID has changed, remove the TPM LUKS key, then add a new one without that ID.