However, it doesn’t work as expected as I’m unable to generate a master passwor hash that match the one on vaultwarden DB.
First of all I have few questions regarding how vaultwarden do create the master_password_hash saved on DB:
Because vaultwarden store the salt as a blob on SQLite DB I do extract it assuming it’s a hexadecimal string using: sqlite> select hex(salt) from users where email='user@acme.com';
Am I correct?
Same question for the master_password_hash
It looks like the official bitwarden client use a b64 encoded master_key string to generate the master_password_hash: https://github.com/bitwarden/clients/blob/master/libs/common/src/platform/services/crypto.service.ts#L256
Did I correctly understood the return of this hashMasterKey function?
Same question for the master_password_hash function.
Here is my current workflow, did I missed something or am I on the right path?
Create a master_key derivated from master_password and email using:
Trying to create a master_password_hash salted as on DB using DB information using:
Function: PBKDF2-HMAC-SHA256 Payload: master password hash Salt: random bytes → using string stored in DB transform as bytes array. iteration: 100 000 key length: 32 hash protocol and function: hmac-sha256
Now my issue is, when I do compare my locally computed master_password_hash it doesn’t match the one from DB.
I tried to use a b64 encoded master_password_hash as input instead of the string master_password_hash, it doesn’t match neither.
Here is a simple python snippet that I use to recreate/test this workflow for anyone interested: vaultwarden_sample (opendev.org)
Any help on this topic would be greatly appreciated, especially @nozza87 if you ever have your password recovery/brute force snippet to share.
If you look at the links that I’ve put on my first message, those are part of it and indeed what is disturbing as none of those models works if you try to implement them.
If you look at bitwarden clients source code for instance they do introduced a base64 return for the master key derivation that isn’t noticed anywhere on those papers unfortunately :-/
Vaultwarden uses byte representation instead of the plain text received of the master password hash received from the clients and uses PBKDF2 with 600_000 iterations on it.
If you would search this forum, there is some who tried the same for brute forcing a forgotten password.
Yes, I’ve seen that, it’s not specifically vaultwarden that use the byte representation, python’s PBKDF2 from cryptodome is also using that and I’m also used it as it to compute it.
However, I think there is something odd at the client side of bitwarden as if you simply code your workflow steps using what’s explain there, then it doesn’t work.
Their own source code seems to explain that they b64 encode the hash and return it, which then in turn vaultwarden do byte translate and iterate over 600k or 100k times + salt depending on your setting and vaultwarden server version. Mine is using 100k so far as stated on DB for few accounts that were created before the upgrade to 600k.
I’ve carefully read @nozza87 thread you’re mentioning too but unfortunately he didn’t responded yet.
I would really appreciate anyone looking at the client sources that have linked on my first link as I’m a bit lost on bitwarden monorepo and extensive TypeScript usage.
From my investigation it seems that:
1°/- They always return b64 encoded hashes from the derivation function, would it be for master key creation AND password hash.
2°/- Unfortunately when I try to do it that way too and then calculate the expected master_password_hash using DB salt and bytes representation I can’t get the same master_password_hash that the one stored on vaultwarden.
3°/- I’ll probably have to compile my desktop client from upstream sources with debugging modification in order to find out what’s the client is really sending to the server as the payload.
Thanks a lot at least for your kind answer and care about this topic that I really can’t get any satisfying answers anywhere :-/ Even Bitwarden communication channels doesn’t work, I really feel like if I missed something somewhere that everybody else out there already clicked xD
Also first step would be to see if you can generate the exact same hash as the clients return of course, so use the browser developer tools and see what it sends Vaultwarden.
Oooh didn’t knew about that tool, I’ll have a look thanks!
Yes the first step know that I validated that I can’t compute a matching hash will be to catch a client made master_password_hash from a compiled client with debug activated.
I’m kinda astonished that bitwarden even on its most recent responses keep telling people the workflow is ABC when indeed it seems to be A A’ B B’ C
Yes I did had a look at this page too but even from that specific page using my testing user the page can’t reproduce the expected hash, I think they didn’t updated it since a long time.
It works perfectly for me. At least when using PBKDF2. It will fail when using Argon2, since that is not (yet) implemented into that page. Also, check what the response is of the /identity/accounts/prelogin call, this will contain the values needed to generate the correct key, like which hashing algo and how many iterations.