DEV Community

Achyuta Das
Achyuta Das

Posted on

Disk Encryption using LUKS and TPM2.0

Introduction to LUKS

Linux Unified Key Setup (LUKS) is a disk encryption specification that encrypts block devices, such as disk drives and removable storage media. LUKS offers Full Disk Encryption (FDE) and selective partition-based encryption.

The system prompts you for a passphrase every time you boot the computer to unlock the encrypted disk. LUKS encrypted volumes can be automatically unlocked (without the need to provide a passphrase at boot), using a Trusted Platform Module 2.0 (TPM 2.0) policy

Introduction to TPM2.0

The Trusted Platform Module 2.0 (TPM) is a hardware-based system security feature that securely stores passwords, certificates, and encryption keys to authenticate the platform. It is embedded in the server motherboard.

TPM supports auto unlocking of the encrypted disk during system startup without requiring any user intervention.

Setup Information

This was performed on vanilla Ubuntu 24.04 Partition based encryption. Ubuntu was installed on a VM which was created using multipass on top of Hyper-V, with TPM and secure boot enabled for the VM.

The steps should work on a bare-metal node as well.

root@honeyglaze:~# uname -r
6.8.0-60-generic

root@honeyglaze:~# cat /etc/os-release
PRETTY_NAME="Ubuntu 24.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.2 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo

# Validating TPM device
root@honeyglaze:~# ls -l /dev/tpm*
crw-rw---- 1 tss root  10,   224 May 22 05:49 /dev/tpm0
crw-rw---- 1 tss tss  253, 65536 May 22 05:49 /dev/tpmrm0

# Validating secure boot
root@honeyglaze:~# mokutil --sb-state
SecureBoot enabled

# cryptsetup version
root@honeyglaze:~# cryptsetup --version
cryptsetup 2.7.0 flags: UDEV BLKID KEYRING FIPS KERNEL_CAPI HW_OPAL

root@honeyglaze:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda       8:0    0   10G  0 disk                    #### We will be working on /dev/sda disk
sdb       8:16   0   20G  0 disk
├─sdb1    8:17   0   19G  0 part /
├─sdb14   8:30   0    4M  0 part
├─sdb15   8:31   0  106M  0 part /boot/efi
└─sdb16 259:0    0  913M  0 part /boot
sr0      11:0    1   54K  0 rom
Enter fullscreen mode Exit fullscreen mode

Encrypting the disk

  • Use the cryptsetup luksFormat command to encrypt the disk. Provide a passphrase for encrypting the partition.
root@honeyglaze:~# cryptsetup luksFormat /dev/sda

WARNING!
========
This will overwrite data on /dev/sda irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda:
Verify passphrase:
Enter fullscreen mode Exit fullscreen mode
  • Create a logical device-mapper that can be mounted to the encrypted partition. Provide the same passphrase given in the previous step to unlock the encrypted drive.
root@honeyglaze:~# cryptsetup luksOpen /dev/sda cryptpart
Enter passphrase for /dev/sda:
Enter fullscreen mode Exit fullscreen mode
  • Format the new partition with the ext4 file system.
root@honeyglaze:~# mkfs.ext4 /dev/mapper/cryptpart
mke2fs 1.47.0 (5-Feb-2023)
Creating filesystem with 2617344 4k blocks and 655360 inodes
Filesystem UUID: 73c76f23-804d-431d-84be-4767a6dc81c7
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information:  done
Enter fullscreen mode Exit fullscreen mode
  • Mount the new file system.
root@honeyglaze:~# mkdir -p /mnt/cryptpart

root@honeyglaze:~# mount /dev/mapper/cryptpart /mnt/cryptpart/

root@honeyglaze:~# lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
sda           8:0    0   10G  0 disk
└─cryptpart 252:0    0   10G  0 crypt /mnt/cryptpart
sdb           8:16   0   20G  0 disk
├─sdb1        8:17   0   19G  0 part  /
├─sdb14       8:30   0    4M  0 part
├─sdb15       8:31   0  106M  0 part  /boot/efi
└─sdb16     259:0    0  913M  0 part  /boot
sr0          11:0    1   54K  0 rom
Enter fullscreen mode Exit fullscreen mode
  • Adding a recovery key.
# Generate a key
root@honeyglaze:~# openssl rand -base64 32 | cut -c1-32 | tee keyfile.txt
UXItVWYLicvja4u4PdYtMZ/iPwcufRGz

# Use luksAddKey to add the generate key, enter the passphrase (given in step 1) when prompted.
root@honeyglaze:~# cryptsetup luksAddKey /dev/sda keyfile.txt
Enter any existing passphrase:
Enter fullscreen mode Exit fullscreen mode
  • Validate the key slots.
# Key slot 0 and 1 are for the primary passphrase and the recovery key

