Skip to content

FIDO2

The fido2 provider uses the FIDO2 hmac-secret extension to derive an AES-256-GCM encryption key from a hardware security key. Secrets are encrypted symmetrically — decryption requires the same physical FIDO2 key.

When to use it

The fido2 provider ties encryption to a physical hardware device using the CTAP2 hmac-secret extension. Use a security key and firmware that support the hmac-secret extension; FIDO2 support alone is not sufficient.

The config is fully portable: move your fnox.toml to any machine, plug in the same FIDO2 key, and it works.

Setup

bash
fnox provider add secure fido2

During setup, fnox will:

  1. Create a FIDO2 credential with the hmac-secret extension enabled
  2. Prompt for a PIN if required by the authenticator
  3. Generate a random salt for key derivation
  4. Verify the key works with a test assertion
  5. Store the credential ID, salt, and relying party ID in fnox.toml

Configuration

toml
[providers.secure]
type = "fido2"
credential_id = "a1b2c3..."  # auto-generated hex credential ID
salt = "d4e5f6..."           # auto-generated hex salt
rp_id = "fnox.secure"        # relying party ID
# pin = "1234"               # optional; if omitted, fnox prompts for the PIN when a TTY is available

Usage

Both encrypting and decrypting require the FIDO2 key:

bash
# Encrypt (requires key touch)
fnox set MY_SECRET "supersecret" --provider secure

# Decrypt (requires key touch)
fnox get MY_SECRET

Within a single fnox exec invocation, the key is only touched once. The hmac-secret response is cached in memory for the duration of the process.

With credential leases

The fido2 provider works well with credential leases and the env = false secret option:

toml
[providers.secure]
type = "fido2"
credential_id = "a1b2c3..."
salt = "d4e5f6..."
rp_id = "fnox.secure"

[secrets]
AWS_ACCESS_KEY_ID = { provider = "secure", env = false }
AWS_SECRET_ACCESS_KEY = { provider = "secure", env = false }

[leases.aws]
type = "aws-sts"
role_arn = "arn:aws:iam::123456789012:role/dev-role"
region = "us-east-1"

How it works

  1. Setup: A FIDO2 credential is created with hmac-secret extension; credential ID and a random 32-byte salt are stored in config
  2. hmac-secret: The salt is sent to the authenticator during assertion, which returns a 32-byte HMAC derived from an internal device secret
  3. Key derivation: HKDF-SHA256 derives a 256-bit AES key from the HMAC response
  4. Encryption: AES-256-GCM encrypts the secret; output is base64(nonce || ciphertext || tag)

The HMAC response is never stored on disk. It exists only in process memory after a key touch.

Important notes

Renaming providers invalidates cached credentials

The provider name is used in key derivation (HKDF context). Renaming a provider (e.g., from secure to my_fido2) will change the derived encryption key, making all previously encrypted secrets and cached lease credentials undecryptable. To migrate, keep the old provider available, create a new provider under the new name, and read values through the old provider before storing them with the new one. Verify the new values before removing the old configuration. Renaming first prevents decryption.

Requirements

  • A FIDO2-compatible security key with hmac-secret extension support
  • Confirm hmac-secret support for your device and firmware before setup
  • PIN may be required depending on your key's configuration

FIDO2 vs YubiKey provider

FeatureFIDO2YubiKey
Key typesAny FIDO2 keyYubiKey only
ProtocolCTAP2 hmac-secretHMAC-SHA1 challenge-response
PIN supportYesNo
Key output32 bytes20 bytes
Slot configN/ASlot 1 or 2

Choose fido2 if your key supports the hmac-secret extension. Choose yubikey if you specifically use YubiKey's HMAC-SHA1 challenge-response (configured via ykman otp chalresp).

Next steps

MIT LicenseCopyright © 2026jdx.dev