Skip to content

Instantly share code, notes, and snippets.

@kmille
Last active June 1, 2025 10:08
Show Gist options
  • Save kmille/1bc2e4b84adac13f4cc529e9f0b6391a to your computer and use it in GitHub Desktop.
Save kmille/1bc2e4b84adac13f4cc529e9f0b6391a to your computer and use it in GitHub Desktop.
How to use a TPM on Linux

This is part of a blog post I wrote: https://debugging.works/blog/tpm-explained/

How to use a TPM on Linux

Prerequisites

  • I use it on Arch Linux (systemd 257.3-1)
  • Install dependency: yay tpm2-tools (5.7-1)

Do I have a TPM 2.0?

kmille@linbox:~ journalctl --boot --dmesg --grep=tpm_tis
Mar 05 12:14:27 linbox kernel: tpm_tis MSFT0101:00: 2.0 TPM (device-id 0x0, rev-id 78)

kmille@linbox:~ systemd-analyze has-tpm2
yes
+firmware
+driver
+system
+subsystem
+libraries
  +libtss2-esys.so.0
  +libtss2-rc.so.0
  +libtss2-mu.so.0

kmille@linbox:~ systemd-cryptenroll --tpm2-device=list
PATH        DEVICE      DRIVER 
/dev/tpmrm0 MSFT0101:00 tpm_tis

Show PCR values

  • you can also use the tool tpm2_pcrread
kmille@linbox:~ systemd-analyze pcrs
NR NAME                SHA256                                                          
 0 platform-code       e24699d6713720c6f6b912ed53c24ed8d3b921ab132a4e7ef8599c79935cfdff
 1 platform-config     0bbaeb2077143a79b1e011839528b383b78b6168f9efe680a08d468fa4dc6582
 2 external-code       3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 3 external-config     3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 4 boot-loader-code    6ea1add10c66122467134b64a502208bcc4e9e841dc2455a6cc3e105548965ed
 5 boot-loader-config  610da8f089771da655d219ed30c204a061477e4e153e58569759ca9101d7924a
 6 host-platform       3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 7 secure-boot-policy  915f1e9f770dcb1bbff7c25293be747805f9fddf607310827f2396582267902b
 8 -                   f15076071efe7176235c8c1d4c1c44d1dac19c2878f80d4fd1b4b043678f9e5b
 9 kernel-initrd       70ab0d5401f7275b9280a8fa0e487112f2e2f3cc257cbf8ec41acde854298efa
10 ima                 0000000000000000000000000000000000000000000000000000000000000000
11 kernel-boot         0000000000000000000000000000000000000000000000000000000000000000
12 kernel-config       0000000000000000000000000000000000000000000000000000000000000000
13 sysexts             0000000000000000000000000000000000000000000000000000000000000000
14 shim-policy         0000000000000000000000000000000000000000000000000000000000000000
15 system-identity     0000000000000000000000000000000000000000000000000000000000000000
16 debug               0000000000000000000000000000000000000000000000000000000000000000
17 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
18 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
19 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
20 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
21 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
22 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
23 application-support 0000000000000000000000000000000000000000000000000000000000000000

How are PCRs extended?

First, install tpm2-tools.

  1. Get current value of PCR 7
  2. Get sha256 of /etc/passwd (just an example)
  3. Extend PCR 7 with hash of /etc/passwd (you can only extend hash values. Probably because memory is limited in the TPM)
  4. Get new value of PCR 7
kmille@linbox:~ sudo tpm2_pcrread sha256:7 
  sha256:
    7 : 0x3B6994F4FC70B3F8715ADE0CC477987D170D0D52EC19ECA50DBFC33C3DA70010

kmille@linbox:~ cat /etc/passwd | sha256sum       
0e33a0c414b1d752930473d5eccf46ddf5bd2333328ed5562ec337b63c08465a  

kmille@linbox:~ sudo tpm2_pcrextend 7:sha256=0e33a0c414b1d752930473d5eccf46ddf5bd2333328ed5562ec337b63c08465a

