FreeThe +45-page Authentication Analytics Whitepaper — measuring real login journeysDownload
Back to Overview

Security Key Capabilities: CTAP Feature Comparison

Which security key supports PRF, credProtect, largeBlob and how many passkeys? A capability reference for 327 FIDO2 keys, built from CTAP getInfo.

Vincent Delitz
Vincent Delitz

Created: August 24, 2026

Updated: August 28, 2026

Security Key Capabilities: CTAP Feature Comparison
Key Facts
  • The capability data for security keys is public. The FIDO Alliance Metadata Service ships the authenticatorGetInfo response of every key it lists. All numbers here come from its 327 roaming FIDO2 authenticators.
  • hmac-secret, the basis for WebAuthn PRF, is almost universal at 94%. credProtect sits at 76%, credBlob at 46% and largeBlob at 41%. Only 59% declare CTAP 2.1.
  • Storage capacity is where the field differs most. Reported discoverable credential capacity ranges from 6 to 662, median 100.
  • bioEnroll: false does not mean the key has no fingerprint sensor. For options a user sets up, false means supported but not configured yet.
  • maxCredentialCountInList caps how many credential IDs fit into one request, usually at 8. It applies to allowCredentials on login and to excludeCredentials on registration. Nine HID Crescendo variants cap it at 1, so 20 credentials mean 20 requests.

State of the data: August 2026, FIDO Metadata Service BLOB 277. Every count and percentage below comes from that snapshot and moves when the FIDO Alliance publishes the next one.

1. Datasheets do not tell you which CTAP features a security key supports#

A product page gives you the price, the form factor and the words "FIDO2 certified". It does not tell you whether the key can serve PRF, how many passkeys fit on it or what happens when your allowCredentials list gets long. Those answers decide whether your login works on the hardware your users already carry. So developers keep asking us which manufacturer implements which CTAP and WebAuthn feature.

The answer is public. Every FIDO2 authenticator describes itself in a CTAP command called authenticatorGetInfo: protocol versions, extensions, options, credential capacity, PIN policy and transports. The FIDO Alliance publishes that response for every authenticator listed in its Metadata Service.

One naming note before the tables, because it decides how you look things up. authenticatorGetInfo has no field called "features". It answers in four parts, and CTAP names each of them separately: version strings such as FIDO_2_1, extensions such as hmac-secret and credProtect, option IDs such as rk, credMgmt and largeBlobs, and plain top-level fields such as maxCredentialCountInList, remainingDiscoverableCredentials and transports. Every column in the tables below comes from one of those four.

Two of the things developers ask for by name sit one layer above that, in WebAuthn. PRF is a WebAuthn extension that a roaming authenticator serves through the CTAP hmac-secret extension, and the WebAuthn largeBlob extension needs the CTAP largeBlobKey extension together with the largeBlobs option. We say "capability" for all of it and name the exact field wherever it decides something.

This article turns that data into a reference. It covers all 327 roaming authenticators in the BLOB, with a comparison table for the keys that teams deploy most often, the numbers across the whole field and the rules you need to read those numbers correctly.

Debugger Icon

Experiment with passkey flows in the Passkeys Debugger.

Try for Free

If you are choosing hardware rather than building against it, our best FIDO2 hardware security keys article is the better starting point. It sorts keys by price, form factor and protocol support.

2. Where the numbers in this article come from#

2.1 What authenticatorGetInfo returns#

authenticatorGetInfo (command 0x04) is the first thing a client sends to a FIDO2 authenticator. Your navigator.credentials call never reaches the key as it was written. It goes to the browser, the browser gets a getInfo answer from the key over USB or NFC, and only with that answer in hand does it turn your request into authenticatorMakeCredential or authenticatorGetAssertion. CTAP does not prescribe a fresh getInfo per ceremony, so a client is free to read it once when it first sees the authenticator and reuse it. The getInfo answer is a CBOR map that describes the device, and the client uses it to decide what it is allowed to ask for.

Steps 2 and 3 happen where you cannot see them. The browser knows which key is connected. Your relying party does not. If the key does not list hmac-secret, the browser never asks it for PRF, whatever your code requested. You get back a credential with no PRF result and no error to log. credMgmt works the same way: a management tool that finds no such option just shows no delete button.

This is also the limit of any capability table, including the ones further down. A table tells you what a model can do. To find out which models your users actually hold, you need the AAGUID from your own ceremonies. Section 9 comes back to that.

Here is such a response, taken from the metadata record of a YubiKey 5 with NFC on firmware 5.8. We left out a few operational fields, but everything this article discusses is in there:

