Since writing this, I’ve collected some of the below in a git repository. There you’ll find a key loader and a systemd unit that will make sure it runs before zfs-mount.service. Give the unit file the same name as the generated key loader unit1 and place it in /etc/systemd/system.

Right now, this implementation uses NVRAM to store the key directly. An obvious improvement would be to use sealed data (tpm_sealdata, tpm_unsealdata).


original content from 2020-11-29 follows

I wanted to have an encrypted OpenZFS dataset whose key was stored with my TPM. Using @morbitzer’s guide to doing the same for LUKS, I divined a couple commands that helped me out.

  1. Install (Debian) tpm-tools, zfs-dkms. Make sure TrouserS tcsd is running.
  2. Create a key
    • dd if=/dev/urandom of=/dev/shm/key.bin bs=32 count=1
  3. Load the key into TPM NVRAM
    • Define a slot; here I chose index 1.
      • tpm_nvdefine -i 1 -s 32 -r0 -r1 -r2 -r3 -r4 -r5 -r6 -r7 -p "OWNERWRITE|READ_STCLEAR"
      • This seals the NVRAM index to PCRs 0-7 (the ones measured into by the system firmware)
    • Fill that slot with the key.
      • tpm_nvwrite -i 1 -f /dev/shm/key.bin
  4. Create the dataset
    • zfs create rpool/dataset -o encryption=aes-256-gcm -o keyformat=raw -o keylocation=file:///dev/shm/key.bin
  5. Destroy the key

Loading the key

This is somewhat silly: tpm_nvread has unsuppressable logging to stdout, so we direct its output to /dev/stdout (it doesn’t support -f -) and chop off the first $KEYLENGTH bytes.

-l none seems like it would work to suppress the logging. It does not.

zfs load-key likewise does not support -L - or -L file://-. We’ll just use the /dev/stdin trick again.

tpm_nvread -i 1 -f /dev/stdout |
    dd bs=32 count=1 |
    zfs load-key -L file:///dev/stdin rpool/dataset

Locking the key

Because we set READ_STCLEAR when we defined the NVRAM slot, sending the TPM a read of size 0 will lock the key to prevent future reading.

tpm_nvread -i 1 -s 0

On one of my test systems, I had the problem that the secret stored in the NVRAM could be read even when the PCRs it was sealed to had changed. It took me quite a long time to figure out what went wrong: Apparently, the TPM manufacturer didn’t set the nvLocked bit, which means that reading the NVRAM was always possible, no matter if you sealed it to some PCRs or assigned a password to it. Thanks to this discussion at the TrouSers mailing list, I was finally able to figure out what to do:

tpm_nvdefine -i 0xFFFFFFFF –size=0

@morbitzer’s guide


  1. look in /run/systemd/generator for zfs-load-key*.service ↩︎