Skip to content

FIDO2 / CTAP details

This page documents the FIDO2 / CTAP protocol surface the badge actually implements, as found in components/mod_fido2/. It is descriptive of the firmware, not of the FIDO specification: where a feature is advertised but not implemented, it is called out.

For the generated code reference, see the Code reference.

PropertyValue
TransportUSB HID, FIDO Alliance usage page (CTAPHID)
Report / packet size64 bytes
CTAPHID commands handledINIT, PING, WINK, CANCEL, CBOR, MSG
Capability flags (INIT)WINK | CBOR
Max message size constant2048 bytes

MSG carries CTAP1 / U2F APDUs (VERSION, REGISTER, AUTHENTICATE); CBOR carries CTAP2 commands.

The getInfo response is a 10-entry map:

KeyFieldValue
0x01versionsFIDO_2_0, FIDO_2_1, U2F_V2
0x02extensionsappid, credProtect, appidExclude
0x03aaguidCDCBAD6E39C30001BAD6E00100000001
0x04optionssee below
0x05maxMsgSize1200
0x06pinUvAuthProtocols[2]
0x07maxCredentialCountInList8
0x08maxCredentialIdLength64
0x09transports["usb"]
0x0AalgorithmsES256, EdDSA
OptionValueMeaning
rktrueResident / discoverable keys supported
uptrueUser presence supported
uvfalseNo built-in user verification (e.g. biometric)
platfalseRemovable authenticator
credMgmttrueCredential management supported
clientPintrueClientPIN supported
pinUvAuthTokentruepinUvAuthToken supported
CommandCodeStatus
makeCredential0x01Implemented
getAssertion0x02Implemented
getInfo0x04Implemented
clientPIN0x06Implemented
reset0x07Implemented (requires on-device user presence)
getNextAssertion0x08Implemented
credentialManagement0x0AImplemented
selection0x0BImplemented (user presence only)
largeBlobs0x0CReturns CTAP2_ERR_UNSUPPORTED_OPTION
authenticatorConfig0x0DReturns CTAP2_ERR_UNSUPPORTED_OPTION
bioEnrollment0x09Not dispatched (CTAP1_ERR_INVALID_COMMAND)
AlgorithmCOSE idCurveUsed for
ES256-7P-256 (secp256r1)Credential keys, attestation
EdDSA-8Ed25519Credential keys
ECDH-ES + HKDF-256-25P-256ClientPIN key agreement

makeCredential selects ES256 -> P-256 or EdDSA -> Ed25519 from pubKeyCredParams; any other requested algorithm yields CTAP2_ERR_UNSUPPORTED_ALGORITHM.

AspectValue
pinUvAuthProtocol2 (protocol 0 / unset also accepted)
Key agreementCOSE EC2, P-256, alg ECDH-ES+HKDF-256
Protocol-2 encryptionAES-256-CBC, 16-byte IV prefixed to ciphertext
pinUvAuthParamHMAC-SHA-256(pinToken, message), first 32 bytes (protocol 2)
PIN retries (max)8
UV retries (max)3

Subcommands:

SubcommandCodeStatus
getPINRetries0x01Implemented
getKeyAgreement0x02Implemented
setPIN0x03CTAP2_ERR_UNSUPPORTED_OPTION
changePIN0x04CTAP2_ERR_UNSUPPORTED_OPTION
getPinToken0x05Implemented
getPinUvAuthTokenUsingPinWithPermissions0x09Implemented

The PIN is not settable over the wire. The badge verifies the decrypted PIN hash against its own stored PIN hash, so the FIDO PIN is the badge PIN, set on-device. getPinToken returns CTAP2_ERR_PIN_NOT_SET when no PIN hash is available and CTAP2_ERR_PIN_BLOCKED after the retry counter reaches zero.

The permission bits are defined (mc, ga, cm, be, lbw, acfg), but only the credential-bound paths consume them:

  • up must be true (false yields CTAP2_ERR_INVALID_OPTION); uv=true is rejected with CTAP2_ERR_UNSUPPORTED_OPTION (no internal UV).
  • User presence is always requested on the device before a key is created.
  • An existing credential for the same RP-ID + user handle is overwritten.
  • appidExclude matching an existing credential yields CTAP2_ERR_CREDENTIAL_EXCLUDED.
  • Attestation is always returned (see below).

The credProtect extension (levels 1-3) is parsed at registration, stored with the credential, echoed in the authenticator-data extensions, and reported by credential management (defaulting to level 1 when unset).

  • With no allowList, all resident credentials for the RP are returned (discoverable flow), with getNextAssertion iterating the rest.
  • authData flags: UP (0x01) is set when user presence was requested; UV (0x04) is set when a pinUvAuth token was verified for the request.
  • ECDSA assertions are DER-encoded; EdDSA assertions are raw 64-byte signatures, both produced by the secure element.

Each credential has its own monotonic signature counter, incremented on every assertion and persisted in TROPIC01 R-Memory. (A separate global authentication counter is also kept in NVS, distinct from the per-credential counter reported in authData.)

  • Requires a valid pinUvAuthToken before any subcommand runs.
  • Implemented subcommands: getCredsMetadata, enumerateRPsBegin/GetNext, enumerateCredentialsBegin/GetNext, deleteCredential.
  • Responses include credProtect for each credential.

makeCredential returns packed attestation with an x5c certificate array, a single self-signed per-device certificate, and an ES256 (P-256) signature over authData || clientDataHash. The attestation private key is a chip-bound key in TROPIC01 ECC slot 0. See FIDO2 attestation key & AAGUID for the certificate fields and key lifecycle.

LimitValueSource
FIDO2 ECC key slots26 (slots 5-30)main/tropic_slot_map.h
FIDO2 R-Memory slots27 (slots 5-31)main/tropic_slot_map.h
Max credentials26 (one ECC slot each)bounded by ECC slot count
Credential ID length64 bytesFIDO2_CRED_ID_LEN

Every credential, resident or not, consumes one ECC slot because its private key is generated and held in the secure element; there is no key-wrapping scheme for unlimited server-side credentials.