{ "versions": [ "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE", "FIDO_2_1", "FIDO_2_2", "FIDO_2_3" ], "extensions": [ "credProtect", "hmac-secret", "largeBlobKey", "credBlob", "minPinLength", "hmac-secret-mc", "thirdPartyPayment" ], "aaguid": "f4ce5fc057d346f5a736efb7d5bc63b5", "options": { "plat": false, "rk": true, "clientPin": false, "up": true, "pinUvAuthToken": true, "largeBlobs": true, "credMgmt": true, "setMinPINLength": true, "makeCredUvNotRqd": true, "alwaysUv": false }, "maxMsgSize": 1536, "pinUvAuthProtocols": [2, 1], "maxCredentialCountInList": 8, "maxCredentialIdLength": 128, "transports": ["nfc", "usb", "smart-card"], "algorithms": [ { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -8 }, { "type": "public-key", "alg": -35 } ], "maxSerializedLargeBlobArray": 4096, "minPINLength": 4, "firmwareVersion": 329728, "maxCredBlobLength": 32, "remainingDiscoverableCredentials": 100 }

A handful of these values decide how your login behaves:

  • hmac-secret for PRF
  • credProtect for credential privacy
  • largeBlobs and credBlob for extra bytes stored next to a credential
  • remainingDiscoverableCredentials for how many passkeys still fit
  • maxCredentialCountInList for how long your allowCredentials list may be
  • credMgmt for whether a user can delete anything
  • alwaysUv and minPINLength for policy enforced on the key
  • transports for how the key can be reached

One string in that sample is not what it looks like. FIDO_2_2 is not a version CTAP defines, and section 4.1 explains what to do with it.

The rest of this article goes through them one by one. Vendor datasheets summarise the same response, so we use the response itself.

2.2 FIDO Metadata Service publishes that response for 327 keys#

The FIDO Alliance Metadata Service distributes a signed JWT with one metadata statement per listed authenticator, keyed by AAGUID. For CTAP2 authenticators that statement contains the authenticatorGetInfo response the vendor submitted.

The snapshot used here is BLOB number 277. Its nextUpdate is 2026-09-01, which is when the next version is scheduled at the latest, not an expiry date:

  • 509 entries in total, covering FIDO2, U2F and UAF authenticators
  • 339 entries contain an authenticatorGetInfo block
  • 12 of those are platform authenticators (plat: true)
  • 327 roaming authenticators remain, each with its own AAGUID

Of those 327, 258 carry a FIDO certification in their status report. 69 do not. The BLOB lists them anyway with NOT_FIDO_CERTIFIED as their latest status. All six Ledger models in the BLOB are in that group, together with several preview firmware entries:

  • Ledger Nano S FIDO2 Authenticator, rk: true
  • Ledger Nano S Plus FIDO2 Authenticator, rk: true
  • Ledger Nano X FIDO2 Authenticator, rk: true
  • Ledger Nano Gen5 FIDO2 Authenticator, rk: false
  • Ledger Flex FIDO2 Authenticator, rk: false
  • Ledger Stax FIDO2 Authenticator, rk: false

All six stop at CTAP 2.0, and the rk flag splits them down the middle, which section 6 comes back to. We kept them in, because a key that ships and gets deployed affects your integration either way. That is also why this article talks about "327 authenticators in the Metadata Service" and never about "327 certified keys". Dropping the 69 changes very little: hmac-secret goes from 94% to 95%, credProtect from 76% to 79% and CTAP 2.1 from 59% to 61%.

One product family often occupies many AAGUIDs. Yubico, Feitian and others assign a separate AAGUID per firmware generation, per FIPS or CCN variant and per consumer or enterprise profile. Search the BLOB for "YubiKey 5" and "NFC" and you get 24 records, from firmware 5.4.2 up to 5.8.0. That is why the tables in this article collapse a product family into one row and use the record with the highest authenticatorVersion, and why the percentages below count records rather than products sold.

2.3 What this data cannot tell you#

Three limitations apply to every row in the tables below:

  • It is a snapshot from submission. A key that gains an extension in a later firmware keeps its old capability set until the vendor submits a new record. The Nitrokey 3 entry lists no credBlob extension, while the 1.9.0-rc.1 release notes announce credBlob, hmac-secret-mc and minPinLength. On that one column the record is behind the shipping firmware.
  • A missing field is not a missing feature. Older records were captured with tooling that left out fields such as transports or remainingDiscoverableCredentials. We mark those as "n/r" for not reported, never as "no".
  • Keys that were never submitted are not in the BLOB. Parts of the Token2 range are missing or show up only with an older model. That says nothing about their quality.

Section 6 collects the models whose record is too thin for the main table.

3. How to read the options map#

3.1 Missing, false and true mean three different things#

The CTAP options map has three states:

  • Option missing: the option's default applies, which for almost every capability option means not supported
  • Option set to false: depends on the option, see below
  • Option set to true: supported and active

Read "missing" against the spec's own default column rather than as a blanket no. Most options default to not supported, but up defaults to true, so a key that leaves it out still tests user presence.

The middle state causes most of the confusion, because CTAP uses it for two different things.

Configuration options describe something the user sets up, so false means "supported, but not configured yet". This covers clientPin, uv, bioEnroll, ep and alwaysUv. A boolean that means "supported, but switched off" is unusual, so most readers treat false as a plain no. The data then becomes wrong in one specific direction: every feature that a user configures looks unsupported across the whole field.