kmille@linbox:~ sudo tpm2_pcrread sha256:7
  sha256:
    7 : 0xCEDF7419118AB3B7305A077E41BC9AA29E70C37FE2CB9712B17043213E1FFA83

To fully understand, let's reproduce with python

#!/usr/bin/env python
import hashlib
from binascii import unhexlify

current_hash = unhexlify("3B6994F4FC70B3F8715ADE0CC477987D170D0D52EC19ECA50DBFC33C3DA70010")
new_data = unhexlify("0e33a0c414b1d752930473d5eccf46ddf5bd2333328ed5562ec337b63c08465a")

data = current_hash + new_data
sha2 = hashlib.sha256(data)
print(sha2.hexdigest())
kmille@linbox:~# ./hashit.py
cedf7419118ab3b7305a077e41bc9aa29e70c37fe2cb9712b17043213e1ffa83

How to reset a PCR?

The TPM does not allow to reset PCRs. It's possible for PCR 16, which is a debug PCR.

kmille@linbox:~# sudo tpm2_pcrreset 7
WARNING:esys:src/tss2-esys/api/Esys_PCR_Reset.c:287:Esys_PCR_Reset_Finish() Received TPM Error 
ERROR:esys:src/tss2-esys/api/Esys_PCR_Reset.c:98:Esys_PCR_Reset() Esys Finish ErrorCode (0x00000907) 
ERROR: Esys_PCR_Reset(0x907) - tpm:warn(2.0): bad locality
ERROR: Could not reset PCR index: 7
ERROR: Unable to run tpm2_pcrreset

kmille@linbox:~# sudo tpm2_pcrreset 16
kmille@linbox:~# sudo tpm2_pcrread sha256:16
  sha256:
    16: 0x0000000000000000000000000000000000000000000000000000000000000000

How to use a TPM for full disk encryption?

First, create a disk

kmille@linbox:~ fallocate -l 100m disk.raw
kmille@linbox:~ 

Encrypt disk (use an empty password here, it can be removed later)

kmille@linbox:~ sudo cryptsetup luksFormat disk.raw 

WARNING!
========
This will overwrite data on disk.raw irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for disk.raw: 
Verify passphrase:


kmille@linbox:~ sudo systemd-cryptenroll disk.raw                                                         
SLOT TYPE    
   0 password

Enroll TPM

  • --tpm2-device=auto automatically uses the right TPM device (most of the time there is just one)
  • --tpm2-with-pin requires a pin to unlock the device
  • --tpm2-pcrs=1+2+3+4 sets the PCR registers
  • --wipe-slot=empty automatically removes key slot 0 (the one with the empty password)
kmille@linbox:~ sudo systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=yes --tpm2-pcrs=1+2+3+4 --wipe-slot=empty disk.raw
πŸ” Please enter current passphrase for disk /home/kmille/disk.raw:                         
πŸ” Please enter TPM2 PIN: β€’β€’β€’β€’                    
πŸ” Please enter TPM2 PIN (repeat): β€’β€’β€’β€’                    
New TPM2 token enrolled as key slot 1.
Wiped slot 0.

kmille@linbox:~ sudo systemd-cryptenroll disk.raw                                                                             
SLOT TYPE
   1 tpm2

Add recovery key

kmille@linbox:~ sudo systemd-cryptenroll --unlock-tpm2-device=auto --recovery-key disk.raw 
Automatically discovered security TPM2 token unlocks volume.
πŸ” Please enter TPM2 PIN: β€’β€’β€’β€’                    
A secret recovery key has been generated for this volume:

    πŸ” rhdhclnt-chjudccv-hhtunvnj-cceutteg-cnvdlnlg-njdbrnkv-rlugknnj-nfldvrie

Please save this secret recovery key at a secure location. It may be used to
regain access to the volume if the other configured access credentials have
been lost or forgotten. The recovery key may be entered in place of a password
whenever authentication is requested.

Optionally scan the recovery key for safekeeping:

