So the goal is to use AES-CBC-256 to encrypt the data in a file. To derive the AES key and IV pbkdf2 will be used to generate a 384 bit output using a given password and salt, where the first 256 bits will be the key and the last 128 will be the IV. The key and iv will be used to encrypt the file and then a hash of the key will be stored at the beginning of the encrypted file so that when decryption happens, the hash of the supplied key will be compared to check whether the correct password has been supplied. Is this a secure method?
-
What are your risks?– kelalakaCommented Dec 23, 2020 at 1:01
-
Would you mind elaborating on that question?– JojoTheCodeDudeCommented Dec 23, 2020 at 1:09
-
1eff.org/keeping-your-site-alive/evaluating-your-threat-model– kelalakaCommented Dec 23, 2020 at 1:12
-
"Is this a secure method?" -- secure against what?– schroeder ♦Commented Dec 23, 2020 at 15:01
-
I am asking this question because i have seen several other similar situations where file encryption is involved, and it is said to generate several random keys, then using pbkdf2 to generate another key which is used to encrypt the random keys, then extra bytes from the pbkdf is written to the header of the file to make sure a correct password is supplied during decryption, my point is that it seems more complicated than what i have proposed in my question, but both come to the same result.– JojoTheCodeDudeCommented Dec 23, 2020 at 15:49
2 Answers
No, you should use Authenticated Encryption or AEAD.
My suggestion
Generate another 128 bits from the KDF, use that as the key to HMAC-SHA-256, compute HMAC of IV and ciphertext, store the HMAC result (called authentication tag) with the ciphertext.
When decrypting, pass the password through KDF to generate IV and HMAC key, compute HMAC over IV and ciphertext, compare with stored authentication tag. If they don't match, the password was bad or the ciphertext was tampered with. Do not decrypt. Decrypt only if the authentication tag is good.
CBC is slow, because it has to work one block at a time and cannot encrypt blocks in parallel. CTR would be better.
HMAC is a strong MAC but is slow.
Modern AEAD modes like AES-GCM or even ChaCha20-Poly1305 (the only modes allowed in TLS1.3 and libsodium, preferred in modern SSH and IPSec, with ChaPoly being the only mode allowed in age and WireGuard and is the default in libsodium) are faster than AES-CBC or AES-CBC+HMAC.
But actually...
Using age would be much better than rolling your own. For example, age uses modern chunked AEADs and scrypt, has a go and rust implementation, and was created by a reputable person (the maintainer of go's standard library's crypto code).
Your method is somewhat similar to to the method used by openssl
for aes-256-cbc encryption, with a key derived from a password by pbkdf2, e.g.:
openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000
As in your method, the pbkdf2 function in the openssl
command above derives a 348-bit key from the password, then this is split into a 256-bit encryption key and a 128-bit iv. However, you'll need to store the salt used by the pbkdf2 function somewhere (in the openssl
command above, the salt is stored in bytes 8-15 in the cyphertext). Also, openssl does not store a hash of the key with the ciphertext. However, incorrect passwords/keys at decryption often produce padding errors, so this can be used to detect incorrect passwords/keys in some cases.
For more information on how to detect incorrect passwords/keys, see https://crypto.stackexchange.com/questions/5921/how-and-why-can-a-decryption-program-tell-me-that-a-key-is-incorrect.
For more information on how openssl aes-256-cbc -e -a -salt -pbkdf2 -iter 10000
works 'under the hood', see See https://crypto.stackexchange.com/questions/3298/is-there-a-standard-for-openssl-interoperable-aes-encryption