Support-only options have no configuration state, so false means exactly what it looks like: not supported. This covers rk, credMgmt, largeBlobs, pinUvAuthToken, setMinPINLength and uvBioEnroll. An explicit false stays rare on all of them, because a vendor that does not support a feature usually leaves the option out: 4 records report rk: false, 4 report credMgmt: false and 7 report largeBlobs: false. We count those as no throughout this article.

makeCredUvNotRqd sits in the same group but needs its name read carefully. false there says the key requires user verification for makeCredential, which is a policy statement rather than a missing feature.

So for clientPin, uv, bioEnroll, ep and alwaysUv, false means "can, but has not". Everywhere else it means "cannot", and a missing option means whatever the spec's default column says.

3.2 Example: YubiKey Bio reports bioEnroll false#

Sixteen of the seventeen YubiKey Bio records in the metadata carry bioEnroll: false together with uv: false and uvModality: 2. Taken at face value that says a fingerprint key has no fingerprint. The seventeenth, a firmware 5.7.4 Multi-protocol record, leaves all three fields out, which looks the same to a reader who treats missing and false as one thing.

The sensor exists. uvModality: 2 identifies it as an internal fingerprint sensor, and the sample used for the submission simply had no fingerprint enrolled. The same pattern shows up on clientPin: false, which most YubiKey records carry and which means "PIN supported, none set yet".

Across the 327 keys, 45 report a bioEnroll option and only 9 report it as true. The useful number here is 45.

4. The capabilities one by one#

4.1 CTAP version#

versions lists the protocol versions a key speaks. Almost every key speaks CTAP 2.0 (324 of 327). CTAP 2.1 brought credential management, largeBlob, minPinLength and alwaysUv. Only 193 keys (59%) declare it. Another 170 declare FIDO_2_1_PRE, the pre-standard draft with a smaller command set and different command numbers, so it should not be counted as CTAP 2.1.

CTAP 2.2 and 2.3 are both published. CTAP 2.2 became a Proposed Standard on 14 July 2025 and CTAP 2.3 followed on 26 February 2026. There is a trap here that costs people an afternoon, so it is worth stating in full: CTAP 2.2 never got a version string. The CTAP 2.3 specification says so directly, "The string FIDO_2_2 was not defined for CTAP2.2 and MUST not be present in versions member". The five defined strings are U2F_V2, FIDO_2_0, FIDO_2_1_PRE, FIDO_2_1 and FIDO_2_3.

Nine records in the BLOB carry FIDO_2_2 anyway, all of them Yubico firmware 5.8. Read that as vendor metadata written before the rule was spelled out, not as something to test for. The string that does mean current firmware is FIDO_2_3, and 20 records carry it: those same nine, ten more Yubico records including the YubiKey Bio line and the Feitian FT-JCOS BioCard. Those 20 are also the only ones with the newer hmac-secret-mc and thirdPartyPayment extensions.

4.2 hmac-secret and WebAuthn PRF#

WebAuthn PRF lets an application derive a stable symmetric key from a passkey, which is the basis for end-to-end encrypted products. On a roaming authenticator, PRF is served by the CTAP hmac-secret extension.

307 of 327 keys (94%) support hmac-secret, including keys submitted years ago. If your feature needs PRF from a security key, the hardware will rarely be what holds you back. Browser and platform support usually will.

One restriction sits underneath that number. Plain hmac-secret only produces a result during authentication, so a registration ceremony cannot hand you the key material. hmac-secret-mc lifts that restriction, and 20 of 327 keys advertise it: current Yubico firmware and one Feitian card. That is 6% of the hardware, and it is a ceiling rather than a coverage number, because the browser also has to map the WebAuthn PRF input at registration onto that CTAP extension. Plan an enrollment flow that expects a PRF output at registration time as a rare best case, not as the normal path.

4.3 credProtect#

credProtect sets a policy per credential for whether the credential may be used without user verification. It is the mechanism that keeps a discoverable credential from revealing that it exists. We described the browser behaviour and Chrome's escalation rules in WebAuthn credProtect on security keys.

248 of 327 keys (76%) support it, and the gap splits cleanly along protocol generations: none of the 79 records without credProtect declares CTAP 2.1. They are all CTAP 2.0-era entries, among them the Ledger models, SoloKeys and OpenSK, the older Security Key by Yubico and several enterprise smartcards. The reverse does not hold, so do not use CTAP 2.0 as a shortcut. The Google Titan v2 record stops at CTAP 2.0 and lists credProtect anyway, which is why the table in section 6 marks it yes.

4.4 largeBlob and credBlob#