β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–„β–„β–„β–„β–„ β–ˆβ–€β–ˆ β–ˆβ–„ β–€β–€ β–€β–„β–„β–„β–„β–ˆβ–„β–ˆβ–ˆ β–„β–„β–„β–„β–„ β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–ˆ   β–ˆ β–ˆβ–€β–€β–€β–ˆ β–€β–€β–ˆβ–ˆβ–€β–€β–€β–„β–„ β–€β–€β–ˆ β–ˆ   β–ˆ β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–„β–„β–„β–ˆ β–ˆβ–€ β–ˆβ–€β–€ β–€β–€ β–„β–„β–„β–„β–„β–ˆβ–„ β–ˆ β–ˆβ–„β–„β–„β–ˆ β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–„β–„β–„β–„β–„β–„β–„β–ˆβ–„β–€ β–€β–„β–ˆβ–„β–ˆ β–ˆβ–„β–€ β–€ β–€ β–ˆβ–„β–„β–„β–„β–„β–„β–„β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ  β–„ β–„β–ˆβ–„ β–„β–„β–€β–„β–€ β–„β–„β–€ β–€ β–€β–€β–„ β–„β–„β–€β–„β–ˆβ–„β–€β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–„β–€ β–„ β–ˆβ–„  β–ˆβ–„β–ˆβ–€β–„β–ˆ  β–„β–€β–ˆβ–€  β–€β–„β–€β–€β–€β–„ β–€β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ β–€β–€β–„β–„β–€ β–„β–„β–ˆβ–„ β–ˆβ–„  β–€ β–€β–€  β–„ β–€β–„β–„β–€β–ˆβ–ˆ β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ  β–€β–ˆ β–€β–„ β–ˆβ–ˆβ–€ β–„β–ˆ  β–€β–„ β–ˆβ–€  β–€β–„β–„β–€β–„β–ˆβ–€ β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–„ β–ˆβ–€ β–€β–„β–€β–ˆβ–€β–ˆβ–„  β–„β–ˆβ–€β–„β–€β–€β–€β–€ β–€β–ˆβ–ˆβ–€β–„β–„β–€β–ˆβ–„β–€β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–€β–„  β–ˆβ–€β–„ β–ˆβ–ˆβ–€β–ˆβ–ˆβ–„β–„β–„β–ˆβ–ˆβ–€β–€β–€ β–„β–€β–„ β–„β–ˆβ–ˆβ–€β–€β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–€β–„β–ˆ β–ˆβ–€β–„ β–€β–ˆ β–€β–„β–„β–„β–„ β–„β–€β–€ β–€  β–„β–„β–€β–ˆβ–„ β–€β–ˆ β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–ˆ  β–ˆβ–ˆβ–„β–„β–„β–€β–„β–ˆβ–€β–„β–„  β–„ β–€β–€ β–„β–ˆβ–ˆβ–ˆβ–„  β–ˆ β–„β–€β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–„β–ˆβ–ˆβ–„β–ˆβ–„β–„β–ˆβ–€β–ˆβ–€ β–„ β–„β–ˆβ–ˆβ–„β–ˆ β–€β–€β–„β–€ β–„β–„β–„ β–€β–ˆβ–€β–€β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–„β–„β–„β–„β–„ β–ˆβ–„β–ˆβ–ˆβ–€ β–ˆβ–„β–„β–€β–„ β–€β–„    β–ˆβ–„β–ˆ    β–€β–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–ˆ   β–ˆ β–ˆ β–„β–€β–€β–ˆβ–„β–„β–„  β–€β–€β–€β–€β–„β–ˆ  β–„  β–ˆβ–€β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆ β–ˆβ–„β–„β–„β–ˆ β–ˆ β–„β–„β–€β–„β–ˆβ–„β–„β–„β–„β–€β–ˆ β–€  β–„β–ˆ β–€β–ˆ β–€β–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–„β–„β–„β–„β–„β–„β–„β–ˆβ–„β–„β–ˆβ–ˆβ–ˆβ–„β–„β–ˆβ–ˆβ–„β–ˆβ–„β–ˆβ–ˆβ–ˆβ–„β–„β–ˆβ–ˆβ–„β–„β–ˆβ–ˆβ–„β–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ
β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ

New recovery key enrolled as key slot 0.

