bcrypt vs pbkdf2 for encrypting private keys
Asked Answered
P

1

10

I'm building an application in which a password is used on the client side to encrypt a private key of a elliptic curve key pair. Then the password is bcrypted and sent to the server (along with the encrypted private key) and the public key.

Originally, I was using pbkdf2 to hash the password before encrypting the private key, but since I'm also bcrypting the password, could I use the bcrypted one instead?

According to https://medium.com/@mpreziuso/password-hashing-pbkdf2-scrypt-bcrypt-1ef4bb9c19b3#.sj4jcbynx the answer is not only yes, but bcrypt is even better as it's more GPU-ASIC resilient. Anything I'm missing?

Parakeet answered 12/11, 2016 at 19:49 Comment(4)
DRAFT NIST Special Publication 800-63B Digital Authentication Guideline suggests that PBKDF2 be used for password derivation.Outland
It is not clear what you are doing. 1. Where is bcrypt used. 2. If you are sending the bcrypted password you will not have the password to decrypt the EC key. 3. Rework the question in execution order step by step: 1. do this, 2. do this, 3. etc..Outland
Insofar as I can make sense of this, you're asking if you can send over the key used to encrypt some plaintext alongside the ciphertext derived from it. Not exactly secure.Inclined
what's the purpose of sending bcypted password to server side? Without your original password, the server can not decrypt the encypted private key even with the bcypted password. The server can only use the bcypted password to validate the correctness of an inputed password. However, with the original password, you can decrypt the private key without the bcypted password. Why leaking suck important info to the server?Punitive
A
16

You should not be using the bcrypt hash output as an encryption key; it is not meant to be key material:

  • BCrypt is not a key-derivation function
  • BCrypt it is a password storage function

You have an elliptic curve private key that you want to encrypt using a user's password. Of course you don't want to use the password directly - you want to use the password to derive an encryption key. For that you can use:

  • PBKDF2
  • scrypt

These are both key-derivation functions (e.g. password-based key derivation function). Their purpose is to generate an encryption key given a password. They are designed to be "hard".

You feed both these algorithms:

  • a password
  • cost parameters
  • salt
  • desired number of bytes (e.g. 32 ==> 32 bytes ==> 256 bits)

and it returns you a 256-bit key you can use as an encryption key to AES-256.

You then want to backup the user's key

I gather that you then want to:

  • store the encrypted elliptic curve private key on your server
  • store a hash of their password on your server

And your question was: since you already ran their password through "a hashing funtion" can't you just use that hash as their stored password?

No! That hash is also the encryption key protecting their private key. You don't want that private key transmitted anywhere. You don't want it existing anywhere. That 32-byte encryption key should be wiped from memory as soon as you're done with it.

What you should do, if you also wish to store a hash of the user's password is use an algorithm that is typically used for password storage:

  • pbkdf2 (a key-derivation function abused into password storage)
  • bcrypt (better than pbkdf2)
  • scrypt (a key-derivation function abused into password storage; better than bcrypt)
  • argon2 (better than scrypt)

Update: Argon2/Argon2i/Argon2d/Argon2id is weaker than bcrypt for password authentication (read more)

You should separately run the user's password through one of these password storage algorithms. If you have access to bcrypt; use that over pbkdf2. If you have scrypt, use that for both:

  • derivation of an encryption key
  • hashing of the password

The security of your system comes from (in addition to the secrecy of the password), the computational distance between the user's password and the encryption key protecting their private key:

"hunter2"  --PBKDF2--> Key material
"hunter2"  ---------bcrypt-------> Key material
"hunter2"  ----------------scrypt----------> Key material

You want as much distance between the password and the key.

Not-recommended cheat

If you're really desperate to save CPU cycles (and avoid computing scrypt twice), you technically could take:

Key Material ---SHA2---> "hashed password"

And call the hash of the encryption key your "hashed password" and store that. Computation of a single SHA2 is negligible. This is acceptable because the only way an attacker can use this is by trying to guess every possible 256-bit encryption key - which is the problem they can't solve in the first place. There's no way to bruteforce a 256-bit key. And if they were to try to brute-force it, the extra hashed version doesn't help them, as they could just test their attempt by trying to decrypt the private key.

But it's much less desirable because you're storing (a transformed) version of the encryption key. You want that key (and any transformed versions of it) stored as little as possible.

To sum up

  • generate EC key pair
  • encryptionKey = scryptDeriveBytes(password, salt, cost, 32)
  • encryptedPrivateKey = AES256(privateKey, encryptionKey)
  • passwordHash = scryptHashPassword(password, salt, cost)

and upload

  • encryptedPrivateKey
  • passwordhash
Ascend answered 16/11, 2016 at 21:42 Comment(11)
Yeah... Of course I don't want to use the same hash for encrypting the private key and authentication. I had a big "doh" moment shortly after posting this question and writing down this design. Doh!Parakeet
I think the answer could be made a little bit more clear if it emphasizes you need to use different salts for the encryptionKey and passwordHash.Chokeberry
you can derive 64 byte encryptionKey,use the first 32 byte as the key of aes256; the hashing (the remaining 32bytes + ciphertext) as passwordHash. encryptionKey = scryptDeriveBytes(password, salt, cost, 64); encryptedPrivateKey = AES256(privateKey, encryptionKey[0,31]); passwordHash = sha3(encryptionKey[32,63]+ encryptedPrivateKey). This can be much safer than hash the encryptionKey directlyPunitive
Why you say pbkdf2 a key-derivation function abused into password storage? Is there any problem using pbkdf2 as password storage function?Twirl
@Twirl PBKDF2 (password-based key-derivation), is designed to derive a key based on a password. It is not designed to hash passwords. Should something like PBKDF2_SHA256 be used for password storage? ABSOLUTELY NOT. SHA-256 is, by design very efficient to compute in minimal hardware environments (like a smart-card). That means it is very easy to compute by attackers. A own a 2.5W USB stick that can churn 330 million SHA-256 hashes per second; and i have 14 of those sticks. You absolutely should not use PBKDF2 for password storage.Ascend
@lan Boyd But bcrypt is also a KeyDerivationFunction: crypto.stackexchange.com/questions/6564/… Bcrypt uses blowfish as hash function. PBKDF2 could be based on any hash function(e.g. md5/sha256/hmac...and blowfish), We could even implement a function pbkdf2_bcrypt like pbkdf2_sh256 and pbkdf2_md5. Both PBKDF2 and Bcrypt could be very slow to compute if we use higher cost.Twirl
@Twirl bcrypt is not a key derivation function. Keep scrolling to the #2 answer in that crypto page you linked - my answer. Bcrypt does not use blowfish as a hash function. bcrypt encrypts the 24-byte phrase "OrpheanBeholderScryDoubt" 64 times uses blowfish encryption. Yes, you can use pbkdf2 as a key derivation function. If you paired PBKDF2 with ROMix, you've invented scrypt. But if you try pair pbkdf2 with SHA256, or MD5, and used a billion iterations as your pbdkf2 cost, it doesn't improve your security against hardware. That's why bcrypt, bufferfish, scrypt exist.Ascend
@lan Boyd Is your definition of PBKDF2 limited to PBKDF2_SHA256 only? I agree PBKDF2_SHA256 is not so safe. I don't know why billion iterations as pbdkf2 cost doesn't improve security against hardware. Is this because it can easily be executed in parallel on the GPU?Twirl
"I don't know why billion iterations as pbdkf2 cost doesn't improve security against hardware." It does improve security; but using SHA the improvement is negligible - because hardware can bruteforce a trillion hashes a second. Meanwhile you're using a plain old CPU to calculate your SHA256 hash. In order for any number of SHA256 iterations to be effective at preventing bruteforcing, the password authentication on your CPU would need to be slower than anyone could tolerate. So rather than doing more iterations of a trivial algorithm, you can do much fewer iterations of a good algorithm.Ascend
@IanBoyd Refer to this, bcrypt is indeed safer than PBKDF2, but the security difference is not that great. Perhaps it would be more accurate to say: PBKDF2 can also be used as password storage, but it is not so safe if you need a high security.Twirl
@Twirl Never use pbkdf2 for password storage, like you never use MD5 for cryptographic hashing.Ascend

© 2022 - 2024 — McMap. All rights reserved.