FIDO2 / WebAuthn: Only one OnlyKey registered, but two OnlyKeys work -- why?

Please help me understand something related to WebAuthn authentication using two hardware OnlyKeys:

  • I have configured Twitter to use my primary OnlyKey as hardware key for 2FA. I can use my primary OnlyKey without problems to log into Twitter (by entering username and password, then touching the unlocked OnlyKey).
  • According to this Twitter blog entry, Twitter uses FIDO2 / WebAuthn for authentication with a hardware key.
  • To my understanding (see here for a related query), FIDO2 / WebAuthn authentication is specific to the exact hardware key used. In other words, two hardware keys, even when one of them is a backup of the other, must be registered separately and are not usable interchangeably for authentication. There is also an interesting article by Yubico related to that.
  • However, I noticed that I can also use my secondary / backup OnlyKey for successful authentication to Twitter. I have only registered a single OnlyKey with Twitter, the primary one.

How can this be explained? I would like to understand what exactly makes the two OnlyKeys interchangeable for FIDO2 / WebAuthn. (For instance, is there a secret key used for Webauthn which is identical for both of my two OnlyKeys, primary and backup?)

(I am not sure if this is relevant here at all, but I have set the HMAC slot 1 private key (not using the pre-set random one). It is identical in both OnlyKeys, as one is a backup of the other.)

Update: Same behavior on Github: I can use both OnlyKeys for authentication, although I only have registered one of them. AFAIK Github also uses FIDO2 / Webauthn.

Update 2: HMAC secret key does not seem relevant. I reset it and can still log into Twitter and Github.

When you restore backup of one OnlyKey to another OnlyKey it is essentially a copy. The articles mentioning that you can not clone or copy a security key are overstated. There is a lot of information related to FIDO2 that is marketing related, such as the credential is unphishable, this is only partially true. In the most simplest terms a FIDO2 credential requires a private key and a counter. If you copy those values to another key you have an exact copy that will work just as the original.

Thanks Tim. Two follow-up questions:

  1. Where on the OnlyKey is the private key for FIDO2 / WebAuthn stored? Is it in any of the (RSA / ECC) slots? Can it be overridden by the user through the CLI?
  2. If a counter is part of the FIDO2 / WebAuthn authentication process, this suggests that the two OnlyKeys in general cannot be used interchangeably for it, but rather just right after the backup, when the counter of the two keys holds the same value. Is my understanding correct?
  1. FIDO2 resident credentials can be viewed and removed through the CLI - OnlyKey Command-Line Utility | Docs

  2. FIDO2 only requires that the counter be higher than the last used counter. If the OnlyKey app is used it is able to increment the counter on either key. This works well for backup/restore scenarios where one key is used regularly and the other is reserved as a backup.

Sorry, I don’t understand what you mean here. When does the OnlyKey app increment the counter? Could you please explain some more as this is an important detail I think.

The OnlyKey app uses a derivative of the unix epoch time to ensure that the counter is always higher than the last counter. This is the same method used by Trezor U2F. Keep in mind that the counter with FIDO2 is not like the counter with HOTP, there is no window or range of acceptable counters.

Ok, I think I get the idea now, please correct me if I’m wrong:

  • The FIDO / U2F / FIDO2 specifications do not give an exact specification for the counter used in the authentication process. Each vendor implements their own version.
  • As you wrote, OnlyKey chose to derive the counter from Unix epoch time. Additionally, if the OnlyKey app is installed on the computer that the OnlyKey is used, the OnlyKey app resets the counter to (a derivative of) the current Unix epoch time when the key is unlocked. (Will the web app at https://apps.crp.to also do this?)
  • This ensures that the counter is strictly increasing for a OnlyKey (and any hardware backups thereof). Due to these special semantics, an OnlyKey all its backups can be used without any issues for FIDO / U2F / FIDO2 authentication.
  • As a consequence, it suffices if only one OnlyKey (and none of its backups) be registered as hardware key for an account.

One thing that I noticed though is that when I tried registering my backup OnlyKey to both Google and Github, the behavior was different:

  • Google did not let me register my backup OnlyKey (“Security key already registered”).
  • Github let me register my backup OnlyKey. (Thinking that Github does not check for duplicate keys, I tried re-registering both keys yet another time, but that did not work.)

So for some reason Google thinks both keys are identical, and Github doesn’t. How could this be explained?

simply said the duplicate check doesnt directly happen by the RP but by the FIDO device, however for that to happen the RP needs to provide the currently registered credentials. if not done it wont work, and U2F-> webauthn chaos is just another thing.

You mean the reason why Google and Github behave differently here lies in whether they provide the credentials to the FIDO device or not for duplicate checking on the hardware key?

Replacing the FIDO private key (used to generate keys for all services that do not require a resident key) can be performed by resetting the FIDO application.

I suspect that Google and GitHub accounts have different behaviors because they use different implementations for security keys. According to my guess, Google uses CTAP1 (FIDO U2F) because it does not need to provide the FIDO2 PIN when registering; and GitHub must use CTAP2 because it supports the registration of internal authenticators such as Windows Hello. I’m not sure if the different behaviors between these two accounts are caused by the difference between CTAP1/CTAP2, or how the different behaviors are caused by this.

ctap2 can be used without PIN as well. they do use U2F/CTAP1 still in a way though because they iirc still use the app ID stuff.

My guess for Google is actually based on the fact that I would not be required for a PIN to register on Windows 10 machine, Windows 10 requires the PIN for every CTAP2 registration no matter how the relying party set the User Verification preference.