kmille@linbox:~ sudo systemd-cryptenroll disk.raw 
SLOT TYPE    
   0 recovery
   1 tpm2

Open the device with TPM, create a file system, mount it

kmille@linbox:~ sudo systemd-cryptsetup attach tpm2-test disk.raw none tpm2-device=auto
πŸ” Please enter LUKS2 token PIN: β€’β€’β€’β€’               

kmille@linbox:~ sudo mkfs.ext4 /dev/mapper/tpm2-test 
mke2fs 1.47.2 (1-Jan-2025)
Creating filesystem with 21504 4k blocks and 21504 inodes

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done


sudo mount /dev/mapper/tpm2-test /mnt 
kmille@linbox:~

kmille@linbox:~ sudo systemd-cryptsetup detach tpm2-test
kmille@linbox:~ 

Open device with recovery key

kmille@linbox:~ sudo cryptsetup open disk.raw tpm2-test
Enter passphrase for disk.raw: 
kmille@linbox:~ sudo cryptsetup close tpm2-test 
kmille@linbox:~ 

See dm-crypt/System configuration#crypttab and dm-crypt/System configuration#Trusted Platform Module and FIDO2 keys in order to unlock the volume at boot time.

Unlock does not work when PCRs mismatch

Extend and thus change PCR 4. The TPM does not unseal the key anymore.

kmille@linbox:~# sudo tpm2_pcrextend 4:sha256=0e33a0c414b1d752930473d5eccf46ddf5bd2333328ed5562ec337b63c08465a

kmille@linbox:~# sudo systemd-cryptsetup attach tpm2-test disk.raw none tpm2-device=auto
πŸ” Please enter LUKS2 token PIN: β€’β€’β€’β€’                    
Failed to unseal secret using TPM2: Operation not permitted
Set cipher aes, mode xts-plain64, key size 512 bits for device disk.raw.

To re-enroll the TPM, use systemd-cryptenroll with --wipe-slot=tpm2 to remove the existing TPM key slot.

Show luks header

kmille@linbox:~ sudo cryptsetup luksDump disk.raw
LUKS header information
Version:        2         
Epoch:          8        
Metadata area:  16384 [bytes]                                       
Keyslots area:  16744448 [bytes]                                    
UUID:           f8cc1e8b-66fc-427e-9da8-04042325af72                
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:      pbkdf2
        Hash:       sha512
        Iterations: 1000
        Salt:       ac aa 7d 68 c4 cf cb 2d 70 da 3b de 9d 13 7c 32 
                    86 d4 ef 51 16 3e 8e ca 41 34 7e 2e 83 6c e9 9a 
        AF stripes: 4000
        AF hash:    sha512
        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:      pbkdf2
        Hash:       sha512
        Iterations: 1000
        Salt:       88 a0 d0 47 52 03 47 0a a7 39 7a e8 90 b6 25 b5 
                    96 3b b1 de f0 11 1e 35 93 21 8c 37 f1 2d 96 a7 
        AF stripes: 4000
        AF hash:    sha512
        Area offset:290816 [bytes]
        Area length:258048 [bytes]
        Digest ID:  0