root@honeyglaze:~# cryptsetup luksDump /dev/sda
LUKS header information
Version:        2
Epoch:          4
Metadata area:  16384 [bytes]
Keyslots area:  16744448 [bytes]
UUID:           608d9da9-a3fa-4a4e-8923-c3e4d95247a3
Label:          (no label)
Subsystem:      (no subsystem)
Flags:          (no flags)

Data segments:
  0: crypt
        offset: 16777216 [bytes]
        length: (whole device)
        cipher: aes-xts-plain64
        sector: 4096 [bytes]

Keyslots:
  0: luks2
        Key:        512 bits
        Priority:   normal
        Cipher:     aes-xts-plain64
        Cipher key: 512 bits
        PBKDF:      argon2id
        Time cost:  6
        Memory:     1048576
        Threads:    2
        Salt:       c4 ec 58 59 3a 34 48 bb 01 44 94 6f 7f 41 4d d9
                    a8 74 ba a6 1a 57 60 86 f1 19 69 e5 c2 d6 e7 30
        AF stripes: 4000
        AF hash:    sha256
        Area offset:32768 [bytes]
        Area length:258048 [bytes]
        Digest ID:  0
  1: luks2
        Key:        512 bits
        Priority:   normal
        Cipher:     aes-xts-plain64
        Cipher key: 512 bits
        PBKDF:      argon2id
        Time cost:  5
        Memory:     1048576
        Threads:    2
        Salt:       3e fb 84 89 4a c2 53 bb 4e d8 0f d1 aa a3 f1 81
                    e1 31 8a af 67 69 b8 96 c1 da ba a1 d7 db 2f 72
        AF stripes: 4000
        AF hash:    sha256
        Area offset:290816 [bytes]
        Area length:258048 [bytes]
        Digest ID:  0
Tokens:
Digests:
  0: pbkdf2
        Hash:       sha256
        Iterations: 246375
        Salt:       2a 25 1d 0e b9 33 2b 8b b6 8f 28 fc 71 4e 51 08
                    78 ba d8 36 45 21 13 b4 6e 66 0a a5 85 f1 a9 eb
        Digest:     2a d1 7b 8a e2 b2 3d f1 17 29 7a 16 54 1e 3a 67
                    a8 f8 f0 f7 79 c1 a3 06 2b bd bf 09 9d 3b 36 4d
Enter fullscreen mode Exit fullscreen mode

Decrypt a LUKS volume with Clevis and TPM

Clevis is a pluggable framework for automated decryption. It can be used to provide automated decryption of data or even automated unlocking of LUKS volumes. I went with clevis since in case of passphrase in TPM chip is corrupted/unreachable, clevis automatically prompts for passphrase/recovery key without requiring intervention to update /etc/crypttab to drop TPM device.

  • Install the required packages.
apt-get install dracut-core clevis clevis-tpm2 clevis-luks clevis-dracut tss2
Enter fullscreen mode Exit fullscreen mode
  • Bind LUKS encryption key/password to TPM2.0. Enter the passphrase when prompted.
# "-s 2" specifies the key slot. 0 and 1 slots are already in use.
root@honeyglaze:~# clevis luks bind -d /dev/sdb tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'
Enter existing LUKS password:
Enter fullscreen mode Exit fullscreen mode
  • Validate the binding.
root@honeyglaze:~# clevis luks list -d /dev/sda
2: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'

root@honeyglaze:~# systemd-cryptenroll /dev/sda
SLOT TYPE
   0 password   # passphrase
   1 password   # recovery key
   2 other      # clevis binding
Enter fullscreen mode Exit fullscreen mode
  • Let's try to unlock the drive and see whether we are getting the prompt to enter the password or not.
# First we need to unmount the disk
root@honeyglaze:~# umount /mnt/cryptpart

root@honeyglaze:~# cryptsetup luksClose cryptpart

root@honeyglaze:~# lsblk
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
sda       8:0    0   10G  0 disk
sdb       8:16   0   20G  0 disk
├─sdb1    8:17   0   19G  0 part /
├─sdb14   8:30   0    4M  0 part
├─sdb15   8:31   0  106M  0 part /boot/efi
└─sdb16 259:0    0  913M  0 part /boot
sr0      11:0    1   54K  0 rom

# unlock command should not give a prompt to enter the passphrase
root@honeyglaze:~# clevis luks unlock -d /dev/sda -n cryptpart

root@honeyglaze:~# lsblk
NAME        MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
sda           8:0    0   10G  0 disk
└─cryptpart 252:0    0   10G  0 crypt
sdb           8:16   0   20G  0 disk
├─sdb1        8:17   0   19G  0 part  /
├─sdb14       8:30   0    4M  0 part
├─sdb15       8:31   0  106M  0 part  /boot/efi
└─sdb16     259:0    0  913M  0 part  /boot
sr0          11:0    1   54K  0 rom
Enter fullscreen mode Exit fullscreen mode