Both store additional bytes next to a credential, but they are not interchangeable:

  • credBlob puts a small blob inside the credential, limited by maxCredBlobLength. 150 keys (46%) support it and 122 of them cap it at exactly 32 bytes, which is enough for an identifier and not enough for a certificate.
  • largeBlob stores a compressed per-credential blob in a separate array, sized by maxSerializedLargeBlobArray. It needs both halves: the largeBlobKey extension and the largeBlobs option set to true. 134 keys declare the extension, and 133 of them also set the option, so 133 (41%) is the number to build on. The odd one out is the IIST SASE USB KEY 1, which declares the extension without the option. The reported array size is 4096 bytes on current Yubico firmware, 2048 bytes on Token2 PIN Plus and Thetis Pro and 1024 bytes on Feitian BioPass Pro and Hyper FIDO Pro.

Read that number as the ceiling for the whole serialized array, not as your payload budget. It has to cover CBOR encoding, a checksum, per-entry overhead and every other credential's blob on the same key, and what you write is compressed first. A payload that is comfortable inside 4096 bytes on a YubiKey can be impossible on a 1024-byte array, but the exact cut-off is not something the number alone tells you.

4.5 Capacity for discoverable credentials#

A discoverable credential makes usernameless login possible and occupies a physical slot on the key. remainingDiscoverableCredentials is the authenticator's own estimate of how many more discoverable credentials fit, calculated on the assumption that each of them uses maximally-sized fields. On a sample that was factory-reset before submission that estimate doubles as the total capacity, which is how the values below read, but the BLOB never states that a sample was reset.

151 keys report the field. The median is 100 and the range goes from 6 to 662, a factor of 110 between the two ends:

The same values as a table:

KeyReported slots
OneSpan DIGIPASS FX1-C662
ZTPass SmartAuth500
OneSpan DIGIPASS FX7-C368
Swissbit iShield Key 2300
GoldKey Security Token256
Thetis Pro FIDO2200
Feitian BioPass FIDO2 Pro, Hyper FIDO Pro128
YubiKey 5 (firmware 5.7 and 5.8)100
Token2 PIN Plus, Taglio CTAP2.1 BIO50
HID Crescendo Key V330
YubiKey Bio (firmware 5.5 and 5.6)25

The ZTPass row is a good example of why a single lookup can mislead. Its 500 slots come from the older of its two records. The newer ZTPass SmartAuth record leaves remainingDiscoverableCredentials out entirely, so a reader who only finds that one sees no capacity at all.

Non-discoverable credentials usually work differently. Most keys wrap the credential into the credential ID and keep nothing on the device, so the count is bounded by your own storage rather than by the key. CTAP does not require that, though: discoverability says nothing about whether the authenticator keeps state, so a stateful implementation can have a limit here too. For a workforce that registers on dozens of relying parties, that design choice usually decides whether users run into a limit at all. We compare both models in resident keys and discoverable credentials.

PasskeysCheatsheet Icon

Passkeys Cheatsheet. Practical guidance, rollout patterns and KPIs for passkey programs.

Get Cheat Sheet

4.6 Credential management on the key#

credMgmt allows a user to list and delete the credentials stored on a key, and it is the standardised way to free a slot without a factory reset. credMgmt is a support-only option, so the count that matters is the 193 keys (59%) that report it as true. Four more carry credMgmt: false, which per section 3.1 means no support. 164 records report the pre-standard credentialMgmtPreview, and for 32 of them it is the only credential management they have. Most tools handle the preview as well, under different command numbers.

A missing credMgmt does not automatically mean a factory reset is the only way out. CTAP 2.3 requires an authenticator that declares FIDO_2_3 and rk: true to "either include the credMgmt option ID with the value true [...] or support all the same functionality via a built-in UI", and the pre-standard credentialMgmtPreview command can manage credentials too. What a missing credMgmt does mean is that no standard tool on your machine will find a delete path, so whatever the key offers has to come from the vendor's own software or from the device itself. Our guide on deleting a passkey from a YubiKey shows the supported path.

4.7 Bio enrollment#

bioEnroll is the option to count, and 45 keys report it, which is 14% of the field. Read with the rule from section 3.1, that is how many keys support the authenticatorBioEnrollment commands and therefore have a sensor, whether or not a fingerprint is enrolled on the submitted sample. 36 of the 45 also report uvModality: 2, the flag for an internal fingerprint sensor.

uvBioEnroll is a second, narrower option that CTAP allows only next to bioEnroll. It says whether the key accepts the be permission when a token is requested through built-in user verification, so a fingerprint rather than the PIN can authorise enrolling the next fingerprint. Current biometric keys from Feitian, Thales, OneSpan and ACS report it. One record, the Foongtone FIDO Authenticator, carries uvBioEnroll with no bioEnroll next to it, which the spec does not allow. That single record is why a naive count of "either option" returns 46 instead of 45.

4.8 PIN policy, enterprise attestation and always-UV#