Tokens:
  0: systemd-tpm2
        tpm2-hash-pcrs:   1+2+3+4
        tpm2-pcr-bank:    sha256
        tpm2-pubkey:
                    (null)
        tpm2-pubkey-pcrs: 
        tpm2-primary-alg: ecc
        tpm2-pin:         true
        tpm2-pcrlock:     false
        tpm2-salt:        true
        tpm2-srk:         true
        tpm2-pcrlock-nv:  false
        tpm2-policy-hash:
                    aa 37 1c 54 18 7c 54 61 3e 1f f3 41 1f 20 a7 e7
                    3d db 17 ec a5 ed ca 0c 00 91 af 6d 98 a5 f3 7d
        tpm2-blob:        00 9e 00 20 b2 72 5d c0 e1 48 3b 8f e2 0f 13 9b
                    53 05 f5 79 bf 8d 55 c8 63 9a 35 2f 27 c9 ed 68
                    93 8f 5a ce 00 10 b3 86 85 12 3b cd a7 ef 97 5e
                    e9 9a 44 b0 17 96 2a d6 49 dd 5a 9d 6d 6c 90 fa
                    24 2f 40 18 62 a4 89 aa ec be a6 ec 52 c2 24 d9
                    8b 22 7b 6d 96 d0 e4 91 52 77 34 9e 74 a9 71 cb
                    73 db 8e 7e 7a a0 44 60 a6 94 1b a4 5c 72 17 f5
                    85 d5 1c 23 7f ed 07 01 04 39 37 43 c5 53 b5 c6
                    d4 eb 6c 3e 00 57 d0 37 52 e2 ff a7 90 2f 69 f4
                    ff 82 9e 3f d1 16 61 85 26 34 37 b1 c6 f6 a0 dd
                    00 4e 00 08 00 0b 00 00 00 12 00 20 aa 37 1c 54
                    18 7c 54 61 3e 1f f3 41 1f 20 a7 e7 3d db 17 ec
                    a5 ed ca 0c 00 91 af 6d 98 a5 f3 7d 00 10 00 20
                    22 ce 78 91 3a 57 45 2f 11 79 72 24 32 47 20 54
                    2e 4f 00 b6 ba 44 fe e6 d1 cf 07 8c e1 49 2d 78
        Keyslot:    1
  1: systemd-recovery
        Keyslot:    0
Digests:
  0: pbkdf2
        Hash:       sha256
        Iterations: 89775
        Salt:       44 c8 01 e3 76 ef e1 9e f7 ce df a9 eb 66 a6 7c 
                    65 58 83 1e 1f d4 94 56 3b d4 b2 62 3b e9 5c 13 
        Digest:     07 9d e3 44 0e 94 8b 8a 39 70 48 5f 9e 3b 00 f0 
                    4d d6 15 3e 26 3c 5f 88 1d 04 96 51 71 2f 8a 7f 

Encrypt data with TPM

kmille@linbox:~ sudo clevis encrypt tpm2 '{"pcr_ids":"4,7"}' <<< 'hello, world' > data.enc
kmille@linbox:~ cat data.enc | base64 -d | jq
base64: invalid input
{
  "alg": "dir",
  "clevis": {
    "pin": "tpm2",
    "tpm2": {
      "hash": "sha256",
      "jwk_priv": "AM4AIFgjlp67Tzinzb4102kdiFijWPZgBDxAiWU_hh1nykK7ABBcZ6bN5XIQABK5KAfnWNTcA2QMsYi6FOmAsKnv1pdfKG9TOdhOJZjuyT7dt-hU_WVwwxkCPjdsAGiAet6O_gEcoM9JDUsy06JEMd-qcssOJwhOqdHjuic0Kcq0F6c2VnZ9N2t8YUyFqJkCvUxakkopQy0ComDImgXV1YRZPsBZkqRiWpRaG9xH4gVzzz1Ase20Q6nSPADANCw-RCtnOc656Wf-fu7W6BaYOg",
      "jwk_pub": "AE4ACAALAAAEkgAgh3hIYBvJ6HtIVysdzo2qEPTLta4NGyiLluBJnBfXw3oAEAAgztpOb1cDUCqfkZCpW5Yd15mPNWClGdJaVr1Lb4At3Wo",
      "key": "ecc",
      "pcr_bank": "sha1",
      "pcr_ids": "4,7"
    }
  },
  "enc": "A256GCM"
}
kmille@linbox:~ cat data.enc|  sudo clevis decrypt tpm2 '{"pcr_ids":"4,7"}' 
hello, world
@kmille
Copy link
Author

kmille commented Apr 24, 2025

When you want to use PCR 11, use a signed policy, as the kernel gets updated often. PCR 11 hashes the boot phase and the UKI containing kernel, initrd and kernel parameters. First, install systemd-ukify. It then gets used automatically when mkinitcpio is executed.

kmille@linbox: cat uki.conf 
[UKI]
OSRelease=@/etc/os-release
PCRBanks=sha256

