Use systemd-cryptenroll with FIDO U2F or TPM2 to decrypt your disk

Photo by Bruno Brito on Unsplash

Fedora Workstation includes systemd-cryptenroll by default which makes adding alternative methods for unlocking LUKS partitions fairly straight forward. This article shows how to use either a TPM2 chip or a FIDO U2F security key as an alternative factor to the passphrase when unlocking your LUKS partitions.

Note: systemd-cryptenroll does not work with LUKS1. LUKS1 volumes must be upgraded to LUKS2 to work with systemd-cryptenroll. Fedora Linux has used LUKS2 by default since release 30, but users who are using LUKS volumes that were created on older Fedora Linux releases will need to upgrade their LUKS volumes. References: documentation, discussion

Previous articles

A TPM2 chip is a little piece of storage with secure APIs where you can store secrets protected by Secure Boot. Secure Boot establishes a chain of trust by computing hashes based on, for example, hardware or software components. This way you can store a LUKS decryption key which is only accessible if the system is in a non-tampered state (in theory). Unfortunately, this means you’ll want to measure things like your initramfs and kernel into this state which means invalidating this factor every time you do a system upgrade. FIDO U2F keys do not suffer from this problem as they are not tied to the hardware platform.

Check out my previous article about using an integrated TPM2 secure storage device to learn more in-depth specifics about how TPM2-based unlocking works and its security implications.

The previous article, however, uses clevis which adds additional dependencies and has a more complex interface than using the already present systemd-cryptenroll.

A FIDO2 or FIDO U2F compliant key is an external storage device with secure APIs for storing and retrieving secrets. These keys can be used as a second- or sole-factor in authentication flows. Secrets never leave the device and verification is done on the client. So attack scenarios like fishing are mitigated by design as compared to other MFA (multi-factor authentication) technologies like TOTP (time-based one time passwords).

A previous post about FIDO U2F / FIDO2 keys here on Fedora Magazine showed how to set up those keys for Linux PAM authentication – primarily sudo and GNOME login.

Find your encrypted LUKS disks

For the following sections you need the filesystem path(s) to your LUKS encrypted partition(s). Use lsblk to find them.

$> lsblk
NAME                  MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
sda                     8:0    1     0B  0 disk
sdb                     8:16   1     0B  0 disk
zram0                 252:0    0     8G  0 disk  [SWAP]
nvme0n1               259:0    0 476.9G  0 disk
├─nvme0n1p1           259:1    0   600M  0 part  /boot/efi
├─nvme0n1p2           259:2    0     1G  0 part  /boot
└─nvme0n1p3           259:3    0 475.4G  0 part
  └─luks-fdb98c38-... 253:0    0 475.3G  0 crypt /home
                                                 /

Find the partition number(s) hosting the luks- partition of type crypt. In this case that’d be /dev/nvme0n1p3. Use this path as target for the following sections.

(Maybe) Get rid of clevis

Assuming you followed the previous post on using TPM2 you might want to unbind and remove clevis before proceeding with systemd-cryptenroll. Otherwise just skip this section.

First, remove any TPM2 binding from the LUKS secrets slots. Beware: do not remove slot 0 as it contains the passphrase binding!

$ sudo clevis luks list -d /dev/nvme0n1p3
... Slot 1 (or whichever is your TPM2 binding) ...
$ sudo clevis luks unbind -d /dev/nvme0n1p3 -s 1

Now remove the clevis packages.

sudo dnf remove -y clevis clevis-luks clevis-dracut clevis-udisks2 clevis-systemd

Choose TPM2 or FIDO as an alternative decryption method

The following steps are required for both methods. Choose one to your liking.

  • Add the corresponding dracut module so support is available in the initramfs at boot
  • Enroll / bind a LUKS secret slot tied to either the TPM2 or the FIDO key
  • Update /etc/crypttab with the new configuration
  • Rebuild the initramfs to apply the changes

It is important to run dracut last to not only include new dependencies but also your updated crypttab in the initramfs.

Use systemd-cryptenroll with a FIDO U2F key

Add the fido2 dracut module to your dracut configuration.

