SYSTEMD-CRYPTENROLL(1)

๐Ÿ’– NAME ๐Ÿ’–

okay so bestie, welcome to systemd-cryptenroll (it lives in the systemd-cryptenroll corner of the man pages, section 1, and it is iconic) โ€” this is the tool that enrolls PKCS#11, FIDO2, and TPM2 tokens/devices into LUKS2 encrypted volumes. โœจ


๐Ÿฆ‹ SYNOPSIS ๐Ÿฆ‹

systemd-cryptenroll [OPTIONS...] [DEVICE]

๐ŸŒธ DESCRIPTION ๐ŸŒธ

So like, systemd-cryptenroll is the girl whose whole job is enrolling hardware security tokens and devices into a LUKS2 encrypted volume, which you can then use to unlock that volume while booting. no cap, that's the gig. ๐Ÿ’…

Specifically, she supports these kinds of tokens and credentials to be enrolled:

  1. PKCS#11 security tokens and smartcards that may carry an RSA or EC key pair (e.g. various YubiKeys) ๐Ÿ”‘
  2. FIDO2 security tokens that implement the "hmac-secret" extension (most FIDO2 keys, including YubiKeys) ๐Ÿ—๏ธ
  3. TPM2 security devices ๐Ÿงš
  4. Regular passphrases ๐Ÿ’ฌ
  5. Recovery keys. These are similar to regular passphrases, however are randomly generated on the computer and thus generally have higher entropy than user-chosen passphrases. Their character set has been designed to ensure they are easy to type in, while having high entropy. They may also be scanned off screen using QR codes. Recovery keys may be used for unlocking LUKS2 volumes wherever passphrases are accepted. They are intended to be used in combination with an enrolled hardware security token, as a recovery option when the token is lost. ๐Ÿ˜ญ๐Ÿ”

Plus โ€” plot twist โ€” the tool may also be used to enumerate currently enrolled security tokens and wipe a subset of them. And the wipe can be combined with the enrollment operation of a new security token, so you can update or replace enrollments in one go. ate that. ๐Ÿ‘‘

IMPORTANT: this tool supports only LUKS2 volumes, because it stores token meta-information in the LUKS2 JSON token area, which is not available in other encryption formats. so don't come at her with LUKS1, she will not. ๐Ÿ’€

Also, systemd-cryptsetup operates on the device backing /var/ if no device is specified explicitly, and no wipe operation is requested. (Note that in the typical case where /var/ is on the same file system as the root file system, this hence enrolls a key into the backing device of the root file system.) ๐Ÿซถ


๐ŸŒˆ TPM2 PCRs and policies ๐ŸŒˆ

PCRs allow binding of the encryption of secrets to specific software versions and system state, so that the enrolled key is only accessible (may be "unsealed") if specific trusted software and/or configuration is used. Such bindings may be created with the option --tpm2-pcrs= described below. โœจ

Secrets may also be bound indirectly: a signed policy for a state of some combination of PCR values is provided, and the secret is bound to the public part of the key used to sign this policy. This means that the owner of a key can generate a sequence of signed policies, for specific software versions and system states, and the secret can be decrypted as long as the machine state matches one of those policies. For example, a vendor may provide such a policy for each kernel+initrd update, allowing users to encrypt secrets so that they can be decrypted when running any kernel+initrd signed by the vendor. Such bindings may be created with the options --tpm2-public-key=, --tpm2-public-key-pcrs=, --tpm2-signature= described below. ๐Ÿ”

See UAPI.7 Linux TPM PCR Registry[1] for an authoritative list of PCRs and how they are updated. The table below contains a quick reference, describing in particular the PCRs modified by systemd. ๐Ÿ“‹

๐ŸŒˆ Table 1. Well-known PCR Definitions ๐ŸŒˆ