[PCRSignature:initrd]
Phases=enter-initrd
PCRPrivateKey=/etc/kernel/pcr-initrd.key.pem
PCRPublicKey=/etc/kernel/pcr-initrd.pub.pem
kmille@linbox: ukify genkey -c /etc/kernel/uki.conf
...

kmille@linbox: ls /etc/kernel/pcr-initrd.*
-rw------- 1 root root 1.7K Apr  9 14:42 /etc/kernel/pcr-initrd.key.pem
-rw-r--r-- 1 root root  451 Apr  9 14:42 /etc/kernel/pcr-initrd.pub.pem
kmille@linbox: sudo mkinitcpio -p linux
==> Building image from preset: /etc/mkinitcpio.d/linux.preset: 'default'
==> Using configuration file: '/etc/mkinitcpio.conf'
...
==> Creating unified kernel image: '/efi/EFI/Linux/arch-linux.efi'
  -> Using ukify to build UKI
  -> Using cmdline file: '/etc/kernel/cmdline'
Using config file: /etc/kernel/uki.conf
Splash image /usr/share/systemd/bootctl/splash-arch.bmp is 566Γ—167 pixels
+ /usr/lib/systemd/systemd-measure sign --osrel=/tmp/mkinitcpio.Z5giRN --cmdline=/tmp/mkinitcpio.ktFNom --uname=/tmp/tmp.unamekpdotn3e --splash=/usr/share/systemd/bootctl/splash-arch.bmp --pcrpkey=/etc/kernel/pcr-initrd.pub.pem --linux=/boot/vmlinuz-linux --initrd=/tmp/mkinitcpio.iu1UeJ --sbat=/tmp/tmp.sbatdqoct7oa --bank=sha256 --private-key=/etc/kernel/pcr-initrd.key.pem --public-key=/etc/kernel/pcr-initrd.pub.pem --phase=enter-initrd
Wrote unsigned /efi/EFI/Linux/arch-linux.efi
==> Unified kernel image generation successful
...
kmille@linbox:~ sudo ukify inspect /efi/EFI/Linux/arch-linux.efi
.sbat:
  size: 326 bytes
  sha256: 09089f4e6c44f00f363ccce9f4bd8af566482be1eb6686fd8ad6bee5465abea8
  text:
    sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
    systemd-stub,1,The systemd Developers,systemd,257,https://systemd.io/
    systemd-stub.arch,1,Arch Linux,systemd,257.5,https://archlinux.org/packages/core/x86_64/systemd/
    uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/
    
.osrel:
  size: 408 bytes
  sha256: 77395add081afa6cd4abacbc77944dda4bb1ebedbae041c8f3e2283a95f3ccc3
  text:
    VERSION_ID=6.14.3-arch1-1
    NAME="Arch Linux"
    PRETTY_NAME="Arch Linux"
    ID=arch
    BUILD_ID=rolling
    ANSI_COLOR="38;2;23;147;209"
    HOME_URL="https://archlinux.org/"
    DOCUMENTATION_URL="https://wiki.archlinux.org/"
    SUPPORT_URL="https://bbs.archlinux.org/"
    BUG_REPORT_URL="https://gitlab.archlinux.org/groups/archlinux/-/issues"
    PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/"
    LOGO=archlinux-logo
.cmdline:
  size: 5 bytes
  sha256: 1bd3612e2cf8c65ab2eb1f3d2eff2c1940279ed95786ac2a33385c3ae48b26d4
  text:
    rw 
    
.uname:
  size: 14 bytes
  sha256: 40dccbf1a571538db1b62afc3113acfc91863f45eb911a3fc16894eeffc08281
  text:
    6.14.3-arch1-1
.splash:
  size: 378226 bytes
  sha256: d0ec3070db1f29799adbc8be8a26341290d2df4bf35939a5adce8e7947bad3bf