$ echo "add_dracutmodules+=\" fido2 \"" | sudo tee /etc/dracut.conf.d/fido2.conf
add_dracutmodules+=" fido2 "

Now enroll the FIDO key with your LUKS partition as an alternative decryption factor. See systemd-cryptenroll(1) for options to control features like touch or pin prompt. By default presence and pin are requested for enrollment and use.

sudo systemd-cryptenroll --fido2-device auto /dev/nvmen1p3

Update /etc/crypttab and append fido2-device=auto to the appropriate line’s options. A line in crypttab consists of four fields with the last one being a comma separated list.

Finally, rebuild your initramfs using dracut. The following command will rebuild your currently-booted initramfs slot.

sudo dracut -f

Don’t worry about the FIDO key not working or about losing it since the passphrase is still available as a fallback.

You’ll now be prompted at boot to enter the PIN of your FIDO key. Be aware that the PIN entry prompt looks exactly the same as the passphrase prompt. You will notice a difference only when using the terminal (which you can view by hitting the ESC key). After entering the correct PIN the hardware token will prompt you to touch it which (also) is not indicated on the prompt. If it does not prompt for a touch, then systemd-cryptenroll was not able to find a hardware token corresponding to the entered PIN.

Note: systemd-cryptenroll does not currently work with multiple connected hardware tokens.

Use systemd-cryptenroll with a TPM2 chip

Add the tpm2-tss module to your dracut configuration.

$ echo "add_dracutmodules+=\" tpm2-tss \"" | sudo tee /etc/dracut.conf.d/tpm2.conf
add_dracutmodules+=" tpm2-tss "

Enroll the TPM2 chip as alternative decryption factor for your LUKS partition(s). The ‐‐wipe-slot tpm2 option ensures that after successful enrollment any previous bindings are removed. Use this command every time you need to update the binding.

sudo systemd-cryptenroll --wipe-slot tpm2 --tpm2-device auto --tpm2-pcrs "0+1+2+3+4+5+7+9" /dev/nvme0n1p3

Update /etc/crypttab and append tpm2-device=auto,tpm2-pcrs=0+1+2+3+4+5+7+9 to the appropriate line’s options, depending on the PCRs used. A line in crypttab consists of four fields with the last one being a plus separated list.

Last but not least, rebuild your initramfs using dracut. The following command will rebuild your currently booted initramfs slot.

sudo dracut -f
FAQs and Guides

