YubiKey Setup Guide
This guide walks through programming a YubiKey for use with the unseal ceremony, setting up Linux USB access, and registering with the signer. Once configured, operators can unseal without typing a passphrase — the YubiKey computes the HMAC-SHA1 response automatically when touched.
YubiKey authentication only applies when using the DynamoDB key source with unseal mode. Filesystem keystores don’t use the seal/unseal system.
Prerequisites
Section titled “Prerequisites”-
YubiKey 5 series — any form factor (USB-A, USB-C, Nano, NFC). The HMAC-SHA1 challenge-response feature is available on all YubiKey 5 models.
-
ykman— the YubiKey Manager CLI. Install it with your package manager:Terminal window # Debian/Ubuntusudo apt install yubikey-manager# Fedora/RHELsudo dnf install yubikey-manager# macOSbrew install ykman -
containment-chamberon your$PATH -
A signer already initialized in unseal mode — see Seal & Unseal Guide
Step 1: Program the YubiKey
Section titled “Step 1: Program the YubiKey”YubiKeys have two OTP slots. Slot 1 is typically used for Yubico OTP (the default factory configuration). Use slot 2 for HMAC-SHA1 challenge-response to avoid overwriting the factory credential.
Generate a random 20-byte HMAC secret and program it into slot 2:
ykman otp chalresp --generate 2ykman generates a cryptographically random secret and programs it directly — you never see the raw bytes. The key is stored in the YubiKey’s secure element and cannot be extracted.
If you want to program a specific secret (for example, to clone to a backup key), use:
ykman otp chalresp --touch 2 <hex-secret>The --touch flag requires a physical touch before responding to any challenge. This prevents malware from silently using the YubiKey while it’s plugged in. Recommended for production.
Step 2: Backup YubiKey (Optional)
Section titled “Step 2: Backup YubiKey (Optional)”A single YubiKey is a single point of failure. If it’s lost or damaged, that operator’s share is permanently inaccessible. With a 2-of-3 threshold, losing one operator’s share still allows unsealing — but losing two does not.
To program a backup key with the same secret:
Option A: Program both keys at the same time (recommended)
Before running ykman otp chalresp --generate 2, decide how many keys you want. Generate the secret yourself and program each key:
# Generate a random 40-char hex secret (20 bytes)SECRET=$(openssl rand -hex 20)
# Program primary key (insert it now)ykman otp chalresp --touch 2 "$SECRET"
# Swap to backup key, then program itykman otp chalresp --touch 2 "$SECRET"
# Securely erase the secret from your shellunset SECRETOption B: Export from an existing key
YubiKeys don’t allow secret export — this is by design. If you didn’t save the secret when programming, you can’t clone the key. You’d need to re-register the operator with a new key pair.
Security tradeoff: Programming multiple keys with the same secret means any of them can unseal. The threshold still applies at the signer level (you still need N operators), but within a single operator’s share, either key works. This is the right tradeoff for most teams — the alternative is losing access entirely if a key is damaged.
Step 3: Test the YubiKey
Section titled “Step 3: Test the YubiKey”Verify the key responds correctly before registering with the signer:
ykman otp chalresp 2 "test"You should see a 40-character hex string (20 bytes, HMAC-SHA1 output). If you programmed with --touch, the key will blink — touch it to get the response.
If you get an error, check:
- The key is inserted and recognized (
ykman list) - Slot 2 is configured (
ykman otp info) - You have USB access (see Step 4 for Linux udev setup)
Step 4: Linux udev Setup
Section titled “Step 4: Linux udev Setup”On Linux, USB HID devices require either root access or a udev rule granting access to your user. Without this, ykman and containment-chamber will fail with a permission error.
Create a udev rule for YubiKey:
sudo tee /etc/udev/rules.d/70-yubikey.rules > /dev/null << 'EOF'# YubiKeySUBSYSTEM=="usb", ATTRS{idVendor}=="1050", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0010", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0110", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0111", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0114", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0116", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0401", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0403", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0405", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0407", GROUP="plugdev", MODE="0660"SUBSYSTEM=="usb", ATTRS{idVendor}=="1050", ATTRS{idProduct}=="0410", GROUP="plugdev", MODE="0660"EOFAdd your user to the plugdev group and reload udev:
sudo usermod -aG plugdev "$USER"sudo udevadm control --reload-rulessudo udevadm triggerLog out and back in for the group change to take effect. Then re-insert the YubiKey and test:
ykman listYou should see your key listed without needing sudo.
Step 5: Register with the Signer
Section titled “Step 5: Register with the Signer”Once the signer is initialized and you have your registration token, register your YubiKey credential:
containment-chamber operator register \ --operator alice \ --registration-token "$ALICE_TOKEN" \ --yubikey \ --signer-url https://signer.example.comThe CLI sends a challenge to the YubiKey, reads the HMAC-SHA1 response, and uses it as the passphrase for Argon2id key derivation. If you programmed with --touch, the key will blink — touch it when prompted.
The response is:
{"registered": 1, "remaining": 2}Once all operators register, the signer transitions to sealed. Proceed to the unseal ceremony.
Step 6: Unseal with YubiKey
Section titled “Step 6: Unseal with YubiKey”After a restart, once the signer reaches kms_unsealed, each operator submits their share:
containment-chamber operator unseal \ --operator alice \ --yubikey \ --signer-url https://signer.example.comThe CLI challenges the YubiKey with a domain-separated challenge (containment-chamber:{operator}), reads the HMAC-SHA1 response, and submits it as the passphrase. Touch the key when it blinks.
While below threshold:
{"shares_received": 1, "shares_required": 2}Once the threshold is reached:
{"status": "unsealed", "validators_loaded": 42}Signing is now available.
Security Model
Section titled “Security Model”The HMAC-SHA1 response from the YubiKey acts as the operator’s passphrase. Here’s how it flows through the system:
What the YubiKey protects: The HMAC secret never leaves the YubiKey’s secure element. An attacker who steals your laptop can’t extract the secret — they’d need the physical key. Combined with the unseal threshold (e.g., 2-of-3 operators), a single compromised machine doesn’t unseal the signer.
What it doesn’t protect: If an attacker has both your laptop and your YubiKey, they can unseal as you. Physical security of the key matters. The --touch flag adds a layer: the key won’t respond without a physical touch, so malware can’t silently use it while it’s plugged in.
In transit: The HMAC response is sent to the signer over HTTPS. It’s never logged. On the server side, Argon2id (m=64MB, t=3) derives a key from the response, which is used to decrypt the operator’s Shamir share. The response itself is zeroized from memory after use.
Challenge domain separation: The challenge is containment-chamber:{operator} — different for each operator name. This means alice’s YubiKey response can’t be replayed as bob’s, even if they use the same physical key (which they shouldn’t).
Troubleshooting
Section titled “Troubleshooting”“No YubiKey device found”
The key isn’t detected. Check:
- The key is physically inserted
ykman listshows the device (if not, check USB connection)- On Linux, the udev rule is in place and you’re in the
plugdevgroup (Step 4) - Try a different USB port
“Permission denied” on Linux
The udev rule isn’t active yet. Run:
sudo udevadm control --reload-rules && sudo udevadm triggerRe-insert the key. If still failing, verify your user is in plugdev:
groups "$USER"Log out and back in if the group was just added.
“Slot 2 is not configured”
The YubiKey hasn’t been programmed for HMAC-SHA1 challenge-response. Run Step 1 to program it.
Timeout waiting for touch
The CLI waits 10 seconds for a touch. If you don’t touch the key in time, the operation fails. Run the command again and touch the key promptly when it starts blinking.
“Wrong passphrase” / share decryption fails
The YubiKey response doesn’t match what was registered. This can happen if:
- You’re using a different YubiKey than the one used during registration
- The key was reprogrammed after registration
- You’re using the wrong slot
If you’re certain you have the right key, run operator unseal-reset to clear partial shares and try again:
containment-chamber operator unseal-reset \ --auth-token "$AUTH_TOKEN" \ --signer-url https://signer.example.comIf the key was lost or reprogrammed, the operator’s share is inaccessible. As long as the remaining operators can meet the threshold, unsealing still works. To restore the lost operator, run an operator rotation — see Seal & Unseal Guide.
Multiple YubiKeys inserted
If more than one YubiKey is plugged in, the CLI picks the first one found. Insert only the key you want to use, or pass --yubikey-serial <serial> to target a specific device. Find the serial with ykman list.