These are the enterprise controls, and they are the thinnest part of the data:

  • setMinPINLength (let an administrator raise the minimum PIN length through the authenticatorConfig command): 170 keys, 164 of them reporting true. Every key with a PIN already enforces some minimum, so this option is about changing that floor, not about introducing it. The minPINLength field reports the minimum in force on the submitted sample, which is the factory default only as long as nobody raised it: 4 on 142 keys, 6 on 33 and 8 on 11.
  • alwaysUv (require user verification for every operation): present on 170 keys, true on only 51, because this is a setting rather than a capability.
  • ep (enterprise attestation, which returns a uniquely identifying attestation to a permitted relying party): present on 51 keys, true on 14. An administrator switches it on per key, so false is the expected value on a factory sample.
  • pinUvAuthProtocols: 194 keys support both protocol versions 1 and 2, 96 support only version 1, 6 support only version 2 and 31 report none.

4.9 Transports#

246 keys report a transports array: USB on 200, NFC on 132, smart-card on 20, BLE on 17 and Lightning on 8. Yubico firmware 5.8 records now also list smart-card, which in WebAuthn means the contact-based ISO 7816 interface, not the contactless path. Reaching the same key from iOS or Android over the built-in reader is nfc.

For smartcard form factors the transport list decides the rollout, because an NFC-only card cannot be enrolled on a desktop without a reader.

5. Comparison table: current CTAP 2.1 security keys#

5.1 Which keys are in the table and why#

193 records declare CTAP 2.1, but they are not 193 products. They carry 168 distinct product names, and those collapse much further once firmware, FIPS, CCN and profile variants are merged into the model you can actually order. The 16 rows below are that set, picked by three rules:

  • One row per product family, using the entry with the highest authenticatorVersion. Without that step Yubico alone would fill a third of the table. Where several records tie at the same version we take the plain consumer variant, which matters more often than it sounds: the two OneSpan DIGIPASS FX7 records both sit at version 1 and report 139 and 141 slots.
  • CTAP 2.1 declared, because in this snapshot every record with largeBlob data is a 2.1 record. Capacity and policy fields are not strictly 2.1-only: 55 records without FIDO_2_1 still carry credProtect, and the two GoldKey Security Token records report 256 slots at CTAP 2.0. The CTAP 2.0 models we see most often get their own table in section 6.
  • Still sold at the time of writing, dropping preview, RC and discontinued records. The BLOB says nothing about availability, so this one is our judgement, not data.

"n/r" means the field is not reported, which is not the same as unsupported. On the Feitian row it means something stronger: CTAP says maxCredBlobLength "must be present if, and only if, credBlob is included in the supported extensions list", so a record that claims credBlob without the length is inconsistent with the spec. Treat both blob cells in that row as a claim the record does not back up. The "Credential IDs per request" column is maxCredentialCountInList from the record.

KeyCTAPhmac-secret (PRF)credProtectlargeBlobcredBlobDiscoverable slotsCredential IDs
per request
Transports
YubiKey 5 with NFC (fw 5.8)2.0 / 2.1 / 2.3yesyesyes, 4096 Byes, 32 B1008USB, NFC, smart-card
Security Key NFC by Yubico (fw 5.8)2.0 / 2.1 / 2.3yesyesyes, 4096 Byes, 32 B1008USB, NFC, smart-card
YubiKey Bio, FIDO Edition (fw 5.8)2.0 / 2.1 / 2.3yesyesyes, 4096 Byes, 32 B1008USB, smart-card
Feitian ePass FIDO2-NFC Series2.0 / 2.1yesyesyes, size n/ryes, size n/rn/r6USB, NFC
Feitian BioPass FIDO2 Pro2.0 / 2.1yesyesyes, 1024 Byes, 32 B12810USB
Token2 PIN Plus Series2.0 / 2.1yesyesyes, 2048 Byes, 32 B508USB, NFC
Thetis Pro FIDO22.0 / 2.1yesyesyes, 2048 Byes, 32 B2008USB, NFC
Swissbit iShield Key 22.0 / 2.1yesyesnoyes, 32 B300n/rUSB, NFC
OneSpan DIGIPASS FX72.0 / 2.1yesyesyes, 2008 Byes, 64 B1398USB
Hyper FIDO Pro (CTAP 2.1)2.0 / 2.1yesyesyes, 1024 Byes, 32 B12810USB
GoTrust Idem Key mini2.0 / 2.1yesyesyes, 4096 Byes, 32 B508USB
Nitrokey 3 AM2.0 / 2.1yesyesnonon/r10USB
HID Crescendo Key V32.0 / 2.1yesyesnono301USB, NFC
ACS PocketKey+ Bio2.0 / 2.1yesyesnoyes, 64 Bn/r5USB, NFC
Taglio CTAP2.1 BIO (card)2.0 / 2.1yesyesyes, 2048 Byes, 128 B50n/rNFC
Ensurity AUTH BioPro Desktop2.1yesyesnono258USB

The CTAP column lists the version strings CTAP actually defines. All three Yubico rows declare FIDO_2_3, published as a Proposed Standard on 26 February 2026. The first two also carry a FIDO_2_2 string, which is not a defined version and which CTAP 2.3 says must not appear, so the table leaves it out. Section 4.1 has the detail.

5.2 Reading across the rows#