okay so here's the tea on which PCR does what, bestie โ€” bookmark this, it's giving cheat sheet ๐Ÿ’…

  • 0 โ€” platform-code: core system firmware executable code; changes on firmware updates โœจ
  • 1 โ€” platform-config: core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements
  • 2 โ€” external-code: extended or pluggable executable code; includes option ROMs on pluggable hardware
  • 3 โ€” external-config: extended or pluggable firmware data; includes information about pluggable hardware
  • 4 โ€” boot-loader-code: boot loader and additional drivers, PE binaries invoked by the boot loader; changes on boot loader updates. sd-stub(7) measures system extension images read from the ESP here too (see systemd-sysext(8)). ๐Ÿฆ‹
  • 5 โ€” boot-loader-config: GPT/Partition table; changes when the partitions are added, modified, or removed
  • 7 โ€” secure-boot-policy: Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, ...) changes. ๐Ÿ”
  • 9 โ€” kernel-initrd: the Linux kernel measures all initrds it receives into this PCR.
  • 10 โ€” ima: the IMA project measures its runtime state into this PCR.
  • 11 โ€” kernel-boot: systemd-stub(7) measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. systemd-pcrphase.service(8) measures boot phase strings into this PCR at various milestones of the boot process. ๐Ÿ‘‘
  • 12 โ€” kernel-config: systemd-boot(7) measures the kernel command line into this PCR. systemd-stub(7) measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR.
  • 13 โ€” sysexts: systemd-stub(7) measures any systemd-sysext(8) images it passes to the booted kernel into this PCR.
  • 14 โ€” shim-policy: the shim project measures its "MOK" certificates and hashes into this PCR. ๐ŸŽ€
  • 15 โ€” system-identity: systemd-cryptsetup(8) optionally measures the volume key of activated LUKS volumes into this PCR. systemd-pcrmachine.service(8) measures the machine-id(5) into this PCR. systemd-pcrfs@.service(8) measures mount points, file system UUIDs, labels, partition UUIDs of the root and /var/ filesystems into this PCR.
  • 16 โ€” debug: Debug
  • 23 โ€” application-support: Application Support

so, like, in general, encrypted volumes would be bound to some combination of PCRs 7, 11, and 14 (if shim/MOK is used). and the way that updates work?? ๐Ÿ‘€ in order to allow firmware and OS version updates, it is typically not advisable to use PCRs such as 0 and 2, since the program code they cover should already be covered indirectly through the certificates measured into PCR 7. validation through certificates hashes is typically preferable over validation through direct measurements as it is less brittle in context of OS/firmware updates: the measurements will change on every update, but signatures should remain unchanged. iykyk ๐Ÿ’…

fr fr tho, go read the UAPI.7 Linux TPM PCR Registry[1] for more discussion โ€” it's literally the deep dive you want. ๐Ÿซถ

๐Ÿ’€ LIMITATIONS (the fine print bestie, read it fr fr)

Okay so first thing's first, and I need you to hear me on this one: right now, when you enroll a new key of one of the five supported types listed above, you have to first provide a passphrase, a recovery key, a FIDO2 token, or a TPM2 key. That's the vibe. No getting around it.

And here's the kicker โ€” it's currently not supported to unlock a device with a PKCS#11 key in order to enroll a new PKCS#11 key. Like, the door won't open with the very key you're trying to duplicate. So if you're thinking about future key roll-over (which, lowkey, you should be), the general recommendation is to make sure a passphrase, a recovery key, a FIDO2 token, or a TPM2 key is always enrolled. Keep one of those in your back pocket, always. ๐Ÿซถ

Also, heads up: support for enrolling multiple FIDO2 tokens is currently limited. When multiple FIDO2 tokens are enrolled, systemd-cryptsetup will perform pre-flight requests to attempt to identify which of the enrolled tokens are currently plugged in. Cute, right? But it's not possible for FIDO2 tokens with user verification (UV, usually via biometrics), in which case it will fall back to attempting each enrolled token one by one. Which meansโ€ฆ multiple prompts for PIN and user verification. ๐Ÿ˜ญ Yeah. A whole parade of prompts.

Good news though: this limitation does not apply to PKCS#11 tokens. They ate and left no crumbs. ๐Ÿ’…

๐ŸŒˆ COMPATIBILITY (aka the "will my old stuff still work" section)

Security technology both in systemd and in the general industry constantly evolves. Like, constantly. In order to provide best security guarantees, the way TPM2, FIDO2, PKCS#11 devices are enrolled is regularly updated in newer versions of systemd. And whenever this happens, here are the compatibility guarantees you get:

  • Old enrollments continue to be supported and may be unlocked with newer versions of systemd-cryptsetup@.service(8).
  • The opposite is not guaranteed however: it might not be possible to unlock volumes with enrollments done with a newer version of systemd-cryptenroll with an older version of systemd-cryptsetup.

That said, it is generally recommended to use matching versions of systemd-cryptenroll and systemd-cryptsetup, since this is best tested and supported. Matching sets, besties. Matching sets. ๐Ÿ‘‘