50 Comments

  1. Dave

    I hope these modules make it into the default initramfs one day, as I don’t see any reason not to ship them in the default initramfs image.

    This would make the whole configuration easier.

    • Good point! Especially on rpm-ostree, where using overlays to get e.g. the tpm2-tools package requires ostree to re-apply the overlay on every update and you also need to enable initramfs generation manually. For Fedora Workstation it is manageable I think, but would be convenient if use of systemd-cryptenroll were the only step to do

  2. Akhila

    It would be great to do this also on Silverblue, is there any way?

    • Dave

      I used it on silverblue, you have to enable rebuilding of initramfs and overlay the tpm2-tools package, iirc

      • Akhila

        Thanks Dave, Searching for enablung rebuilding of initramfs has put me in the right direction. Now I’ve successfully avoided painfully typing my LUKS password everytime I reboot. I wrote a little guide to myself and I will put it below. To all readers, please be noted that I’m a noob so kindly correct me if possible.
        #tpm2 tools are already present in Fedora Silverblue 38 and above
        #enable initramfs regeneration
        su
        rpm-ostree initramfs –enable

        #Enroll the TPM2 chip as alternative decryption factor, avoided PCR 9 and others as they give troubles
        systemd-cryptenroll –wipe-slot tpm2 –tpm2-device auto –tpm2-pcrs “0+1+4+5+7” /dev/nvme0n1p3

        #open crypttab
        nano /etc/crypttab

        #in the appropriate line’s options, delete “discard” and append to crypttab
        tpm2-device=auto,tpm2-pcrs=0+1+4+5+7

        #regenerate initramfs
        rpm-ostree initramfs-etc –force-sync

    • I at least verified that the fido2 configuration of dracut results in the fido2 module being available in the initramfs built by dracut. If you happen to test this, feel free to share your findings here 🙂

    • joka63

      There’s also a German article describing how to setup Silverblue for automatic decryption with FIDO2 devices: https://curius.de/2023/04/fedora-silverblue-kinoite-mit-fido2-entsperren/

      Setting up automatic decryption with TPM2 chip on Silverblue 38 has become surprisingly easy (compared to the Clevis approach), for example (with /dev/vda3 being the LUKS2 encrypted system partition):

      systemd-cryptenroll –tpm2-device=auto –tpm2-pcrs=7 /dev/vda3
      rpm-ostree kargs –append=rd.luks.options=tpm2-device=auto
      rpm-ostree initramfs –enable –arg=-a –arg=systemd-pcrphase

      and then reboot.

      I was surprised that it is no more necessary to manually edit the /etc/crypttab file (to add TPM2 or FIDO2 specific options), but it works.

      Note however, the system partition (in my example /dev/vda3) must be LUKS2 encrypted. In contrast to Clevis, systemd-cryptenroll requires LUKS2! If the system partition is LUKS1 encrypted (which was standard in the Anaconda installer), you have to convert it to LUKS2 at first.

      • Dave

        I was surprised that it is no more necessary to manually edit the /etc/crypttab file (to add TPM2 or FIDO2 specific options), but it works.

        Yeah, that’s, because you already specified them as a kernel argument – line 2 in your example – so there is no need for a change on the crypttab. On the other hand, the kargs may be overritten by an grub update, so i think, manipulating the crypttab file is safer than using kernel args, isn’t it?

        • joka63

          On Silverblue (and other rpm-ostree based systems), grub is part of an deployed image. So grub or grub.cfg can’t be overwritten accidentally by an independent grub update.
          The “rpm-ostree kargs” command modifies the kernel arguments in /boot/loader/grub.cfg for the next deployment. Unless rpm-ostree gets broken this should be safe enough IMHO.

          Since /boot is not encrypted, its contents (grub.cfg or initramfs) could be manipulated by an evil maid attack.
          According to https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/ this could be detected by measuring PCR7+8+9. But then one had to re-enroll the LUKS keys after each update (not only kernel updates) and then reboot again.

  3. Rodrigo

    Oh yeah, this was incredible easy. Just enrolled my Yubikey 5 with PIN+Touch.

    The only diference that on my system the root partition is actually

    /dev/nvme0n1p3

    , so please be aware to point to the right partition.

  4. Kai

    Thanks for making this great guide! I had a bit of trouble with the TPM2 setup; turns out that the tpm2-pcrs option in /etc/crypttab should have the numbers plus-separated, not comma-separated, and also that you don’t need it because systemd-cryptenroll puts the list of PCRs in the LUKS header. Hope that helps anyone else with the same issue!

  5. Nathanaël Renaud

    Hello everyone,
    I am using two FIDO keys for unlocking my drives and since I had a primary FIDO key and a backup one, I thought that it would not matter to have another unlocking method for my drives, like a password or a recuperation code… Big mistake, becauseit will definitely work, but if later you wish to add another method, like let say that your primary key dies, you will not be able to register a new method because the tool to do so are designed to work with password for new registration.

    The workaround I found was do dump the luks header with a third method like a password, then drop the third method. If later you need to change the mothods you can restore the header with the password and proceed…

    Maybe this is not a problem anymore because I haven’t tried to add a method since months, or maybe there was already a way to add methods but I never found it…

    • As a side-note: I don’t think it adds anything for securing your disk to remove the password slot all together. With a fido key as unlocking method you can choose a long and secure decryption passphrase, stored in a password manager and only ever use it to rebind your fido key(s) or tpm configuration or as a backup if you loose access through the other methods.

      Is there any particular reason why you removed the passphrase slot?

      • Be

        Passwords can be broken with brute force.

        To the contrary, I don’t think it adds anything for security to add a FIDO key as an option if a password can still be used without the FIDO key. Security is only as strong as the weakest link.

  6. Can you talk more about FIDO U2F or TPM2?

    • What would you like to know? If you want to know more about those technologies in general, check out my other articles on the magazine. I’ve given more in-depth overview in the linked articles at the beginning of this article 🙂

  7. Jens

    Hello everyone,

    I am using TPM2 (PCRs 0+1+2+4+7) to secure the luks key and it works fine unless I update the kernel.
    Everytime ‘dnf update’ installs a new kernel/initramfs/grub-entry and I reboot the host to boot the new kernel, I have to enter the passphrase again to unlock the disk.
    I thougth, unless I use PCR 9, the update of the initramfs should not lock the secret.

    Is there a chance/method to avoid the need of manual unlocking with the passphrase after ‘dnf update’, because most of my hosts are remote and headless.

  8. Kelsar

    I wish it was possible to use TPM and FIDO as combination, since they verify different things.

  9. Francois

    I tried this two times now, but I can’t get it working.

  10. Anastasia

    Hello, this configuration is not working for me, only enrolling 0+1+7+9 seems to work for me. How can I fix this to use all the PCRS of my TPM2?

    • Dave

      It may depend on your board, some pcrs could change at every boot. My setup utilizes secure boot enabled and using the default PCRs by not specifying them at all, so you don’t have to worry about updates.

  11. joka63

    IMHO it is not a good idea to arbitrarily measure as many PCRS as possible. In my experience, I had to enter the LUKS password many times for unknown reason if I use the combination 1+4+5+7 as recommended in https://fedoramagazine.org/automatically-decrypt-your-disk-using-tpm2/. “PCR 1 changes on basic hardware/CPU/RAM replacements”. But I had to enter the LUKS password even after connecting the PC to a different monitor.

    When measuring only PCR7, the system may become vulnerable to “evil maid” attacks. But I believe it is still difficult though not impossible to exploit, especially if booting from USB devices is disabled, and editing GRUB and UEFI config is protected by a password.

    In (a far?) future, when unified kernel images will be supported for Fedora Silverblue, it should be sufficient to measure, 7+11 or 7+11+12+15 according to Lennart Poettering in https://0pointer.de/blog/brave-new-trusted-boot-world.html

  12. Nikos

    sudo systemd-cryptenroll –fido2-device auto /dev/sda3
    gjves an error.
    My sda3 is luks1 volume and not working wiht systemd-cryptenroll

    • Thanks for pointing this out Nikos! A note that users must upgrade to LUKS2 before using systemd-cryptenroll has been added to the article.

  13. Hello. This may be a lame question but does this work with Secure Boot disabled? Unfortunetely my lemp10 cannot enable Secure Boot 🙁

    • joka63

      Unlocking with a FIDO compliant key should work.
      Unlocking with TPM2 requires Secure Boot enabled.

      • Dave

        That’s not true. TPM2 works without Secureboot, but it’s a bad idea if you’re only checking against PCR 8.

        • joka63

          I admit. You are right: measuring PCR8 would work with and without SecureBoot enabled. I have tested it on a KVM machine with emulated TPM2 chip . Only measuring PCR7 checks if SecureBoot is enabled.

          • Dave

            Hahaha sorry for this Typo. Of course, PCR7 is the correct one

  14. Anastasia

    Everything is working perfectly, but if I add the pcr 9 or 8+9 does not work, but if I add only 8 it’s working. It’s very confusing because other guides encourage the use of pcr 9.
    Should I be worried for not be using pcr9?
    (I tried this guide on fedora and Opensuse tumbleweed)

  15. Felicia G

    This configuration works with PCR=0+1+2+3+4+5+6+7+8 but not if I add +9
    Why is the reason?
    It’s bad not using 9?

  16. Rodehoed

    Hi,

    This looks nice! And I’m new to FIDO keys. But I was wondering let’s say if I own 5 pc’s with 5 different users. But the users should be able to access all pc’s. Could I add one master key ? Are do I have to add all keys from all the FIDO keys then?

  17. Parvati

    Hi,
    Just to confirm – the physical key (Yubikey) is only needed during boot. It can be then removed, since the LUKS key is stored in memory?

  18. G

    Could these directions be implemented on F32 Dom0 in Qubes OS in theory?

    https://forum.qubes-os.org/t/yubikey-luks-on-qubes-4-1/5373/27?u=procshield

  19. Luca

    I followed the instructions but it does not work for me. I reinstalled my Fedora operating system (KDE spin) and it still doesn’t work. I still have to enter my password when booting. Can you please make a video tutorial on this? I am at a loss as to what to do.

  20. Jimmy

    Thanks! Would love if you could do a related article on PIV/CAC cards with luks.

  21. Jimmy

    Btw with a Yubico 5 I’m able to see and enroll it as a pkcs#11 device with systemd-cryptenroll, but upon boot it doesn’t prompt for anything — which makes me think there’s a needed dracut module missing?

    For a Taglio PIVKey card unfortunately systemd-cryptenroll doesn’t seem to recognize it at all — I just get “No suitable PKCS#11 tokens found.”

  22. Claudio Chinicz

    Hi,

    Thanks for the article, I’ve already registered 2 Yubikeys so that I can open my LUKS with the keys’ PIN and the physical key.

    Is there a way to “disable” opening LUKS with password? being able to register Yubikeys is a lot more secure than relying on password IMHO but leaving the door open through password defeats the whole purpose of using security keys. I do not mean that disabling password after registering security key(s) should be the default but an option since people may fear losing their keys.

    Cheers

    • Dave

      Hi,

      you may want to remove it with this command:
      sudo systemd-cryptenroll /dev/nvme0n1p3 –wipe-slot password

      where /dev/nvme0n1p3 is your luks partition.

      However, you should test this on a test-system first, as you may not be able to set another slot without a password. You can set a new password slot with this command:
      sudo cryptsetup luksAddKey –token-id X –token-type systemd-fido2 /dev/nvme0n1p3

      where X is the correct token. Use cryptsetup luksDump to display your currently installed tokens.

      if this works, i think it’s safe to remove all passwords and recovery keys, if you have a minimum of two FIDO2-keys.

      • Claudio

        Hey Dave,

        Thanks for the detailed explanation.

        I can delete the password effectively but cannot create a new one of enroll a new device after having removed the password.

        The command “sudo cryptsetup luksAddKey -token-id X -token-type systemd-fido2 /dev/sda3” gets this error message “cryptsetup: invalid numeric value” for any value of X I’ve tried (0,1,2,3).

        The output of “sudo cryptsetup luksDump /dev/sda3” is:

        LUKS header information
        Version: 2
        Epoch: 12
        Metadata area: 16384 [bytes]
        Keyslots area: 16744448 [bytes]
        UUID: XXXXXXXXXXXXXXXXXXXXXXXXX
        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:
        1: luks2
        Key: 512 bits
        Priority: normal
        Cipher: aes-xts-plain64
        Cipher key: 512 bits
        PBKDF: pbkdf2
        Hash: sha512
        Iterations: 1000
        Salt: 04 19 64 9b d2 94 1c ba 6f da 4d 83 54 2d 47 80
        a1 7b bf 59 d1 19 a6 82 d6 ca b9 d6 b2 7c 6e 28
        AF stripes: 4000
        AF hash: sha512
        Area offset:290816 [bytes]
        Area length:258048 [bytes]
        Digest ID: 0
        2: luks2
        Key: 512 bits
        Priority: normal
        Cipher: aes-xts-plain64
        Cipher key: 512 bits
        PBKDF: pbkdf2
        Hash: sha512
        Iterations: 1000
        Salt: be c8 6c 9d a9 b0 d1 df d5 d5 45 21 e1 10 eb d8
        41 1e c7 03 1e 69 68 25 af c6 34 a9 32 21 d0 9f
        AF stripes: 4000
        AF hash: sha512
        Area offset:548864 [bytes]
        Area length:258048 [bytes]
        Digest ID: 0
        3: luks2
        Key: 512 bits
        Priority: normal
        Cipher: aes-xts-plain64
        Cipher key: 512 bits
        PBKDF: pbkdf2
        Hash: sha512
        Iterations: 1000
        Salt: 19 2b 0e ef a6 2d 74 f2 a2 3e 68 ef 31 e9 9a e1
        dc 14 75 21 2f a7 53 3d c5 06 1c a2 85 aa f2 b0
        AF stripes: 4000
        AF hash: sha512
        Area offset:806912 [bytes]
        Area length:258048 [bytes]
        Digest ID: 0
        Tokens:
        0: systemd-fido2
        fido2-credential:
        XXXXXXXXXXXXXXXXXXXXXXXXXXX
        fido2-salt: 58 f2 2e 48 07 d2 60 ba ef 13 df 29 09 47 60 be
        a8 0a c4 5b 6c f4 e7 16 39 1e 1a b3 55 9e 78 5a
        fido2-rp: io.systemd.cryptsetup
        fido2-clientPin-required:
        true
        fido2-up-required:
        true
        fido2-uv-required:
        false
        Keyslot: 1
        1: systemd-fido2
        fido2-credential:
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx
        fido2-salt: 61 ab 06 33 51 78 99 ce 6b 48 39 e1 a9 78 35 e7
        46 27 34 ea 59 97 e1 7b b5 87 9f 61 19 ba 2f 55
        fido2-rp: io.systemd.cryptsetup
        fido2-clientPin-required:
        true
        fido2-up-required:
        true
        fido2-uv-required:
        false
        Keyslot: 2
        2: systemd-fido2
        fido2-credential:
        XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        fido2-salt: a4 97 7a 05 ba 5f 5b 66 35 60 de 8c 7e 12 12 2a
        bd 6b 9f ee 4f a8 58 74 14 bb e0 a4 97 c2 e9 7a
        fido2-rp: io.systemd.cryptsetup
        fido2-clientPin-required:
        true
        fido2-up-required:
        true
        fido2-uv-required:
        false
        Keyslot: 3
        Digests:
        0: pbkdf2
        Hash: sha256
        Iterations: 113975
        Salt: 53 ef 32 63 33 ae b8 4e 75 44 ff ce 33 22 87 dc
        4e b7 6d b2 4b 4b 9a d8 ef 4a 7c 1d c6 d2 4f b5
        Digest: 9e a1 cb b2 63 d4 7a 67 83 08 80 da 98 58 e3 c7
        bd e9 59 a2 04 f9 8e 61 ab 57 56 6f da f5 79 c7

        I do not know why I see 3 tokens (0,1,2) with type “systemd-fido2” since I’ve registered just two Yubikeys.

        I’ve tried also the command “sudo cryptsetup luksAddKey /dev/sda3” (without the parameter “-token-type systemd-fido2”) but that requires entering any existing passphrase and none is enrolled after deleting all passwords.

        The good news is that I can open LUKS with any of the two Yubikeys.. the bad news is that I cannot enroll a new key (is that true?) nor a passphrase.

        Any ideas?

        Regards,
        Claudio

        • Claudio Chinicz

          Hi,

          I’m somewhat disapointed with Yubikeys.

          I’ve installed USBGuard and got locked out because it blocked all existing devices (I had understood that all connected devices at the time of install would be allowed) and I could not issue a SUDO command to allow the devices since I had already closed the terminal and in a new terminal I have to use Yubikey together with password but Yubikeys were blocked.

          Then I thought maybe if I send an email from another computer with some Yubekey codes I could use them in the terminal to issue SUDO commands. I’ve generated some 5 codes by tapping the Yubikeys and, to my surprise, the first one worked and I was able to allow all devices permanently.

          After the initial excitement I started to wonder how secure Yubikeys for this use case..

          I know this is not the subject of this article but may be an issue to consider. If someone has physical access to your Yubikey you’re vulnerable, although you still need a PIN for some applications or a password (my use case here).

          Regards,
          Claudio

  23. It can be then removed, since the LUKS key is stored in memory?

Comments are Closed

The opinions expressed on this website are those of each author, not of the author's employer or of Red Hat. Fedora Magazine aspires to publish all content under a Creative Commons license but may not be able to do so in all cases. You are responsible for ensuring that you have the necessary permission to reuse any work on this site. The Fedora logo is a trademark of Red Hat, Inc. Terms and Conditions