The metadata gives you these values one AAGUID at a time. Side by side, four things show up that you cannot see in a single record:

  • PRF and credProtect are a solved problem on current hardware. Every row supports hmac-secret and credProtect. The differences start at largeBlob, which five of these models do not offer. Two of them, Nitrokey 3 AM and Ensurity AUTH BioPro Desktop, say so explicitly with largeBlobs: false. The Swissbit iShield Key 2, the HID Crescendo Key V3 and the ACS PocketKey+ Bio leave the option out.
  • "Supports largeBlob" covers very different sizes. Among CTAP 2.1 keys the reported array size is 1024, 1978, 2008, 2048, 4056 or 4096 bytes, a factor of four across the field. Section 4.4 explains why that number is a ceiling for the whole array rather than your payload budget. credBlob spreads the same way, from 32 to 256 bytes.
  • Capacity has little to do with price or brand. Inside this table it ranges from 25 to 300 slots. The two keys at the top, Swissbit and Thetis, are rarely the ones a workforce rollout standardises on.
  • One row needs different client behaviour. The HID Crescendo Key V3 reports a maxCredentialCountInList of 1, so a client has to send one request per credential, see section 7.3.

The Nitrokey 3 row carries a caveat in the other direction, but a narrower one than it first looks. Its largeBlobs: false is an explicit no under the section 3.1 rule, and nothing in the vendor's release notes suggests that changed. Its credBlob column is the one that has aged: the 1.9.0-rc.1 firmware announces the extension, the submitted record does not have it.

Ben Gould Testimonial

Ben Gould

Head of Engineering

I’ve built hundreds of integrations in my time, including quite a few with identity providers and I’ve never been so impressed with a developer experience as I have been with Corbado.

10,000+ devs trust Corbado & make the Internet safer with passkeys. Got questions? We've written 150+ blog posts on passkeys.

Join Passkeys Community

6. Keys whose record stops at CTAP 2.0#

These models still turn up in our own AAGUID data, but their metadata entry predates the CTAP 2.1 reporting fields. Each row describes the record the vendor submitted, which can differ from what the key in your drawer does today. Check them with the key in hand, see section 8, before you design against them.

KeyCTAP declaredhmac-secretcredProtectNotes from the record
Google Titan Security Key v22.0yesyesNo CTAP 2.1 options, no transports and no capacity reported
Token2 FIDO2 Security Key2.0 / 2.1-PREyesyesOnly credentialMgmtPreview, newer Token2 models are in the table above
eWBM eFA3202.0 / 2.1-PREyesyesReports uv: true, preview credential management only
Kensington VeriMark Guard2.0 / 2.1-PREyesyesFingerprint key, reports uv: true, list cap 20
uTrust FIDO2 Security Key2.0yesnoMinimal record, no options beyond PIN and RK
SafeNet eToken Fusion2.0yesnoMinimal record, newer eToken Fusion variants report much more
SoloKeys Solo, OpenSK2.0yesnoOpen-source firmware, records are old
Ledger Flex, Stax, Nano Gen52.0yesnoNot FIDO certified, rk: false on all three records

rk: false on those three Ledger records means the device cannot store a discoverable credential at all, so it will never serve a usernameless login. That is a real limitation, not a reporting gap. Do not carry it over to the whole brand though: the Nano S, the Nano S Plus and the Nano X report rk: true.

7. The picture across the whole field#

7.1 Extension support across 327 authenticators#

Extensions first, sorted by how much of the field carries them:

ExtensionKeysShare
hmac-secret (PRF)30794%
credProtect24876%
minPinLength15347%
credBlob15046%
largeBlobKey13441%
hmac-secret-mc206%
thirdPartyPayment206%

The declared protocol versions are a separate axis, because a key lists every version it speaks rather than one. FIDO_2_2 is in the chart because the data contains it, not because it is a valid string:

Version declaredKeysShare
CTAP 2.0 (FIDO_2_0)32499%
U2F (U2F_V2)24374%
CTAP 2.1 (FIDO_2_1)19359%
FIDO_2_1_PRE17052%
CTAP 2.3 (FIDO_2_3)206%
FIDO_2_2, undefined93%

Teams usually assume PRF is the exotic capability and that credential storage comes with every key. It is the other way round. PRF is the safest thing on this list to build on, while the blob and management features that make an enterprise rollout comfortable sit below 50%.

Signature algorithms show the same concentration: ES256 (COSE -7) on 233 keys, EdDSA (-8) on 93, ES384 (-35) on 78 and RS256 (-257) on 6. Leave -7 out of pubKeyCredParams and you lock out most of the field.

7.2 Capacity varies the most#

Only 151 of 327 keys report their capacity at all. For the other 176 the metadata will not carry a rollout with discoverable credentials and you have to measure. Among the keys that do report, the values cluster tightly around 100:

69 of those 151 report exactly 100 slots, which has become the de facto standard for the mid-range. 8 keys stop below 26 slots and 13 go past 200, so a rollout sized on the median breaks at both ends.

