I recently discovered a significant difference in the performance of Argon2id in Bitwarden as compared to KeePass for the same key settings. I’m guessing this might have something to do with browser constraints, but I thought I’d ask the community.
For example, I thought it would be a good idea to make the key settings in Bitwarden match the settings I use in KeePass. KeePass has a feature that will select settings based on a benchmark of a 1 second delay.
Using this feature on one of my systems, I get the following:
Iterations = 56
Memory = 64MB
Parallelism = 2
But when I set these same values for Bitwarden, I thought I had locked myself out at first. Instead of 1 second, it significantly longer to login to my vault. I’m talking about ~10 minutes or so as compared to 1 second in KeePass.
I had to dial back the settings in Bitwarden obviously.
For contrast, my Bitwarden settings that are “tolerable” are:
Iterations = 3
Memory = 64MB
Parallelism = 4
Any comments on this? Tips for improving this with higher settings, hopefully closer to KeePass?
First of all, please specify which version of Bitwarden you are using, and what device/operating system?
Bitwarden’s Argon2id implementation should be speeding up (by a factor of 2 or so, I believe) with version 2023.7.1.
Furthermore, the Parallelism factor has no effect except on Android/iOS devices.
Additional insight can most likely be provided by @Quexten. Most likely, any performance differences are caused by the restriction to use the WebAssembly library.
Recently, my SIMD PR got merged which made things 2x (not quite) as fast as before on SIMD enabled systems as grb mentioned. Compilation with a newer compiler would have made things even 10% faster but was out of scope for the PR. Parallelism is sequential in the web / desktop version atm. It’s technically fixable but only with severe effort as multithreading in wasm is a bit of a hack atm. In total the perf difference (at parallelism 1) should be about 20-40% ( from what I recall) excluding the time taken to prepare the argon2 runtime. At parallelism 2 the wasm is 2x slower, on 4x it is 4x slower (due to being run sequentially).
Regardless, when circumventing the limits I get ~4 second kdf time for Iterations = 56 etc. in Firefox with SIMD, without SIMD it’s around ~9 seconds. Working parallelism would speed it up to around half that respectively.
One bug I did discover, is that the rather complex loader of the argon2-browser library has a codepath that leads to the Desktop build not using the SIMD implementation, even though SIMD would be available. I’ll fix that at some point.
But interestingly I’m not seeing the same extremely poor performance now. Has something changed? The version info looks the same but its a very different experience today with these settings.
Perhaps the size of the vault or some other aspect of one vault over another is a factor.
My comment above is true for a demo/lab vault I use. It has almost nothing in it.
But I just change the Argod2id iterations to 56 again on another “production” vault I use and the extremely poor performance persists there.
I’m timing the length of time to unlock it now, already over 2 minutes…
UPDATE: It took just over 3 minutes using 56 iterations. Not the ~10 minutes I originally reported and I admittedly didn’t time it before. But I will say it seemed much longer than what I’m seeing today.
When I did this before, I even walked away from my computer and when I returned, it was still trying to devrypt the vault.
I have confirmed that you can set the iterations to a value higher than the specified max=10 if one types the number into the field instead of using the up-arrow to increase the value (using the up-arrow, the value cannot be incremented past 10 iterations).
Interestingly, if entering a value smaller than the specified min=2 (i.e., 1 iteration), then there is an error message (“Argon2 iteration minimum is 2”) when clicking Change KDF after entering the Master Password. Such an error is not shown when surpassing the max.
Edited to Add: Seems like some code of the form
else if (kdfConfig.iterations > 10) {
throw new Error("Argon2 iteration maximum is 10.");
}
needs to be added at the end of Line 432 in crypto.service.ts. Beyond that, shouldn’t these input conditioning checks use the min and max variables that are defined in change-kdf.component.html — or even better, as constants defined in kdf-type.enum.ts — instead of hardcoding the numerical values of these limits?
Decryption time is different from kdf time and not dependent on kdf setting (iterations, memory, pbkdf2 / argon2), even if from the users perspective it’s one step.
Edit: If you plan to do a PR to fix this, please also note that the Cancel button doesn’t work on the Change KDF confirmation screen (below the input field for the Master Password). You might want to look at that, as well.
Please (temporarily) set your KDF to 100000 iterations of PBKDF2-HMAC-SHA256, then time the unlock delay on your large production vault. The try it again with Argon2id, using the minimum settings for memory (16 MiB) and iterations (2 iterations). How do these unlock times compare to what you had measured for 56 Argon2id iterations?