AES Encryption Keys (password hashing)
6 min read.Overview
The prior requirement to any encryption is having a good key.
All known encryption algorithms rely on that the key has a certain length and is non deterministic (indistinguishable from random data). User supplied passwords do not qualify as good encryption keys, they are either too short or not random enough.
Keys must have a minimum length, and the length depends on the encryption algorithm. AES+CBC encryption require a minimum length of 16 bytes (128bits), AES+GCM can work with 12bytes but 16bytes is recommended.
So far so good. We want to encrypt something, we just generate a random key with a secure random number generator and done. For software to software interaction this can work, and several schemes exist to exchange the key securely¹² ; Diffy-Hellman¹³ is used in TLS for example. Unfortunately allot of encryption starts with user provided input as “passwords” which are then used as keys to encrypt and, imho this is a very bad idea, but what can you do?.
Passwords do not make good encryption keys¹⁵ and can be ‘easily’ guessed with:
- Brute force attacks (guessing)
- Rainbow tables¹⁶ (guessing with pre-computed hash values)
To use passwords in encryption we need to apply what is called a “key derivation function”¹ or KDF² (like argon2¹⁷, scrypt¹⁸ or bcrypt¹⁹) and with a good KDF use a random salt.
KDFs give us the following:
- They ensure we have an encryption key of length N and indistinguishable from random data. Lengths are normally 16, 32, or 64 bytes (128, 256, 512 bits)
- They make it harder if not for brute force attacks by being computationally costly to calculated i.e slow, and as a result make brute force guessing infeasible.
Using a unique random salt gives us:
- Protection against rainbow tables²⁰.
Warning:
If you use a password hash as an encryption key you should not store the hash for login purposes. Either use two different hash schemes or better yet, encrypt the hash with itself, and store the encrypted version. This way for login: You get the user password, hash it, decrypt the encrypted hash you stored, and finally compare with the decrypted hash. You need this last step for the constant time hash compare.
Do not store the hash if you use it for encryption (I’m repeating myself :) ), rather store the hash encrypted with the hash itself.
A closer look at brute force attacks
Brute force attacks against remote login systems are feasible but not as affective as offline attacks on stolen data can be. The number of hashes that can be tried per second is mind boggling on GPUs and custom built crypto hardware.
How long an offline brute force attack will take depends on N * (time-to-generate-hash + time to decrypt). Time to decrypt is very efficient these days, so we only have N and time-to-generate-hash. N here is the number of different combinations the password can have, and for passwords we can only go so far.
For “time-to-generate-hash” we have KDFs. They are designed to be slow and some like argon2¹⁷ are designed to take up a reasonable amount of memory, such that doing calculations on GPUs are not feasible.
Argon2
Argon2 is (at the time of writing this article) considered the best KDF to use for password key derivation.
Argon2¹⁷ comes in three modes, Argon2d, Argon2i, and Argon2id.
Always use Argon2id, and only consider the other modes if you really cannot use Argon2id.
The reference implementation²² is in C, with bindings for many languages.
Hashes not to use
Don’t use md5²¹.
It is not a secure to use in anything security/cryptography related. The only use it has now a days is as a checksum calculation for operations that are not security sensitive.
Unfortunately there are many blogs out there that still have md5 in their examples. And worse, many companies, many good programmers are using md5 when they should not (I have no references for this).
Weak Keys
Lastly we have weak keys. Weak keys have nothing to do with passwords or how they are processed with KDFs.
They are cryptographically secure generated keys which make an encryption algorithm vulnerable to exploit⁷. For a list of encryption algorithms with weak keys see [6].
Although AES in general is reported to not have any weak keys, the GCM mode AES+GCM does have several papers published about weak keys see:[10][11][12]. Most of the material out there to date suggests that such attacks are either improbable⁹ or highly unlikely⁹, but still there is the risk. Combine this that when GCM is used wrongly with a fixed salt/iv the outcome is disasterous²³.
IMHO: I would use GCM for anything that has ‘short lived’ keys. For things like disc encryption where keys and ciphers live longer, I would use AES+CBC.
Salts
Salts²⁰ are public, and they are ok to be public²⁴. Normally you would store your salt with your password. By their name and definition, if they were not public they would be called Pepper²⁴.
Salts are used not because they are secret, but because they make the hash with the same password look different²⁴, and because of this, they also make pre-calculated rainbow tables infeasible.
Take away:
- Salts should be generated with a cryptographically secure random number generator.
- Salts should be unique for each hash and each encryption operation.
- Salts are public.
Password length
There is much to be said here, and deserves a blog on its own.
Just using a decent hashing algorithm and a random unique salt are not enough.
If a would be attacker gains access to the hash and salt, all they would need now is knowledge of how your password is composed to try and limit the possibilities, then calculate the possible hashes and hope for luck. If a weak or easily guessable password is used the hash can be guessed in a reasonable amount of time.
The attacker only needs to get lucky once.
This article makes for a very interesting read on password lengths and speed of offline attacks.
The takeaway here is:
- Passwords should be 12 characters or more.
- Some people suggest pass phrases.
Summary
- Use AES for encryption.
- Use random salts for your password hashes. Hashing alone is not safe, and reusing the salt is not safe either.
- Use KDFs ideally argon2id²².
- Ensure that passwords are a minimum of 12 characters long.
References
- [1] https://en.wikipedia.org/wiki/Key_stretching
- [2] https://en.wikipedia.org/wiki/Key_derivation_function
- [3] https://tools.ietf.org/html/rfc3602#page-4
- [4] https://tools.ietf.org/html/rfc7714#page-18
- [5] https://crypto.stackexchange.com/questions/3615/what-is-the-effect-of-the-different-aes-key-lengths
- [6] https://en.wikipedia.org/wiki/Weak_key
- [7] https://en.wikipedia.org/wiki/Galois/Counter_Mode
- [8] https://crypto.stackexchange.com/questions/39620/detection-of-weak-keys-for-aes-gcm
- [9] https://www.iacr.org/archive/crypto2008/51570145/51570145.pdf
- [10] https://eprint.iacr.org/2011/202.pdf
- [12] https://eprint.iacr.org/2013/144.pdf
- [13] https://en.wikipedia.org/wiki/Key_exchange
- [14] https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange#Description
- [15] https://en.wikipedia.org/wiki/Password_strength
- [16] https://en.wikipedia.org/wiki/Rainbow_table
- [17] https://en.wikipedia.org/wiki/Argon2
- [18] https://en.wikipedia.org/wiki/Bcrypt
- [19] https://en.wikipedia.org/wiki/Scrypt
- [20] https://en.wikipedia.org/wiki/Salt_(cryptography)
- [21] https://en.wikipedia.org/wiki/MD5 (do not use for anything security wise)
- [22] https://github.com/P-H-C/phc-winner-argon2
- [23] https://stackoverflow.com/questions/36760973/why-is-random-iv-fine-for-aes-cbc-but-not-for-aes-gcm
- [24] https://blog.codinghorror.com/speed-hashing/
- [25] https://en.wikipedia.org/wiki/Pepper_(cryptography)