7.3 maxCredentialCountInList and long allowCredentials lists#

maxCredentialCountInList limits how many credential IDs the authenticator accepts in one request. On login that is your allowCredentials list, and on registration the same limit applies to excludeCredentials, which is the list that stops a user from enrolling the same key twice. 222 keys report the field and the values sit close together, with one group of outliers that causes the trouble:

The nine keys with a limit of 1 are all HID Crescendo variants. 8 is the value on 108 of the 222 keys that report a limit, so just under half of them, and roughly a third of all 327 authenticators.

If a relying party sends more credential IDs than the limit allows, the client cuts the list into chunks of that size and sends one request per chunk. Nothing gets dropped, it simply takes several round trips. Browsers also probe those chunks silently first, with user presence turned off, so the extra requests do not each ask the user for something.

Over USB nobody notices any of this. Over NFC it depends on how long the card stays in the field: a placement powers the card for as long as it is in range, so 20 requests do not mean 20 taps, but they do mean the user has to hold the card still through 20 round trips instead of one. On a key with a limit of 1 that is the difference between a login that feels instant and one that feels slow.

Only a shorter list fixes this. Send an empty allowCredentials and rely on discoverable credentials, or reduce the list on the server to the credentials that plausibly belong to this user and this transport.

Substack Icon

Subscribe to our Passkeys Substack for the latest news.

Subscribe

8. How to check a key yourself#

The metadata tells you what a vendor submitted. Only the key in your hand tells you what is installed on it. Two commands are enough for that.

Start with fido2-token from libfido2, on macOS via brew install libfido2. It speaks plain CTAP rather than anything vendor-specific, so it reads FIDO2 keys from any manufacturer over the transports your operating system exposes to libfido2. The example below is USB HID. First list what is connected. On macOS the device path is an ioreg:// handle, on Linux it is a /dev/hidraw* node:

$ fido2-token -L ioreg://4296123542: vendor=0x1050, product=0x0407 (Yubico YubiKey OTP+FIDO+CCID)

Then read the getInfo response from that path. This is a YubiKey 5 NFC that has been in daily use for a while:

$ fido2-token -I ioreg://4296123542 proto: 0x02 major: 0x05 minor: 0x04 build: 0x03 caps: 0x05 (wink, cbor, msg) version strings: U2F_V2, FIDO_2_0, FIDO_2_1_PRE extension strings: credProtect, hmac-secret transport strings: nfc, usb algorithms: es256 (public-key), eddsa (public-key) aaguid: 2fc0579f811347eab116bb5a8db9202a options: rk, up, noplat, clientPin, credentialMgmtPreview fwversion: 0x50403 maxmsgsiz: 1200 maxcredcntlst: 8 maxcredlen: 128 maxcredblob: 0 maxlargeblob: 0 minpinlen: 4 pin protocols: 2, 1 pin retries: 8

On a YubiKey, ykman adds the state a user cares about. It is a short status summary rather than a second capability dump, so it complements fido2-token -I instead of replacing it:

$ ykman fido info AAGUID: 2fc0579f-8113-47ea-b116-bb5a8db9202a PIN: 8 attempt(s) remaining Minimum PIN length: 4

Note that ykman finds Yubico hardware only, because it filters the USB device list on Yubico's vendor ID. For a Feitian, Token2 or HID key you need fido2-token.

8.1 Comparing that output against the metadata#

Look up the AAGUID 2fc0579f-8113-47ea-b116-bb5a8db9202a in the BLOB and you get "YubiKey 5 Series with NFC". Line by line the two agree: the same two extensions, the same maxCredentialCountInList of 8, the same transports, the same algorithms. Two fields differ, and both differences are the ones this article warned about:

  • firmwareVersion. The record says 328706, the key says 0x50403. Yubico encodes the version as major, minor and patch bytes, so that is 5.4.2 in the metadata against 5.4.3 in the hand. The same encoding makes 329476 firmware 5.7.4 and 329728 firmware 5.8.0.
  • clientPin. The record says false, this key lists clientPin in its options and reports 8 PIN attempts remaining. That is exactly the section 3.1 rule at work: the submitted sample had no PIN set, this one does.

The output also shows where the capability tables run out. This key declares FIDO_2_1_PRE instead of FIDO_2_1, so it reports maxcredblob: 0, maxlargeblob: 0 and no capacity at all. Nothing is broken here. It is a working CTAP 2.0-era key. It sits in the 59% without largeBlobKey and in the 54% that report no remainingDiscoverableCredentials, so it contributes nothing to either number above.

If you cannot plug the key in, the AAGUID is enough to find the metadata entry. You read it out of the attestationObject from registration, specifically from the attested credential data inside authData, not out of the attestation statement next to it. That is also how a relying party identifies hardware in production.

StateOfPasskeys Icon

See how many people actually use passkeys.

View Adoption Data

9. How Corbado can help#