.pcrpkey:
  size: 451 bytes
  sha256: 1458de633c10d4d231197303d2c124b0769c0ff5a4ab9a923bd82f48ca16d64c
  text:
    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoQl3cmNMx+b9mjIf0HVu
    MofwJOaWcLbghwaBipghOprLOUb74n4jaWrV+YPCTHX1pXYFt2vCDQwE0j87AFnL
    hSwcSOEcGHGTnqn+8hETMVZDlvonrTDIQxCPqeIggHBiAbe6OJg1g14dm9U4jiLB
    Nu3I2HXrhWpCUv1o8t7vkawPI++2wLIM0KyGgDU0s+eaoNtIYBKuXmLP5KAtvvMl
    yvWSTzmOGs1ez6kN5Y9t3VRtajj1N7R+Vcdyd87k3E8MmIFwr3dc93pQ7YE/D07s
    Q6/TIl3VRZZmkf0uVwPIWq4coc6P4QGFYyXAHoyGcDvvZAAuG5jLFu0WX3o1Eiry
    +wIDAQAB
    -----END PUBLIC KEY-----
.linux:
  size: 15368704 bytes
  sha256: 45def5a3453c6682acf39799a377e1ad6e27cbed39263d6dd40bd990cccf5ea9
.initrd:
  size: 25548170 bytes
  sha256: 8c5efeda193932e132a9c27c17d48625b09b791342f34c7a2051859c162d8403
.pcrsig:
  size: 534 bytes
  sha256: 40c789f54d835962f426977c6cd5eb119251baad28601a3afbed0c3865de6de3
  text:
    {"sha256": [{"pcrs": [11], "pkfp": "c470426df161e6da0383a4268a487a909572990d69aac94882734dcde0bdd22b", "pol": "e248a22a4cb5031d99d57e3f3970eb7b2246cfe1b7fc379b843272686e8022b5", "sig": "ALdEzYi7xKrf+j3mXgCL1Sij4yXBRJvtEJFp3tnnfMx13aATgjWBYdO8166UE19vUfOeffiEdYwg9OgqMQZdHhYJXNCt6ADauejYjeYYNv2lTeeJBN2Qt6sz7wcLvEuLGQmldMgHILNRtriutggKJs43Ux971y5CMP7ONZPqWrpAHGoA4lHiG3FWGfK4r76y6tzR2bL71hDRxZZweZ72jEiLu9SM/IB/8wBzVVDeUsRCRi7DxEZRgSq2lG7e/pZInFD5HcWhBIdJ5gg9IKvBNGveBR/tK/d+iu7ZGV/kcSdZrdz358XjZFcKVGilU4ya4qB6UFQN6zps+i5RpaZBbg=="}]}
kmille@linbox: sudo lsinitcpio /efi/EFI/Linux/arch-linux.efi                                                          
bin                                                                                                                    
early_cpio                                                                                                             
lib                                                                                                                    
lib64                                                                                                                  
sbin                                                                                                                   
usr/                                                                                                                   
usr/lib/                                                                                                               
usr/lib/firmware/                                                                                                      
usr/lib/firmware/amd/                                                                                                  
usr/lib/firmware/amd/amd_sev_fam17h_model0xh.sbin.zst         
...

@kmille
Copy link
Author

kmille commented May 28, 2025

TPM lockout configuration

These are the default values I have on my device:

kmille@linbox:~# sudo tpm2_getcap properties-variable | grep -e LOCKOUT -e AUTH | sort
TPM2_PT_LOCKOUT_COUNTER: 0x0
TPM2_PT_LOCKOUT_INTERVAL: 0x258
TPM2_PT_LOCKOUT_RECOVERY: 0x15180
TPM2_PT_MAX_AUTH_FAIL: 0x1F

Explanation of the values (source):

TPM2_PT_LOCKOUT_COUNTER    the current value of the lockout counter failedTries (number of failed attempts)
TPM2_PT_LOCKOUT_INTERVAL   the number of seconds before the value reported by TPM2_PT_LOCKOUT_COUNTER is decremented
TPM2_PT_LOCKOUT_RECOVERY   the number of seconds after a lockoutAuth failure before use of lockoutAuth may be attempted again
TPM2_PT_MAX_AUTH_FAIL      the number of authorization failures before DA lockout is invoked (failed attempts before lockdown)

You can use tpm2_dictionarylockout (man page) to change these values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment