Discussion about U2F vs FIDO2 passkeys for Bitwarden Two-Step Login

Ah, okay. I think I was confused also, because the ones used for login-with-passkey as they also lead to a (more or less) 2-step-login (initiate the passkey + PIN/biometrics).

That’s right - but I guess you didn’t mention another case, where it might be confusing for the end-user also: with a discoverable credential you can get also usernameless login, and with a non-discoverable you can’t. So again, I guess it can be confusing for the end-user also, why some of their “passkeys” make also username-less login possible and other “passkeys” don’t work like that.

And here I still think, it would be easier in the end, if it wasn’t called the same, when it isn’t. :thinking:

Yeah and I can see that in part also. But still have my doubts. :rofl:

True, and (to add more to the confussion), the fact that a Passkey is discoverable does not mean that the login flow will be usernameless. It wasn’t with Google Passkeys until recently.

And if you want to add even more to the confusion, take login-with-passkey on the bitwarden web vault: if the authenticator and browser you use do not support the necessary bits for vault decryption, the login flow will ask for your master password after using the Passkey.

This Passkeys thing is a bit of mess. Impossible to understand for a non technical user.

:confounded:

2 Likes

As a non technical user, let me summarize what I have understood. On Bitwarden with a Yubikey, I have these three options:

  • “login-with-passkey” discoverable credential i.e. nameless
  • “WebAuthn” 2FA uses FIDO2 non-discoverable credential
  • “Yuibkey OTP” 2FA with a factory key

@th6mas In general, yes. Maybe two additions:

Technically, there is (at least) a fourth method:

  • for TOTP: storing the TOTP seed code for Bitwarden-2FA on the YubiKey and using Yubico Authenticator to access it

And to your second point: I guess the question remains, how Android or Windows Hello (and others?) store the Bitwarden-FIDO2-2FA-credential (and whether it can also be stored as a discoverable credential?) (@grb raised that question)

1 Like

Yes, username-less (and sometimes passwordless).

…except for when it (sometimes) uses a discoverable, resident credential. :man_shrugging:

Technically an available option, but largely a deprecated protocol (and only available to Premium subscribers).

Then there is a fourth option as already pointed out by @Nail1684:

1 Like

[context: Bitwarden’s FIDO2-2FA] Is this a fact or an assumption as of now? - I guess “we” assume at the moment, that e.g. Android (= Google Password Manager, on the device and/or Google account/Cloud) and Windows Hello can’t store non-discoverable credentials (and therefore store it then as a discoverable credential), right? But was there “evidence” (for both***), I already mixed up or forgot? :thinking:

*** “both” meaning again: 1. that those really can’t store non-discoverable credentials and 2. that “therefore” Bitwarden takes the route of alternatively storing it’s dedicated FIDO2-2FA credential as a discoverable credential

My chain of logic is as follows:

  1. Bitwarden 2SV passkeys (to borrow @kpiris’s terminology) can be created and stored in an Android passkey wallet, based on your observation.
  2. Android passkey wallets are syncable (via Google cloud).
  3. Syncable passkeys must be resident credentials.
  4. Resident credentials are also discoverable.
  5. Ergo, Bitwraden 2SV passkeys can be discoverable FIDO2 credentials.

Perhaps one (or more) of my assumptions is flawed, but enumerating the antecedent of my claim should make it easier to identify any premises that may be invalid.

This, I believe, is not true (the must part).

I have passkeys on my bitwarden vault that are non-discoverable (discoverable and resident, IINM, are synonyms here).

And, by the fact that they are on a password manager vault, I would consider them to be syncable.

@grb Just a short first reply: to be bold, I have my doubts about your first and second points. - If I have time, I will test this. - I mean, we had that discussion before, about where a credential get’s stored on Android (the device itself? - is Google Password Manager always involved? - if Google Password Manager is involved, it can be “offline” or “synced with the Cloud”)…

It should be easy to prove your points, because then (the complete chain along to the end) a passkey should “manifest itself” in my Google account. :sweat_smile:

PS: To the last point: as you @grb also mentioned, I did this before as I tested it (Android phone as FIDO2-2FA for Bitwarden) some time ago - and I can’t remember, seeing it in my Google account then. I can’t remember, seeing that credential anywhere on my Android phone. :thinking:

Just to this and to @grb 's post before: I see you fall victim to your own “loose” terminology now. :rofl: (and maybe another disadvantage of using the term “passkey” for discoverable and non-discoverable credentials, altough it is not the official definition)

