Passkeys with encryption, PRF-related keys, account-related keys

hi everyone,

I was reading here how the passkeys are used for encryption in bitwarden,

When a passkey is registered for encryption the following things are uploaded:

  • passkey public key
  • PRF public key
  • PRF-encrypted private key
  • PRF-encrypted account encryption key

Questions:

  1. is [PRF public key] unique per account?
  2. are we gonna have so many [PRF-encrypted private key] and [PRF-encrypted account encryption key] as passkeys have we have registered for vault encryption?

As I understand our account has a single encryption key pair in the system, the unique “private key” is used to encrypt the unique “account encryption key” and we encrypt the “private key” so many times as passkeys we have registered, the private key is encrypted symmetrically using as seed the deterministic 32 byte keys derive from WebAuthn PRF.

  1. is “account encryption key” a 32 byte key?
  2. where is the web client github repository the part where a passkey is registered for encryption?

Thanks

Hello and welcome to the community :waving_hand:

our account has a single encryption key pair in the system

I believe you hypothesize that an account has a single PRF public-private key pair. The PRF public key is used to encrypt the account encryption key (256 bits, 32 bytes for encryption, doubled with MAC key), which can be decrypted by the PRF private key. The PRF private key is encrypted by the PRF symmetric key (256 bits, 32 bytes). The PRF public key, the PRF-encrypted account encryption key, and the PRF-encrypted private key are stored on the server.

If so, I don’t think this can be done, because to create an entry for PRF login passkey, the client needs to have the PRF private key, which, if generated for a previously registered passkey, can only be retrieved by the prior PRF symmetric key. This symmetric key might not be available when that prior passkey isn’t used to log in.

If the counter-hypothesis is that each PRF passkey has its own PRF public-private key pair, then the answers may be:

  1. No, each PRF passkey has its own PRF public-private key pair.
  2. Yes, we’re going to have as many PRF public keys, PRF-encrypted private keys and PRF-encrypted account encryption keys as PRF passkeys.
  3. -
  4. The account encryption key is 512 bits, 64 bytes, counted with the 256-bit MAC key, as indicated in the security white paper.

Thanks for you the link the security whitepaper. Very clarifying and interesting.

If you don’t mind I have a few more questions.

1st) about 512-bits by HKDF and “master password hash” generation:

Based on the following quote, the “master key” is stretched to 512-bits by HKDF.

The resulting salted value is the 256-bit Master Key. The Master Key is then again stretched to 512-bits using HMAC-based Extract-and-Expand Key Derivation Function (HKDF), resulting in the Stretched Master Key.

but from this derivation of 512-bit only 256-bit is used for AES-256 bit encryption based on:

The Generated Symmetric Key is encrypted with AES-256 bit encryption using the Stretched Master Key and Initialization Vector.

where is the other 256-bit used? it is not used for the “master password hash” because based on the diagram and description the “master password hash” is derived from a 2nd KDF (PBKDF2/Argon2id).

Master Password Hash is generated using PBKDF-SHA256 with a payload of the Master Key and with a salt of the master password

2nd) PBKDF2 is applied twice:

I am imaging that we can’t use any derivation of this HKDF as “password manager hash” because bitwarden does not use SRP. It would be risky, because from this hash an attacker could eventually break the HKDF and get the “master key” and from there decrypt the “protected symmetric key”, so we need to apply again a 2nd PBKDF2/Argon2id. Right?

The first KDF used “email” as salt, but the 2nd KDF use “password”. This is not relevant, right? We could use “email” in both as salt.

3rd) CSPRNG generated: 256-bit encryption key & 256-bit MAC key

We need a MAC key because the encryption is done with AES-CBC 256 bit. If we would use AES-GCM 256 bit, we could get rid of the MAC key, because AES-GCM has already data integrity. Right?

From the diagram: MAC Key: 256 bits

but aren’t this 256 bit MAC key generated from CSPRNG? the CSPRNG ouputs (1) encryption key 256 bit, (2) MAC key: 256 bit, and (3) IV 128 bit

Somewhere in this security whitepaper is written that the HKDF derived a 512 bit key, but then the “stretched master key” is just 256 bit. Where are the extra 256 bit used?

Although this page is slightly outdated (and no longer publicly available), you can get the gist of how it works here:

The stretched master key (512 bits) is divided into a 256-bit encryption key and a 256-bit MAC key. This is not explicitly shown in the whitepaper schematic, but it is analogous to how the CSPRNG-generated symmetric key (512 bits) is divided into a 256-bit encryption key and a 256-bit MAC key. The MAC key derived from the stretched master key is used to verify that the protected symmetric key received from the server (or loaded from the local vault cache when unlocking the vault) has not been tampered with.

2 Likes

Aha, I understand now what the “PRF public-private key pair” means. I was mixing it up with the “user asymmetric key”. They are two different asymmetric key pairs. I didn’t understand why we were using PRF suffix in this context. The naming haven’t made sense til now.

So,

  • we have only one “user asymmetric key per user but it is used for other stuff, it does not encrypt the “vault encryption key” (prob it is the other way around),
  • we can’t encrypt the “vault encryption key” directly with the PRF secret because in case the “vault encryption key” rotation we are in trouble, so we need asym encryption to fix this,
  • I thought we had just one “asymmetric key”, where the public key was encrypting the “vault encryption key” and the private key was encrypted with the PRF secret symmetrically. So every time we would add a new passkeys its PRF secret would be used to encrypt the private key. The problem of this would be that if we need to rotate this “asym key” for whatever reason we are in trouble. Having 1 asym key per passkey makes straightforward every kind of key rotations. Very interesting.

So both “stretched master key” and “generated symmetric key” have a pair of “256 bit encryption key” and “256 bit MAC” key.

I think that “AES-256 bit Encryption” is executed twice:

(1) with “encryption key” of “stretched master key” and “generated symmetric key”, in order to have an encrypted “vault encryption key”,

and (2) with “MAC key” of “stretched master key” and “generated symmetric key” in order to have a “integrity key”.

We end with 2 “protected symmetric key” one for encryption and other for integrity. Right?

Actually, I would reword like: the MAC key derived from the stretched master key is the key used to encrypt the MAC key from CSPRNG, and the MAC key from CSPRNG is the one used to verify the integrity.

anyway very interesting and well thought all this ceremony of keys in order to get the zero knowledge encryption :clap: :clap: :clap:

I don’t think this is correct.

you were absolutely right, I think I got it now,

csprng generates a 512 bit block the (1) 256 bit vault encryption and (2) 256 bit vault mac key (used to check the integrity of the encrypted vault)

512 bit stretched master is divided in (1) 256 bit encryption key and (2) 256 bit mac key

the first half encrypt the whole 512 bit block generated from csprng, and the 2nd half check the integrity of that encryption

1 Like