A capability table tells you what the hardware can do. It says nothing about which hardware your users hold or which of those models fails in your login. The AAGUID data from your own ceremonies answers both, and that is what Corbado Observe, our authentication observability layer, collects.

See Authenticator Inventory in Corbado Observe →
  • See the authenticator behind every ceremony. The inventory resolves the AAGUID to a model, so "our workforce uses YubiKeys" turns into a distribution with the long tail of Feitian, HID and Token2 keys that procurement never mentioned.
  • Find capability mismatches as an error pattern. WebAuthn failures are attributed by authenticator, OS and browser, so a largeBlob or credProtect assumption that only breaks on one model shows up as a cluster instead of as scattered noise.
  • Separate device-bound from synced credentials. The split is reported per segment, which is usually the number a regulated environment asks for.

If you roll out security keys next to platform passkeys, hardware passkey observability describes how the two populations differ in practice.

10. Conclusion#

None of this data was ever hidden. It sits in a signed file that the FIDO Alliance republishes whenever its contents change, with an authenticatorGetInfo response for every CTAP2 key it lists. It answers most of the questions that teams still answer by guessing or by buying a key and trying it out.

PRF sits at 94%, higher than most teams expect. CTAP 2.1 stops at 59%, credential blobs stay below half and reported capacity spans two orders of magnitude. Build on the 94% and degrade gracefully around the rest.

Two caveats apply to every row above. The metadata is a snapshot from the moment of submission, so firmware moves ahead of it. And the records are patchy: 54% report no capacity, 25% report no transports and 24% report neither. Read a key in your hand with fido2-token -I before you commit a design to it, and measure which AAGUIDs your users present before you commit a rollout.

Corbado

About Corbado

Corbado is the Passkey Intelligence Platform for large-scale CIAM teams running consumer authentication. We help you see what IDP logs and generic analytics tools can't: where passkeys, passwords, OTP, social login and fallback journeys succeed, stall or fail, which devices and browsers create friction, and when an OS update silently breaks login. Two products: Corbado Observe layers process mining and observability across authentication journeys. Corbado Connect adds managed passkeys with analytics built in alongside your IDP. VicRoads runs passkeys for 5M+ users with Corbado (+80% passkey activation). Talk to a Passkey Expert

Frequently Asked Questions#

Which FIDO2 security keys support the WebAuthn PRF extension?#

PRF on a security key needs the CTAP hmac-secret extension. 307 of the 327 roaming authenticators in the FIDO Metadata Service support it, which is 94% and makes it the most widely implemented CTAP extension. The newer hmac-secret-mc, which lets a key return a PRF result during registration, is much rarer at 20 of 327 keys.

How many passkeys can a FIDO2 security key store?#

The range is wide. Of the 151 keys that report remainingDiscoverableCredentials in their CTAP getInfo response, the median is 100. The OneSpan DIGIPASS FX1-C reports 662, the Swissbit iShield Key 2 reports 300 and the HID Crescendo Key V3 reports 30. The field is the key's own estimate of how many more credentials fit, calculated for maximally-sized fields, so read it as an order of magnitude rather than an exact slot count. Non-discoverable credentials are a different story: most keys wrap them into the credential ID and store nothing, so their number is bounded by your database instead.

What does bioEnroll false mean in a CTAP getInfo response?#

It means the key has a fingerprint sensor, but no fingerprint has been enrolled yet. In CTAP true means supported and active, and a missing option falls back to the default the spec defines for it, which for almost every capability option means not supported. false depends on the option: for the ones a user sets up, such as bioEnroll, clientPin, uv, ep and alwaysUv, it means supported but not configured, while for support-only options such as rk, credMgmt and largeBlobs it means not supported. Reading bioEnroll: false as no support is the most common mistake when interpreting getInfo, because it makes every YubiKey Bio look like it has no biometrics.

Why does a security key not accept my whole allowCredentials list at once?#

Every key states a limit in its getInfo response, maxCredentialCountInList, for how many credential IDs it accepts in a single request. The most common limit is 8. If a login sends more IDs than that, the browser has to cut the list into chunks of that size and ask the key once per chunk. Over USB nobody notices. Over NFC the card stays powered while it is in the field, so extra requests do not mean extra taps, but the user has to hold the card still for longer. Nine keys in the metadata, all HID Crescendo variants, have a limit of 1, so a list of 20 credentials turns one round trip into 20. The same limit applies to excludeCredentials on registration. Keep the list short, or send an empty allowCredentials and let the key use its discoverable credentials.

Where can I look up the capabilities of a specific security key?#

The FIDO Alliance Metadata Service publishes a signed BLOB with the submitted authenticatorGetInfo response for every FIDO2 authenticator it lists, keyed by AAGUID. For a key you can plug in, fido2-token -I from libfido2 prints the same response from the firmware that is actually installed, for any vendor. ykman fido info adds the PIN state of a YubiKey, but it is a status summary rather than a second capability dump.

See how Corbado fits your passkey rollout and existing authentication stack.

Explore the Console

Share this article


LinkedInTwitterFacebook