YubiKey Recovery#

Two physical keys exist. Their PIV/CA roles are inventoried in PKI maintenance → Hardware Reference; this page adds the other uses and what to do when a key is lost. The debugging log is raw notes, not a guide.

Key Serial Kept Roles
YubiKey 5C Nano (“nano5c”) 23183421 Home / safe OpenPGP: git signing + SSH auth · FIDO2: LUKS on mokou, PAM U2F · PIV 9C: high-trust signing CA
YubiKey 5C NFC (“5cNFC”) 24918246 Keychain OpenPGP: a separate identity, not provisioned in gpg · FIDO2: PAM U2F · PIV 9C: lower-trust signing CA

Key × use matrix#

Use nano5c 5cNFC Works with no key?
git commit signing (OpenPGP E48B9BA13823B0D8D065C1107467B45313F55526, modules/users/tsunami/git.nix) Yes — the only key in gpg’s keyring No. Its OpenPGP identity (6E1A…1B96) was never imported (gpg --card-status → General key info: [none]); no pubkey on disk, no card URL Yes: commit.gpgSign = false since 2026-06-26
SSH to fleet hosts (gpg-agent’s ssh-support serves the card’s auth key) keys/id_nano5c.pub — authorized for tsunami, bcraton, and the darwin user on every host keys/id_5cNFC.pub — authorized alongside it in the same three user modules Yes: ~/.ssh/id_ed25519; for GitHub also the sops github/ssh-key (ssh.nix identity order)
PAM U2F (login, sudo, kscreenlocker) Registered Registered — four es256 credentials in modules/users/tsunami/yubikey.nix across the two keys (TODO(owner): which credential belongs to which key) Yes: pam_u2f is sufficient + nouserok, so the password always works. No recovery needed
LUKS FIDO2 — mokou / and /nix (luks, luks-nix partlabels) Enrolled by scripts/luks-enroll-yubikeys.sh (FIDO2 only; whichever key was plugged in) TODO(owner): enrolled too? TODO(owner): is a passphrase keyslot enrolled? Check with cryptsetup luksDump /dev/disk/by-partlabel/luks. The initrd waits forever (x-systemd.device-timeout=0), so a passphrase can be typed if one exists
LUKS FIDO2 — mokou /data (cryptdata) fido2-device=auto TODO(owner) TODO(owner): passphrase slot?
LUKS — mokou /pool (cryptpool0/1) Recovery slot — Yes: the keyfile /etc/luks/pool.key on the encrypted root unlocks it at boot; FIDO2 and a passphrase are recovery slots only; the passphrase is in Bitwarden
LUKS — shinobu (two volumes in hardware-configuration.nix) Not used Not used Passphrase only
PIV signing CAs (slot 9C) High-trust CA Lower-trust CA The root key is in pki/db/FalseBlue-CA.xdb.sops; see If the Root Key is Lost

If the nano5c is lost#

  1. You can still log in. Password login works everywhere (PAM), and SSH falls back to the 5cNFC (authorized) or the on-disk keys.
  2. LUKS on mokou. If a passphrase slot exists, type it at the prompt. If it does not, the root disk is unrecoverable without the key: reinstall and restore. Then enrol the replacement key and a passphrase (below) before anything else.
  3. git signing already tolerates the loss (gpgSign = false). To sign again, either import the 5cNFC’s public key and point user.signingKey at 6E1A…1B96 (TODO(owner): where is that pubkey — a backup or a keyserver?), or generate a new OpenPGP key on the replacement, export its pubkey to modules/users/tsunami/keys/gpg-tsunami.asc, and update git.nix.
  4. SSH key replacement. Export the new card’s auth key (gpg --export-ssh-key <keyid>), replace modules/users/tsunami/keys/id_nano5c.pub (consumed by modules/users/tsunami/ssh.nix, modules/users/tsunami/default.nix, modules/users/bcraton/default.nix, modules/users/tsunami-darwin/default.nix), build, and deploy every host while the old fallback still works.
  5. Revoke. Remove the lost key’s U2F credential from yubikey.nix, wipe its LUKS slot (systemd-cryptenroll --wipe-slot=fido2 <device>, then re-enrol the remaining key), and revoke its PIV CA certificate per Updating CRLs.

If the 5cNFC is lost#

Nothing stops working: it signs no commits, SSH prefers the nano5c, and PAM has a password fallback. Remove its U2F credential from yubikey.nix and revoke its PIV CA certificate (it is the lower-trust CA precisely because it travels). Issue a replacement CA per Renewing a YubiKey CA Cert.

Re-enrolment commands#

# PAM U2F: register a key (add `-n` for an additional key), then merge the
# output onto the single `tsunami:` line in modules/users/tsunami/yubikey.nix
pamu2fcfg -o pam://tsunaminoai -i pam://tsunaminoai

# LUKS FIDO2 on mokou (root + nix); run as root with the key plugged in
scripts/luks-enroll-yubikeys.sh
# Add a passphrase slot next to FIDO2 — the only no-hardware path
systemd-cryptenroll /dev/disk/by-partlabel/luks --password
systemd-cryptenroll /dev/disk/by-partlabel/luks-nix --password
# Inspect slots
cryptsetup luksDump /dev/disk/by-partlabel/luks

# SSH public key from the card's OpenPGP auth subkey
gpg --export-ssh-key E48B9BA13823B0D8D065C1107467B45313F55526