1 Like

Are you referring to the 2SV passkey from your Yubikey?

My terminology may be inaccurate here, because I’m recalling some obscure technical details from memory, but my understanding is that for nonresident keys, there is a private key (on the Yubikey) that cannot be synced or otherwise extracted (except for by sophisticated attacks like YSA-2024-03), and then there is a public key that is stored by Bitwarden to allow for two-step login.

What I had meant in Point #3 was something to the effect that the private key part of a syncable passkey must be a resident credential.

Starting to get a headache now, so I’ll leave it to the two of you to determine whether my logic is valid or not! :face_with_head_bandage:

A good site for testing this kind of things is webauthn.io.

With the Advanced Settings button you can specify different credential options (including the desired discoverability).

1 Like

No, I’m referring to passkeys that I use for 2SV on different websites (and that, as I said, store on my bitwarden vault).

I think that is incorrect. A passkey (well, a fido credential if we want to be precise) can be:

  1. device-bound and non-discoverable
  2. device-bound and discoverable
  3. synced and non-discoverable
  4. synced and discoverable

Both properties (syncability and discoverability) are independent of each other.

And, edit to add:

This is how I think each of those two properties get set:

The discoverability of the credential is requested by the website (aka. relying party) as required, preferred or discouraged. But ultimately decided by the authenticator (the yubikey, the bitwarden client, the android or apple device, etc.

The syncability is dependent of the authenticator used to store the credential (eg. hardware key ==> device bound, bitwarden client ==> synced).

Isn’t this definition already problematic, as with a non-discoverable credential, there is no credential stored in the authenticator?

Do you have a source about the syncability of non-discoverable credentials? Because I wondered these days already, what even get’s synced there, when “nothing” is stored. Only thing I can think of is, that that what is stored on the server side get’s synced? But again, is that a theory or is that done in the field?

PS: But Bitwarden (the authenticator) is not the “server” here (in the example, that I would store a non-discoverable credential for a given account/service in my Bitwarden vault)… So what or where would the sync be? - I’m confused again? :rofl: → PPS: To that: if you replaced “Bitwarden” in my example with “YubiKey”, it would be a bit clearer… but with the YubiKey it would be right from the start “hardware-bound” and not syncable… but in theory, the question would be the same: how would the “server” (which server then?) do the sync (hypothetically speaking in regard with the YubiKey)?

I’m no expert, but as I understand it, this is (very high level and simplified; and possibly something is wrong) how the cryptographic part works:

With a discoverable credential, a key pair is created and stored in the hardware key, and the public key is sent to the website.

With a non discoverable credential, a key pair is derived from the fido master key of the hardware key. The public key is sent to the website and stored there. The private key is not stored in the hardware key, it is derived from the fido master key and the public key that the website provides to the hardware key when the client tries to authenticate.

This is with hardware keys, with something like a password manager, the flow is the same, but I guess that this fido master key is randomly created for every credential (i would not think it gets reused, there is no need).

So, there is always a private key to sync.

Not really, only the fact that this item I just created contains a non-discoverable credential and is, or better, was stored on my vault:

{
  "passwordHistory": null,
  "revisionDate": "2024-10-20T19:45:35.840Z",
  "creationDate": "2024-10-20T19:45:35.536Z",
  "deletedDate": null,
  "object": "item",
  "id": "2c961731-c731-4bea-9968-b20f0145a20f",
  "organizationId": null,
  "folderId": null,
  "type": 1,
  "reprompt": 0,
  "name": "webauthn.io",
  "notes": null,
  "favorite": false,
  "login": {
    "fido2Credentials": [
      {
        "credentialId": "1c1e4e16-9b8e-460a-a744-c7845575de2e",
        "keyType": "public-key",
        "keyAlgorithm": "ECDSA",
        "keyCurve": "P-256",
        "keyValue": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQguA5NTJOcytndET7TE9qDSCL4LIMjZFxTayGO1XQRJ_6hRANCAATu4V7G0qi4HmftIU9CuY6oePrlJ6JqfUFr9iJC2yEmSqKoUjf8tFRkb8VBNGdlz5DcRaP1MUJqDpWmDfAKCMcM",
        "rpId": "webauthn.io",
        "userHandle": "a1cyLW5jZDZUeFh6amp2T19Ud3A2QS0tc0twLVpUMlFadjRmd1A4cUtZSQ",
        "userName": "example_username",
        "counter": "0",
        "rpName": "webauthn.io",
        "userDisplayName": "example_username",
        "discoverable": "false",
        "creationDate": "2024-10-20T19:45:35.616Z"
      }
    ],
    "uris": [
      {
        "match": null,
        "uri": "https://webauthn.io/?regUserVerification=discouraged&attestation=none&attachment=all&algES256=true&algRS256=true&discoverableCredential=discouraged&regHints=&authUserVerification=discouraged"
      }
    ],
    "username": "example_username",
    "password": null,
    "totp": null,
    "passwordRevisionDate": null
  },
  "collectionIds": []
}

Without question; even as someone who has studied passkeys, I find them difficult to understand.

Uniform definitions, consistent work-flows and interoperability certification would go a long way to solving this. And, it all begins by embracing the FIDO Alliance definition:

Put another way, a passkey is intended to encompass the entire authentication ceremony. It is not intended to replace just the password (as in the non-resident case), and it is not intended to replace just the 2nd-factor.

If one elects to use a key in a way that does not conform with the Alliance’s definition/workflow it seems they should be prohibited from using the word “passkey”, given that the Alliance “owns” the word.

My doctor goes bonkers when I claim a cold and the flu are the same thing, just with different durations. There is no question I am wrong, but from my perspective, in both cases, I stay home, feel like crap and make soup. So why should we have two names? Because, technically, they are completely different “under the hood”. Same argument applies with “FIDO passkeys” vs “Bitwarden passkeys”.

2 Likes

So what you posted there was actually from an export of your Bitwarden vault (and not only what the webauthn.io site showed you)? - I really don’t know enough about this, and I never looked into a (passkey-) export like this, but can it be that this data is similar to a “complete” passkey (= discoverable credential), just with

“discoverable”: “false”

?

(as I stated I don’t know it, so this is an honest question)

Correct, you can get a bitwarden item in json format from an export or from bitwarden cli (what I posted is exactly the output of the following cli command:

bw get item 2c961731-c731-4bea-9968-b20f0145a20f | jq

jq only makes the output a little prettier.

Discoverable and non-discoverable credentials inside the vault look exactly the same with just that diference (the value of that discoverable attribute).

This is a discoverable one:

{
  "passwordHistory": null,
  "revisionDate": "2024-10-21T12:29:28.300Z",
  "creationDate": "2024-10-21T12:29:27.973Z",
  "deletedDate": null,
  "object": "item",
  "id": "15468098-4f8a-4ebf-b299-b21000cdd8ec",
  "organizationId": null,
  "folderId": null,
  "type": 1,
  "reprompt": 0,
  "name": "webauthn.io (discoverable)",
  "notes": null,
  "favorite": false,
  "login": {
    "fido2Credentials": [
      {
        "credentialId": "bb7844f6-15b4-4c6e-a391-e4c09c277513",
        "keyType": "public-key",
        "keyAlgorithm": "ECDSA",
        "keyCurve": "P-256",
        "keyValue": "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQggIKdTKRh3rBjNAOCnFzYylw3vVb0qBn7eua7Kg0WTEyhRANCAAQHiYYej53bpuA6svRfoLY343rd357SvWq1hC5QZcrxTquUb3bs5aBY9arlNoSi2zBKaOiF3HEQgXSFnFJFhJkw",
        "rpId": "webauthn.io",
        "userHandle": "eXgxSlkwQXNUaEhxc3poa3dHell2TExKdng0RFgtUUVnRGd1LWtBSGpOaw",
        "userName": "example_username",
        "counter": "0",
        "rpName": "webauthn.io",
        "userDisplayName": "example_username",
        "discoverable": "true",
        "creationDate": "2024-10-21T12:29:28.250Z"
      }
    ],
    "uris": [
      {
        "match": null,
        "uri": "https://webauthn.io/"
      }
    ],
    "username": "example_username",
    "password": null,
    "totp": null,
    "passwordRevisionDate": null
  },
  "collectionIds": []
}

Non-discoverable credentials, I guess, make more sense on hardware keys (where the storage available for the cryptographic keys and the data associated with them is very limited).

With a password vault that limitation does not exist.

But, as they are in the FIDO specifications, password managers have to support them.

1 Like

@kpiris Interesting… so in effect, one could say, Bitwarden stores every FIDO2 credential as a passkey (PS: and I mean by that of course as a discoverable credential)… discoverable and non-discoverable alike (but both marked as such). Right?!