It might be advisable to re-enroll existing enrollments to take benefit of newer security features, as they are added to systemd. Glow-up your keys, periodt. โœจ


๐Ÿ”“ UNLOCKING (how we get the volume open before we do the thing)

The following options are understood that may be used to unlock the device in preparation of the enrollment operations:

  • --unlock-empty โ€” Use an empty password/passphrase to unlock the volume, instead of reading one from stdin. This is useful to unlock volumes that are currently protected by an empty password, in order to enroll a different, stronger unlock mechanism. Glow-up arc activated. ๐ŸŒฑ

Added in version 262.

  • *--unlock-key-file=PATH* โ€” Use a file instead of a password/passphrase read from stdin to unlock the volume. Expects the PATH to the file containing your key to unlock the volume. Currently there is nothing like --key-file-offset= or --key-file-size= so this file has to only contain the full key. Like, the whole key, nothing else, no extras. ๐ŸŽ€

Added in version 252.

  • *--unlock-fido2-device=PATH* โ€” Use a FIDO2 device instead of a password/passphrase read from stdin to unlock the volume. Expects a hidraw device referring to the FIDO2 device (e.g. /dev/hidraw1). Alternatively the special value "auto" may be specified, in order to automatically determine the device node of a currently plugged in security token (of which there must be exactly one). This automatic discovery is unsupported if --fido2-device= option is also specified. Note that currently FIDO2 devices enrolled without an accompanying LUKS2 token (i.e. --fido2-parameters-in-header=no) cannot be used for unlocking. ๐Ÿšซ

Added in version 253.

  • *--unlock-tpm2-device=PATH* โ€” Use a TPM2 device instead of a password/passphrase read from stdin to unlock the volume. Expects a device node path referring to the TPM2 chip (e.g. /dev/tpmrm0). Alternatively the special value "auto" may be specified, in order to automatically determine the device node of a currently discovered TPM2 device (of which there must be exactly one). One and only one, bestie, don't make it choose. ๐Ÿ™ˆ

Added in version 256.

  • --unlock-headless โ€” Try the unlock mechanisms suitable for headless operation in turn, without requiring any interactive input. If a TPM2 device is available the volume is unlocked via TPM2, and otherwise (or if TPM2 unlocking fails) an empty password is attempted. This is primarily useful in automated provisioning scenarios where a volume is first set up with a TPM2 enrollment or an empty password and a stronger unlock mechanism shall be enrolled subsequently. Hands-free, no vibes required. ๐Ÿค–

Added in version 262.


๐Ÿ”‘ SIMPLE ENROLLMENT (the classics, the OGs)

The following options are understood that may be used to enroll simple user input based unlocking:

  • --password โ€” Enroll a regular password/passphrase. This command is mostly equivalent to cryptsetup luksAddKey, however may be combined with --wipe-slot= in one call, see below. Two birds, one slay. ๐Ÿฆ

Added in version 248.

  • --recovery-key โ€” Enroll a recovery key. Recovery keys are mostly identical to passphrases, but are computer-generated instead of being chosen by a human, and thus have a guaranteed high entropy. The key uses a character set that is easy to type in, and may be scanned off screen via a QR code. It's giving "written by the algorithm, typed by you." ๐Ÿ“ฑโœจ

Added in version 248.