Enable automatic decryption during boot

  • Add an entry to /etc/crypttab for decrypting the volume at boot time.
# Use blkid to get the UUID of the encrypted drive
root@honeyglaze:~# blkid -t TYPE=crypto_LUKS
/dev/sda: UUID="608d9da9-a3fa-4a4e-8923-c3e4d95247a3" TYPE="crypto_LUKS"

root@honeyglaze:~# cat /etc/crypttab
# <target name> <source device>         <key file>      <options>
cryptpart    UUID=608d9da9-a3fa-4a4e-8923-c3e4d95247a3 none luks,discard,nofail
Enter fullscreen mode Exit fullscreen mode
  • Rebuild the initramfs and reboot.
root@honeyglaze:~# dracut -f

......
dracut[I]: *** Creating image file '/boot/initrd.img-6.8.0-60-generic' ***
dracut[I]: Using auto-determined compression method 'pigz'
dracut[I]: *** Creating initramfs image file '/boot/initrd.img-6.8.0-60-generic' done ***

root@honeyglaze:~# reboot
Enter fullscreen mode Exit fullscreen mode
  • Validate boot logs.

root@honeyglaze:~# journalctl -b > boot.log

root@honeyglaze:~# cat boot.log | grep tpm
May 22 08:10:24 localhost kernel: tpm_crb VTPM0101:00: [Firmware Bug]: Bad ACPI memory layout
May 22 08:10:24 localhost kernel: tpm_crb VTPM0101:00: [Firmware Bug]: Bad ACPI memory layout
May 22 08:10:26 honeyglaze systemd[1]: systemd-tpm2-setup-early.service - TPM2 SRK Setup (Early) was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:26 honeyglaze systemd[1]: systemd-tpm2-setup.service - TPM2 SRK Setup was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:29 honeyglaze systemd[1]: systemd-tpm2-setup-early.service - TPM2 SRK Setup (Early) was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:29 honeyglaze systemd[1]: systemd-tpm2-setup.service - TPM2 SRK Setup was skipped because of an unmet condition check (ConditionSecurity=measured-uki).
May 22 08:10:32 honeyglaze systemd[1]: tpm-udev.path - Handle dynamically added tpm devices was skipped because of an unmet condition check (ConditionVirtualization=container).

root@honeyglaze:~# cat boot.log | grep clevis
May 22 08:10:24 localhost systemd[1]: Started clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:26 localhost systemd[1]: clevis-luks-askpass.path: Deactivated successfully.
May 22 08:10:26 localhost systemd[1]: Stopped clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:24 localhost systemd[1]: Started clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:26 localhost systemd[1]: clevis-luks-askpass.path: Deactivated successfully.
May 22 08:10:26 localhost systemd[1]: Stopped clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:26 honeyglaze systemd[1]: Started clevis-luks-askpass.path - Forward Password Requests to Clevis Directory Watch.
May 22 08:10:29 honeyglaze systemd[1]: Started clevis-luks-askpass.service - Forward Password Requests to Clevis.
May 22 08:10:29 honeyglaze clevis-luks-askpass[809]: Unlocked /dev/disk/by-uuid/608d9da9-a3fa-4a4e-8923-c3e4d95247a3 (UUID=608d9da9-a3fa-4a4e-8923-c3e4d95247a3) successfully
May 22 08:10:36 honeyglaze systemd[1]: clevis-luks-askpass.service: Deactivated successfully.
May 22 08:10:36 honeyglaze systemd[1]: clevis-luks-askpass.service: Consumed 1.283s CPU time.
Enter fullscreen mode Exit fullscreen mode

Bonus Info

  • TPM is a hardware chip that can securely store keys and perform cryptographic operations.
  • pcr_id refers to the Platform Configuration Register (PCR) index that is used by the TPM (Trusted Platform Module) to store measurements of system state during the boot process. These registers in TPM (numbered 0–23 for TPM 2.0) that store hashes of system components during boot. These values change if the boot environment changes, which makes them great for detecting tampering.
  • When you bind a LUK S volume to TPM2 using Clevis , you specify one or more pcr_ids —for example, with clevis luks bind -d /dev/sdX tpm2 '{"pcr_ids":"0,7"}'. This command seals the decryption key to the current values of PCRs 0 and 7, which represent specific measurements of the system state (like firmware, bootloader, and kernel). If these PCR values change—due to modifications in boot parameters, kernel, initramfs, or the bootloader—the TPM will not unseal the key, and the system will prompt for the LUKS passphrase. This mechanism ensures that the disk can only be automatically decrypted in a known, trusted, and unmodified boot environment.
  • Execute sudo tpm2_pcrread sha256:0,1,2,3,4,5,6,7 to display the hashes. Reboot the validate whether these hash values are changing.

Top comments (0)