Trusted Platform Module/LUKS
Installation
Device Drivers --->
Character devices --->
[*] TPM Hardware Support --->
<*> TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface
<*> TPM 2.0 CRB Interface
USE flags
USE flags for app-crypt/tpm2-tss TCG Trusted Platform Module 2.0 Software Stack
+fapi
|
Enable feature API (requires openssl as crypto backend) |
+openssl
|
Use dev-libs/openssl as crypto engine |
+policy
|
Enable policy library (requires openssl as crypto backend) |
doc
|
Add extra documentation (API, Javadoc, etc). It is recommended to enable per package instead of globally |
mbedtls
|
Use net-libs/mbedtls as crypto engine |
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) |
USE flags for sys-fs/cryptsetup Tool to setup encrypted devices with dm-crypt
+argon2
|
Enable password hashing algorithm from app-crypt/argon2 |
+openssl
|
Use dev-libs/openssl crypto backend |
+udev
|
Enable virtual/udev integration (device discovery, power and storage device support, etc) |
fips
|
Enable FIPS mode restrictions |
gcrypt
|
Use dev-libs/libgcrypt crypto backend |
kernel
|
Use kernel crypto backend (mainly for embedded systems) |
nettle
|
Use dev-libs/nettle crypto backend |
nls
|
Add Native Language Support (using gettext - GNU locale utilities) |
pwquality
|
Use dev-libs/libpwquality for password quality checking |
ssh
|
Build cryptsetup-ssh for experimental support of token via SSH-server |
static
|
!!do not set this during bootstrap!! Causes binaries to be statically linked instead of dynamically |
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) |
urandom
|
Use /dev/urandom instead of /dev/random |
Emerge
root #
emerge --ask app-crypt/tpm2-tss
root #
emerge --ask sys-fs/cryptsetup
Clevis
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
Usage
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.
The TPM can be used to check the integrity of the system at boot. If it has been tampered with, then the root partition will not be unlocked and will require the passphrase to be entered. If no tampering has been detected, it unlocks the root partition without any user input.
Adding a TPM LUKS key
This guide assumes that a LUKS-encrypted drive already exists and is in a usable state. For more information on this topic, refer to Full Disk Encryption From Scratch Simplified.
The cryptsetup luksDump {device} command can be used to view keys associated with that device.
Keeping at least one LUKS key that uses a passphrase is recommended, otherwise all data will be irrecoverable if the keys are lost. If the only allowed key is in the TPM, and the UEFI changes, all encrypted 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 may do more harm than good because some parameters change often, depending on the system's UEFI, resulting in a system that won't boot without user intervention. PCRs 0, 2, 3, 5, 6, and 7 are reasonable choices.
Finally, cryptsetup luksDump can be used 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.
See also
- Secure Boot — an enhancement of the security of the pre-boot process of a UEFI system.