Not sure why this announcement has not been pinned in the Community Forum, but the following new feature (arriving with the 2022.10 release) will offer an additional alternative to the methods described in this thread:
For purposes of risk/benefit analysis, how does the hashing/encryption process differ from what is done in the regular encrypted export? Does the user-supplied password directly act as the Generated Symmetric Key (a.k.a. account encryption key), or is any hashing done to stretch the entropy of the user-supplied password (and to impose time-penalties for brute-force cracking attempts)?
Regarding password protected exports, the key is generated through pbkdf2 and stretched using hkdf.Expand to provide an encryption and mac key parts. Regarding brute force difficulty, kdf_iterations is currently hard-coded to 100,000, which is the same default for a Bitwarden account and Bitwarden Send.
This is a permalink to the 2022.10 web release where the encryption key is being generated. You can see there that there is indeed a salt used to generate these keys – you really shouldn’t make password-based keys without them, after all. The salt is random per-export and stored right in the exported file. You can open your export up and read it in any text editor (but don’t change it!). It has this format:
encrypted: true, // whether this is an ecrypted export
passwordProtected: true, // whether this is password protected or account protected
salt: salt, // the salt used to generate the encryption key
kdfIterations: kdfIterations, // the number of kdf iterations to use in generating the key. constant for now
kdfType: KdfType.PBKDF2_SHA256, // the kdf used. constant for now
encKeyValidation_DO_NOT_EDIT: encKeyValidation.encryptedString, // A short string encrypted with the same key. Used for key validation so we don't have to decrypt data unless your key is valid
data: encText.encryptedString, // your encrypted data
You should be able to drill down into the makePinKey method to answer other questions you have, but feel free to keep posting here and I’ll help out . In general, this all uses very standard encryption stuff in our cryptographic toolbelt – no reinventing the wheel, just some format definition and better understanding of what users want in exports.
Edit: Forgot to mention. IV is indeed randomized. It’s buried two levels down in the makePinKey implementation.
Thank you for the detailed explanation and link to the source code – this is extremely helpful! The main purpose of my questions has been to get information that would help me determine the following:
Whether a password-encrypted vault requires any trade-off in security relative to the standard encrypted export, in exchange for the benefits of portability/flexibility.
Whether using one’s Bitwarden master password as the password for the new password-encrypted exports creates any risk exposures for the master password.
So far, it seems that with regards to #1, if an attacker has acquired a copy of a regular encrypted export, there are no stored key hashes, salts, or iteration counts, so their only brute-force option would be to guess the 256-bit encryption key. In contrast, if an attacker as acquired a copy of the new, password-encrypted export, the risk of them cracking it seems similar to the risk of an attacker cracking an exfiltrated data.json file — in both cases, the salt and KDF iteration count are available in plaintext, and each brute-force guess would require calculation of the PBKDF2-SHA256 hash. The password-encrypted export would be easier to crack than the exfiltrated vault if and only if the user has set the number of KDF iterations to a value larger than 100,002.
With regards to #2, it seems that the master password would be at a somewhat increased risk of being brute-forced if it is used as the password for a password-encrypted backup, if and only if the user has set the number of KDF iterations to a value larger than 100,002 (for the same reasons as stated above).
I would greatly appreciate if you could confirm or refute my analysis above!
Details on your analysis #1
While you’re right, I want to say that it’s a relatively academic question as long as you have a strong password.
##Salts and KDF iterations
It is generally accepted that salts and difficulty are not considered secrets. These exist to negate rainbow tables and to tweak hashing difficulty. Due to this fact, there is an unathenticated endpoint to get KDF iterations (identity/account/prelogin) for an account given the email and the salt for an account is not a secret – it’s just the account’s email. If someone got ahold of your regular encrypted export, they likely know your email, in which case they’re equally secure.
I would recommend not using your master password for the encrypted export for the simple reason that it’s generally a bad idea to reuse passwords.
Agreed – I’m all about those academic questions, though!
I’m unclear on the above statement. If, say, someone stole or found a USB flash drive that contained “regular” encrypted exports, how would the thief/finder be able to find out the associated email address? It does not appear to be stored in the .json.
That is the conventional wisdom, but the rationale for this advice is that this protects against credential stuffing attacks. With a sufficiently strong password, both the vault and its backup should be uncrackable (by brute force), so there would be no practical risk that the master password is cracked from a backup and then used to access the vault (or vice versa). Am I missing something in this risk analysis?
Its great to have this feature !!
Appreciate the devs effort to bring this to the gui client.
I am guessing this was earlier only possible through cli , so only a few people made efforts to keep an encrypted export locally.
This will allow users to get more control over their data and would make them worry less about they getting locked out of their vault, or are unable to access their vaults for whatever reason.
Also i just tried creating an exported backup , but against the 'Kdf-type ’ field i am seeing the value as “0” and not PBKDF2_SHA256 as shown in the above code. Though i can see the number of iterations as 100000 , so i hope the backup went right.
I’m guessing that the number 0 is an enumerated encoding that represents the KDF algorithm (0 = “PBKDF2_SHA2”). I know that the cipher strings in the encrypted vault start with the number 2. to represent the encryption algorithm (AesCbc256_HmacSha256_B64), so it is probably the same idea.
Edit: I think I found the code that confirms the explanation above: