Scrypt KDF Support

Hi,

discussion has been ongoing about other key derivation functions being added to Bitwarden.

The current KDF, PBKDF2 uses little to no memory, and thus scales very well on GPUs which have a comparatively low amount of memory per core. For long, high entropy, master passwords this is not an issue, but these days better hash functions, that limit this attack, are available. One suggestion has been to add support for Argon2, though the Javascript ecosystem support is lacking here. While there is a WASM implementation, it is unaudited, and might not work on some platforms. Another KDF that limits the amount of scalability through a large internal state is scrypt. For scrypt there are audited, and fuzzed libraries such as noble-hashes.

I have created basic scrypt support for Bitwarden. Instead of KDF iterations, there is a “Work Factor” which scales linearly with memory and compute. It has to be a power of 2, and thus I made the user configurable work factor a drop down selection.

Additionally, there are some other configurable factors for scrypt, which I did not make user-configurable, but set statically according to a post from the guy in charge of cryptography for Golang at google.

The code is on this branch. Still missing are: unit tests and an implementation for the mobile clients. Before writing the mobile client implementation, I wanted to get feedback on the current implementation :slight_smile:

6 Likes

Since I’m a new user on the forum I had to put these links in a second post due to a limit on new users:
Noble Hashes Library: https://github .com/paulmillr/noble-hashes
SCRYPT Parameters by Filippo Valsorda: https://words .filippo .io/the-scrypt-parameters/

I’m not familiar with scrypt, so please ignore if the following is a bad idea, but my suggestion would be from a usability perspective, to have the user input the base-2 logarithm of the work factor instead of the work factor itself (an inscrutable 5-7 digit number). Thus, you could keep the same input field format as currently used for PBKDF2, and just limit the valid inputs to integers the range 16-22. You could even take inputs in the range 1-7 (or 0-6) and transform these into a valid work factor in the range 216–222.

Also, what is your source for 216 as the minimum acceptable value? The two sources you linked above have 210 and 215, respectively, as the minimum.

1 Like

Yeah, good idea, I agree that the current selection is a bit unreadable. We could also just write “2^15” or “2¹⁵” instead of 15 (or 32768) so it is clear what the work factor is, but at the same time more readable.

About the minimum: To be honest I didn’t find any device where at 2^16, the hashing took any noticeable amount of time, so I’m not sure what the use-case for setting it lower would be, still I’m happy to decrease the minimum to 2^15.

Thanks @Quexten, I’ll review with the team :+1:

1 Like

Ok, as an update: I have now implemented scrypt for the mobile clients. The cryptographic library used, is BouncyCastle, the same one Bitwarden already uses on Android for other cryptographic functions. I had to add it to the iOS client as a dependency, since the Bitwarden client only used iOS native cryptographic functions so far.

I have tested it to correctly unlock the vault on Android. Since the iOS implementation is the same, it should also work but I have no way of testing the iOS app.

Here is the branch:

1 Like

Ok, I have added unit tests for the javascript implementation, and created the pull requests.
Here:

and

1 Like

Thanks for your interest Bernd, I definitely appreciate it!

My gut for a user experience is that we’d want to save difficulty options independent of one another for each KDF type we implement. If that’s the goal we would definitely want a KDFOptions object of some sort as part of the prelogin responses and subsequent crypto information responses. As for server-side storage, this would mean either a new table relation or storing it as JSON. I’m leaning to JSON for two reasons.

  1. As you point out in the feature thread each KDF is likely to have unique requirements on options, so we’d need a very generic table structure if we went that route
  2. New KDFs are a rare occurrence so the higher migration cost isn’t a big deal
2 Likes

As commented on the scrypt pull-request, here is the draft-pull request enabling the back-end functionality for the KDF options. Since as you pointed out new KDF’s don’t occur every other day, I chose to simply add 2 new columns, one for kdfMemory, one for kdfParallelism instead of a JSON object, but the pull request is of course open for discussion :slight_smile: :

The scrypt pull request is closed for now as we decided to focus on argon2.

2 Likes

From what I understand that is a good decision.