๐Ÿงš INTERACTIVE ENROLLMENT (the wizard!! it's giving first boot energy)

The following options are understood that drive an interactive enrollment wizard, intended to be run on first boot (similar in spirit to systemd-firstboot(1)):

  • --firstboot โ€” Run an interactive wizard that lets the user enroll a passphrase, a recovery key, or a FIDO2 security token (one menu entry per suitable token currently plugged in). If credentials of any of these types are already enrolled, the wizard additionally offers to wipe them. Pressing enter at the top-level menu leaves the volume unchanged. This option may not be combined with an explicit enrollment type or with --wipe-slot=. As with the other commands, the volume to operate on is the one backing /var/ if none is specified. Honours the systemd.firstboot= kernel command line option.

Added in version 262.

  • *--prompt-suppress=TYPE*... โ€” Takes a comma-separated list of slot types, out of "password", "recovery", "pkcs11", "fido2" and "tpm2". Only useful together with --firstboot: if a slot of any of the listed types is already enrolled on the volume, the wizard does nothing and exits successfully. This is useful to hook the wizard into the boot process while ensuring it stays quiet once suitable credentials have been set up. Silent mode: engaged. ๐Ÿคซ

Added in version 262.

  • *--chrome=BOOL* โ€” Takes a boolean argument, defaults to on. Only useful together with --firstboot: if enabled the interactive wizard draws a coloured bar across the top and bottom of the terminal to visually frame the prompt. Pass --chrome=no to disable this decoration, for example when running on a terminal that does not render it well. Aesthetic framing, immaculate. ๐ŸŽจ

Added in version 262.

  • *--mute-console=BOOL* โ€” Takes a boolean argument, defaults to off. Only useful together with --firstboot: if enabled the kernel and PID 1 are asked to refrain from writing log messages to the console while the interactive wizard is shown, so that such output does not interfere with the prompt. The console is unmuted again once the wizard exits. Shhh, the wizard is speaking. ๐Ÿค

Added in version 262.


๐Ÿ’ณ PKCS#11 ENROLLMENT (smartcard girlies rise up)

The following option is understood that may be used to enroll PKCS#11 tokens:

  • *--pkcs11-token-uri=URI* โ€” Enroll a PKCS#11 security token or smartcard (e.g. a YubiKey). Expects a PKCS#11 URI that allows finding an X.509 certificate or a public key on the token. The URI must also be suitable to find a related private key after changing the type of object in it. Alternatively the special value "auto" may be specified, in order to automatically determine the suitable URI if a single security token containing a single key pair is plugged in. The special value "list" may be used to enumerate all suitable PKCS#11 tokens currently plugged in. Window shopping for tokens, we love to see it. ๐Ÿ›๏ธ

The PKCS#11 token must contain an RSA or EC key pair which will be used to unlock a LUKS2 volume. For RSA, a randomly generated volume key is encrypted with a public key in the token, and stored in the LUKS2 JSON token header area. To unlock a volume, the stored encrypted volume key will be decrypted with a private key in the token. For ECC, ECDH algorithm is used: we generate a pair of EC keys in the same EC group, then derive a shared secret using the generated private key and the public key in the token. The derived shared secret is used as a volume key. The generated public key is stored in the LUKS2 JSON token header area. The generated private key is erased. To unlock a volume, we derive the shared secret with the stored public key and a private key in the token. Crypto math, but make it fashion. ๐Ÿงฎ๐Ÿ’…

In order to unlock a LUKS2 volume with an enrolled PKCS#11 security token, specify the pkcs11-uri= option in the respective /etc/crypttab line:

myvolume /dev/sda1 none pkcs11-uri=auto

See crypttab(5) for a more comprehensive example of a systemd-cryptenroll invocation and its matching /etc/crypttab line.

Added in version 248.

๐Ÿ”‘ FIDO2 ENROLLMENT โœจ

okay bestie so these are the options you're gonna use when you wanna enroll a FIDO2 token. they're giving "tiny key that lives on your keychain but is lowkey a whole security system" ๐Ÿ’…

  • --fido2-device=PATH โ€” this one enrolls a FIDO2 security token that implements the "hmac-secret" extension (hi, YubiKey ๐Ÿ‘‹). You pass it a hidraw device that points at the FIDO2 device, like /dev/hidraw1. OR โ€” and this is the slay part โ€” you can pass the special value "auto" and it'll go find the device node of the currently plugged-in security token all by itself (there has to be exactly one tho, no sharing). ngl this auto-discovery does not work if you also specify --unlock-fido2-device=. There's also a special value "list" that enumerates every suitable FIDO2 token that's currently plugged in, like a little roster. โœจ Note that lots of hardware security tokens that do FIDO2 also do the older PKCS#11 thing โ€” and typically FIDO2 is the move, since it's simpler to use and more modern. periodt.

So to unlock a LUKS2 volume with an enrolled FIDO2 security token, you specify the fido2-device= option in the matching /etc/crypttab line, like:

myvolume /dev/sda1 none fido2-device=auto

Wanna see a more comprehensive example of a systemd-cryptenroll invocation and its matching /etc/crypttab line? That's in crypttab(5). ๐Ÿซถ

Added in version 248.

  • --fido2-credential-algorithm=STRING โ€” picks the COSE algorithm used when generating the credential. Default is "es256". Supported values are "es256", "rs256" and "eddsa". Let's break it down fr fr:
    • "es256" = ECDSA over NIST P-256 with SHA-256.
    • "rs256" = 2048-bit RSA with PKCS#1.5 padding and SHA-256.
    • "eddsa" = EDDSA over Curve25519 with SHA-512.

Heads up tho: your authenticator might just be like "no ๐Ÿ’€" and not support some of these algorithms.

Added in version 251.

  • --fido2-salt-file=PATH โ€” when you're enrolling a FIDO2 security token, this gives the path to a file (or an AF_UNIX socket ๐Ÿ‘€) that we read the salt value from โ€” the salt gets used in the HMAC operation that the FIDO2 security token performs. If you don't specify this option at all, the salt just gets randomly generated for you. cute, effortless.

Added in version 257.

  • --fido2-parameters-in-header=BOOL โ€” when enrolling a FIDO2 security token, this controls whether the FIDO2 parameters get stored in a token in the LUKS2 superblock. Defaults to "yes". If you set it to "no", then you have to specify the fido2-cid= option manually in the respective /etc/crypttab line, along with a key file. Details are in crypttab(5). iykyk.

Added in version 257.

  • --fido2-with-client-pin=BOOL โ€” when enrolling a FIDO2 security token, controls whether the user has to enter a PIN when unlocking the volume (that's the FIDO2 "clientPin" feature). Defaults to "yes". But bestie, note that this setting does literally nothing if the security token doesn't support the "clientPin" feature, or if it doesn't let the client PIN requirement be enabled/disabled for the requested operation. it's giving "the token decides" ๐Ÿ™ˆ

Added in version 249.

  • --fido2-with-user-presence=BOOL โ€” when enrolling a FIDO2 security token, controls whether the user has to verify presence by tapping the token (the FIDO2 "up" feature) when unlocking the volume. Defaults to "yes". Same vibe check as before: this setting has no effect if the security token doesn't support the "up" feature, or doesn't allow the user presence requirement to be enabled or disabled for the requested operation.

Added in version 249.

  • --fido2-with-user-verification=BOOL โ€” when enrolling a FIDO2 security token, controls whether user verification is required when unlocking the volume (the FIDO2 "uv" feature). Defaults to "no". And yep, same disclaimer: no effect if the security token doesn't support the "uv" feature, or doesn't allow the user verification requirement to be enabled or disabled for the requested operation. ๐Ÿคท

Added in version 249.


๐Ÿ›ก๏ธ TPM2 ENROLLMENT ๐Ÿ’–

now for the other main character: TPM2 devices. these options are what you use to enroll them. ngl this section is a whole meal, so hydrate first ๐Ÿ“

  • --tpm2-device=PATH โ€” enrolls a TPM2 security chip. Expects a device node path referring to the TPM2 chip, like /dev/tpmrm0. OR you can pass the special value "auto" and it'll automatically figure out the device node of a currently discovered TPM2 device (and again โ€” there must be exactly one, no polyamory for TPMs). The special value "list" enumerates all suitable TPM2 devices that are currently discovered. ๐ŸŒˆ

So to unlock a LUKS2 volume with an enrolled TPM2 security chip, you specify the tpm2-device= option in the respective /etc/crypttab line:

myvolume /dev/sda1 none tpm2-device=auto

Again, crypttab(5) has a more comprehensive example of a systemd-cryptenroll invocation and its matching /etc/crypttab line. โœจ

And use --tpm2-pcrs= (peep below ๐Ÿ‘‡) to configure which TPM2 PCR indexes the enrollment gets bound to.

Added in version 248.

  • --tpm2-device-key=PATH โ€” enrolls a TPM2 security chip using its public key. Expects a path referring to the TPM2 public key in TPM2B_PUBLIC format. This cannot be used together with --tpm2-device=, because it does the exact same operation โ€” but without connecting to the TPM2 security chip. Instead, the enrollment gets calculated using the TPM2 key you provided. This is highkey useful when the TPM2 security chip just isn't available at the time of enrollment. iconic for that exact scenario. ๐Ÿ’…

The key, in most cases, should be the Storage Root Key (SRK) from a local TPM2 security chip. But if you use a key from a different handle (not the SRK), then you must specify its handle index using --tpm2-seal-key-handle=.

Good news: the systemd-tpm2-setup.service(8) service writes the SRK to /run/systemd/tpm2-srk-public-key.tpm2b_public automatically during boot, in the correct format. so it's just... there. ๐ŸŽ€

Alternatively, you can use systemd-analyze srk to retrieve the SRK from the TPM2 security chip explicitly. See systemd-analyze(1) for details. Example:

systemd-analyze srk > srk.tpm2b_public

Added in version 255.

  • --tpm2-seal-key-handle=HANDLE โ€” configures which parent key to use for sealing, using the TPM handle (index) of the key. This is what you use to "seal" (encrypt) a secret, and it must be used later to "unseal" (decrypt) that secret. Expects a hexadecimal 32bit integer, optionally prefixed with "0x". Allowable values are any handle index in the persistent range ("0x81000000"-"0x81ffffff") or the transient range ("0x80000000"-"0x80ffffff"). โš ๏ธ But listen: since transient handles get lost after a TPM reset, and may be flushed during TPM context switching, they should not be used except for very specific use cases, e.g. testing. don't be brave.

The default is the Storage Root Key (SRK) handle index "0x81000001". A value of 0 will use the default. For the SRK handle, a new key will be created and stored in the TPM if one doesn't already exist; but for any other handle, the key must already exist in the TPM at the specified handle index.

bestie. this should not be changed unless you know what you are doing. ๐Ÿ’€

Added in version 255.

  • --tpm2-pcrs=PCR[+PCR...] โ€” configures the TPM2 PCRs (Platform Configuration Registers) to bind to when enrollment is requested via --tpm2-device=. It takes a list of PCR entries, where each entry starts with a name or a numeric index in the range 0...23, optionally followed by ":" and a hash algorithm name (which specifies the PCR bank), optionally followed by "=" and a hash digest value. Multiple PCR entries get separated by "+". If you specify an empty string, it binds the enrollment to no PCRs at all (and this is also the default if you don't use this option). See the table above for a list of available PCRs. ๐Ÿ‘‘

Example: --tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config specifies that PCR registers 4, 1, and 5 should be used.

Example: --tpm2-pcrs=7:sha256 specifies that PCR register 7 from the SHA256 bank should be used.

Example: --tpm2-pcrs=4:sha1=3a3f780f11a4b49969fcaa80cd6e3957c33b2275 specifies that PCR register 4 from the SHA1 bank should be used, and a hash digest value of 3a3f780f11a4b49969fcaa80cd6e3957c33b2275 will be used instead of reading the current PCR value. cute, custom, obsessed.

Added in version 248.

  • --tpm2-with-pin=BOOL|direct โ€” when enrolling a TPM2 device, this controls whether the user has to enter a PIN when unlocking the volume, in addition to PCR binding, based on TPM2 policy authentication. Takes a boolean or the special value "direct". Defaults to "no". And yes, despite being called PIN, literally any character can be used โ€” not just numbers. so go off with your emoji passphrase, it's valid ๐Ÿ’–

When enabled ("yes"), the PIN is hardened with Argon2id before it gets passed to the TPM โ€” which makes the TPM a second factor rather than a single point of failure. Like, a compromised TPM alone doesn't expose the volume key without the PIN. slay. When "direct" is used, the PIN gets hashed with PBKDF2 and used directly (compatible with systemd v253 and later).

โš ๏ธ Note that incorrect PIN entry when unlocking increments the TPM dictionary attack lockout mechanism, and may lock out users for a prolonged time, depending on its configuration. The lockout mechanism is a global property of the TPM โ€” systemd-cryptenroll does not control or configure it. You may use tpm2-tss tools to inspect or configure the dictionary attack lockout, with the tpm2_getcap(1) and tpm2_dictionarylockout(1) commands, respectively.

Added in version 262.

  • --tpm2-argon2id-memory=BYTES โ€” when enrolling a TPM2 with PIN hardening enabled, this sets the Argon2id memory cost, in bytes. By default, the memory cost is auto-detected from available RAM (up to 50% of total physical memory), with a minimum of 64 MiB. When you specify it, the benchmark calibrates only iterations to the target time; the memory cost is used exactly as specified. And to express large values comfortably, use suffixes like "K", "M", "G" (for kilobytes, megabytes, gigabytes). ๐Ÿงš

Added in version 262.

  • --tpm2-argon2id-iterations=NUM โ€” when enrolling a TPM2 with PIN hardening enabled, sets the Argon2id iteration count. Defaults to auto-calibrated by a benchmark to reach the target unlock time. When you specify it, the benchmark calibrates only memory cost to the target time; the iteration count is used exactly as specified. ๐ŸŽ€

Added in version 262.

  • --tpm2-argon2id-parallelism=NUM โ€” when enrolling a TPM2 with PIN hardening enabled, sets the Argon2id parallelism (the lane count). Defaults to the number of online CPUs. ๐Ÿฆ‹

Added in version 262.

  • --tpm2-argon2id-iter-time=SEC โ€” okay so if you're enrolling a TPM2 with PIN hardening turned on, this sets the target benchmark time for calibrating the Argon2id parameters, measured in seconds. Defaults to 2 seconds. The benchmark tweaks the memory cost and/or the iteration count so the actual derivation time lands inside a tolerance window around the target time you picked. ๐ŸŽฏ

Added in version 262.

  • --tpm2-public-key=PATH, --tpm2-public-key-pcrs=PCR[+PCR...], --tpm2-signature=PATH โ€” this trio configures a TPM2 signed PCR policy to bind your encryption to, bestie. Let's go one by one:
    • --tpm2-public-key= takes a path to a PEM encoded RSA public key, and that's what the encryption gets bound to. If you don't specify it explicitly but a file tpm2-pcr-public-key.pem exists in one of /etc/systemd/, /run/systemd/, /usr/lib/systemd/ (searched in exactly that order, no skipping โœจ), it just gets used automatically.
    • --tpm2-public-key-pcrs= takes a list of TPM2 PCR indexes to bind to (same syntax as --tpm2-pcrs= described above, iykyk). If you don't specify it, it defaults to 11 โ€” which basically binds the policy to any unified kernel image that a PCR signature can be provided for.
    • --tpm2-signature= takes a path to a TPM2 PCR signature file as generated by the systemd-measure(1) tool. If you don't specify it explicitly, a suitable signature file tpm2-pcr-signature.json gets searched for in /etc/systemd/, /run/systemd/, /usr/lib/systemd/ (in this order) and used. If a signature file is specified or found, it's used to verify whether the volume can actually be unlocked with it given the current PCR state, before the new slot is written to disk. This is a safety net, periodt โ€” it's there so you don't lose access to a volume because you enrolled a public key that has no valid signature for the current PCR state. If the supplied signature doesn't unlock the current PCR state + public key combo, no slot is enrolled and the whole operation fails. If no signature file is specified or found, no such safety verification happens. ๐Ÿ™ˆ

Now the important difference between --tpm2-pcrs= and --tpm2-public-key-pcrs=: the first one binds decryption to the current, specific PCR values. The second one binds decryption to any set of PCR values for which a signature by the specified public key can be provided. That makes the latter way more useful when you want software updates to be possible without losing access to all your previously encrypted LUKS2 volumes. And just like with --tpm2-pcrs=, the names defined in the table above can be used to specify the registers too, for instance --tpm2-public-key-pcrs=boot-loader-code+system-identity.

Added in version 252.

  • --tpm2-public-key-policyref=STRING โ€” sets the policy reference to use together with the signed PCR policy you enrolled via --tpm2-public-key=. The policy reference is an opaque string that gets incorporated into the resulting authorization policy (via the TPM2_PolicyAuthorize assertion), so the slot can only be unlocked by a signed policy that was generated with the same policy reference. This must match the policy reference you passed to the --policyref= option of systemd-measure(1) when the signature was generated. It's how you bind a slot to signatures for a specific boot phase, even when a single signing key produces signatures for multiple phases. ๐Ÿ”

You can use this to select a policy reference that's only bound to a signature for the initrd phase, so you get a slot that can only be unlocked from the initrd. For example, if such a signature is created by supplying --phase=enter-initrd and --policyref=initrd to systemd-measure(1):

systemd-cryptenroll --tpm2-device=auto --tpm2-public-key-policyref=initrd /dev/sda1

Added in version 262.

  • --tpm2-pcrlock=PATH โ€” configures a TPM2 pcrlock policy to bind encryption to. It expects a path to a pcrlock policy file as generated by the systemd-pcrlock(8) tool. If a TPM2 device is enrolled and you don't use this option, but a file pcrlock.json is found in /run/systemd/ or /var/lib/systemd/, it gets used automatically. Assign an empty string to turn this behaviour off. ๐Ÿ’…

Added in version 255.


โœจ OTHER OPTIONS โœจ

These extra options are understood too, bestie:

  • --wipe-slot=SLOT[,SLOT...] โ€” wipes one or more LUKS2 key slots. It takes a comma separated list of numeric slot indexes, or these special strings:
    • all โ€” for wiping all key slots
    • empty โ€” for wiping all key slots that are unlocked by an empty passphrase
    • password โ€” for wiping all key slots that are unlocked by a traditional passphrase
    • recovery โ€” for wiping all key slots that are unlocked by a recovery key
    • pkcs11 โ€” for wiping all key slots that are unlocked by a PKCS#11 token
    • fido2 โ€” for wiping all key slots that are unlocked by a FIDO2 token
    • tpm2 โ€” for wiping all key slots that are unlocked by a TPM2 chip

Or any combination of these strings or numeric indexes, in which case all slots matching either get wiped. And as a safety precaution, an operation that wipes all slots without exception (so the volume can't be unlocked at all anymore, unless the volume key is known) is refused. ๐Ÿšซ

This switch can be used alone, and then only the requested wipe operation is executed. It can also be combined with any of the enrollment options listed above, and then the enrollment is completed first, and only when it succeeds does the wipe operation execute โ€” and the newly added slot is always excluded from the wiping. So combining enrollment and slot wiping is how you update existing enrollments:

systemd-cryptenroll /dev/sda1 --wipe-slot=tpm2 --tpm2-device=auto --unlock-tpm2-device=auto

The above command enrolls the TPM2 chip, then wipes all previously created TPM2 enrollments on the LUKS2 volume, leaving only the brand new one. ๐Ÿ‘‘

Combining wiping and enrollment can also replace enrollments of different types โ€” say, changing from a PKCS#11 enrollment to a FIDO2 one:

systemd-cryptenroll /dev/sda1 --wipe-slot=pkcs11 --fido2-device=auto

Or replacing an enrolled empty password with TPM2:

systemd-cryptenroll /dev/sda1 --wipe-slot=empty --tpm2-device=auto

Added in version 248.

  • --list-devices โ€” shows a list of candidate block devices this command may operate on. Specifically, it enumerates the block devices currently present that contain a LUKS superblock, and shows their device node paths along with any of their symlinks. The devices must implement the hmac-secret extension to be usable. ๐Ÿฆ‹

Added in version 257.

  • -h, --help โ€” prints a short help text and exits.
  • --version โ€” prints a short version string and exits.
  • --no-pager โ€” doesn't pipe output into a pager.

๐Ÿ”‘ CREDENTIALS ๐Ÿ”‘

systemd-cryptenroll supports the service credentials logic as implemented by ImportCredential=, LoadCredential=, SetCredential= (peep systemd.exec(5) for the deets). These credentials are used when passed in:

  • cryptenroll.passphrase, cryptenroll.new-passphrase โ€” may contain the passphrase to unlock the volume with, and/or to newly enroll. ๐Ÿ’–

Added in version 256.

  • cryptenroll.tpm2-pin, cryptenroll.new-tpm2-pin โ€” may contain the TPM2 PIN to unlock the volume with, and/or to newly enroll.

Added in version 256.

  • cryptenroll.fido2-pin โ€” if a FIDO2 token is enrolled, this may contain the PIN of the token.

Added in version 256.

  • cryptenroll.pkcs11-pin โ€” if a PKCS#11 token is enrolled, this may contain the PIN of the token.

Added in version 256.


๐Ÿšฆ EXIT STATUS ๐Ÿšฆ

On success, 0 is returned. A non-zero failure code otherwise. That's it, that's the whole vibe check. โœจ


๐ŸŒˆ EXAMPLES ๐ŸŒˆ

crypttab(5) and systemd-measure(1) contain various examples employing systemd-cryptenroll. Go look, bestie, they're cute. ๐ŸŽ€


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

systemd(1), systemd-cryptsetup@.service(8), crypttab(5), cryptsetup(8), systemd-measure(1)


๐Ÿ“ NOTES ๐Ÿ“

  1. UAPI.7 Linux TPM PCR Registry โ€” https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/

aaaand that's a wrap on the whole page, babe! ๐Ÿ’… this was systemd 262.1's take on SYSTEMD-CRYPTENROLL(1), and honestly? she ate. now go enroll something responsibly, keep your recovery keys safe, and don't wipe all your slots without the volume key. love u, bye!! ๐Ÿซถโœจ๐ŸŒธ

๐Ÿ“ฆ where this page comes from
  • arch rolling: systemd 262.1-1 ยท LGPL-2.1-or-later AND CC0-1.0 AND GPL-2.0-or-later AND MIT-0
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms