8

I am using the PKCS#5 standard to generate a key using a random and unique salt and the user's password in input. Consider this key as the "encryption" key.

The "encryption" key is used to encrypt a random AES key. Each user has an AES key associated to their profile.

So, a user's profile will contains this information:

  • password hash for authentication purpose.
  • salt used in the PKCS#5 algo. (From the PKCS#5 V2.0 documentation, we know that this information needs no protection).
  • the encrypted AES key generated randomly and encrypted with the "encryption" key generated by the PKCS#5 algo with the salt and the user's password.

I was asking myself if it is dangerous to be in possession of the password's hash, the salt and the encrypted AES key AT THE SAME TIME. I am 99.9% sure that this is not a problem, but can it facilitate the work of an attacker being in possession of all those details?

Thomas Pornin
  • 322,884
  • 58
  • 787
  • 955
Normand Bedard
  • 241
  • 1
  • 3

3 Answers3

7

Just to make sure I've got it - you're storing:

  • the hashed password - theoretically the password can't be recovered, since the hash is one way.

  • the salt - only half the equation for the encryption key, since you need the password in the clear as well, right?

  • the encrypted AES key

So... the AES key is the real payoff, presumably, because that's being used for encrypted communication or storage.

When you want the server to use the key, I'm guessing the process is this:

  • user gives the system his password
  • system checks the password, hashing it - and it's good.
  • system takes the password (not the hash!) and the salt and computes the encryption key
  • system takes encryption key and decrypts AES Key

Then the system handles the AES key appropriately, does whatever it needs to do and erases any copy of the AES key from memory so that it's not lying around in the clear anywhere.

As is, I don't see a weakness in that, but you've boiled everything down to the strength of the password. If an attacker should get your data store, the one thing he needs but doesn't have is the password. If a user has an easy to guess password, then the attacker will get the user's AES key.

I think it if was analyzing this system, I'd not worry too much about that - instead, I'd start poking around at how user passwords are communicated and how the AES key is handled after it's decrypted - those are weak points, too and may be easier to hack than a back end data store of hashes, salts and encrypted keys.

bethlakshmi
  • 11,656
  • 1
  • 28
  • 59
3

If an attacker wants to break your system, he will try to guess the password. This means trying out potential passwords until one "matches" (that's what is called a dictionary attack). Matches what ? There are two ways an attacker can see if the password is correct:

  1. by verifying the stored hash;
  2. by applying the PKCS#5 password derivation (PBKDF1 or PBKDF2, PKCS#5 describes both), then decrypting the AES key, then using the obtained key to try to decrypt whatever is encrypted with that key, and see if the result makes sense.

The attacker will employ whatever way is easiest for him. The PBKDF* functions include some features which make dictionary attacks harder, namely the salt and a configurable number of internal iterations. The iterations make each password trial expensive. The salt prevents the attacker from optimizing things with a big table of precomputed hashes (e.g. a "rainbow table"). PBKDF2 is considered quite good at that (although bcrypt is arguably better). If your password-verification hash does not include the same features, then that hash is a weakness which an attacker will use.

In shorter words, your security will be that of the weaker of your password-verification hash, and the PKCS#5 key derivation.

A better scheme would be the following:

  • From the user password, you derive a key K using a proper key derivation function, e.g. PBKDF2 or bcrypt. This uses a salt S.
  • You encrypt your "AES key" symmetrically with K, yielding some value B.
  • You store h(K), S and B for each user (where h is a secure hash function -- I suggest SHA-256).

To verify a password, you rerun the key derivation function, using the stored salt S, and hash the result to see if it matches the stored h(K). If you want to decrypt B, you just use the reconstructed K. This way, you are sure that the key derivation used for encryption, and the password verification hash, are equally resistant to dictionary attacks.

Thomas Pornin
  • 322,884
  • 58
  • 787
  • 955
1

I don't think you'd want to store the hash next to the AES key. If someone were to get access to the raw encrypted AES key, hash, and salt they could easily brute force the password from the hash alone. Given today's computational power brute forcing all alphanumeric passwords of only 6 letters takes 2 seconds using off the shelf GPU. Algorithms like MD5, SHA1, and even SHA256 can easily be brute forced with a small amount of today's computational power.

http://www.zdnet.com/blog/hardware/cheap-gpus-are-rendering-strong-passwords-useless/13125

http://www.engadget.com/2010/08/16/gpus-democratize-brute-force-password-hacking/

http://www.insidepro.com/eng/egb.shtml

http://whitepixel.zorinaq.com/

So let's say you didn't store the password as a hash next to it. The attacker could still brute force the encrypted AES key, but if the computation used to calculate the encryption is significantly higher than the hashing it might not be feasible to brute force that. Hash algorithms like MD5 and SHA1 are designed for speed. So it really comes down to which algorithm is higher in computational complexity as to how well this scheme provides you protection.

However, both algorithms will probably fall to password < 8 characters in length. A 7 password, mix cased, all symbols only takes 7 hours on a GPU for certain algorithms. That's not that long to wait if this information is important enough.

BTW salting doesn't help because it's stored in the clear so you have to assume your attacker can know it. The only thing salting does is prevent rainbow table attacks which are sorta pointless now that we can just brute force them.

chubbsondubs
  • 215
  • 1
  • 7
  • If Norman were dumb enough to store his password as an md5 or SHA1/256/512 hash, that would be a big concern. Fortunately, however, Norman isn't that stupid--he's implementing a well-reviewed crypto standard. This crypto standard knows about GPGPU, so instead of simply hashing, it calls for a minimum of 1000 iterations of the hashing function. This, in the words of the PKCS#5 standard, "will increase the cost of exhaustive search for passwords significantly." Still not as good as bcrypt, with its tunable work factor (or scrypt, with memory-hard tuning), but it's better than naively hashing. – user502 Jun 07 '11 at 14:19
  • I've re-read Norman's text, and the first bullet point sounds like he IS storing the hashed version of the user's password and it was confirmed in bethlakshmi post. The PKCS#5 standard is only applied to the password used to encrypt the AES key which is derived from the user's password and salt. There are no details about how the user's password is being hashed (algorithm, iterations, etc). I agree bcrypt or scrypt would be a better choice. – chubbsondubs Jun 07 '11 at 15:11