6
$\begingroup$

I have a program which uses AES-256 in CBC mode to encrypt and decrypt files. As I have quickly realized, AES will even use an incorrect passphrase to decrypt data, which leaves me with no way to validate whether the passphrase was correct or not and, in turn, whether I'm decrypting things successfully.

One idea which was suggested before was to embed a known value in beginning of the encrypted data to validate whether decryption produced an expected result. Essentially, since the IV is a known value, I thought of simply hashing it and embedding it as the first 32 bytes in the file. If, after the first block is decrypted, the first 32 bytes equal the hash of the IV, we can know that decryption succeeded. If not, we can know that it failed.

Does this in any way compromise my encrypted files?

$\endgroup$
2

3 Answers 3

3
$\begingroup$

That would work and almost certainly wouldn't have any negative impact on security, but it would be cleaner just to have a string of 16 0x00 bytes at the start of a message, instead. Not only does this save you the trouble of hashing, but you stay within the standard threat model for CBC which assumes the IV is independent of the message blocks. (One can come up with pathological yet technically still secure hash functions that would cause your approach to become insecure.)

And a quick note: the block size is 16 bytes, not 32 bytes. The 256 in AES-256 refers to the length of the key, not the block.

You should look into using a message authentication code, such as HMAC. Doing so will not only validate a correct passphrase, but will also ensure the ciphertext has not been tampered with. (CBC will prevent someone from learning information about the plaintext, but will not stop someone who already knows it from tampering with the ciphertext so that parts of it decrypt to strings of his choosing.)

Edit: A couple warnings, if you decide to go with a MAC (and I think you should): (1) Use different keys for CBC and the MAC; (2) MAC the ciphertext, not the plaintext; (3) Concatenate the IV to the ciphertext before using the MAC.

$\endgroup$
7
  • $\begingroup$ Would you mind explaining how a MAC of the ciphertext could validate a correct passphrase (assuming independent keys)? I've noticed @CodesInChaos's comment above offering similar advice, but I don't see how a MAC of the ciphertext could be used to confirm that the ciphertext has been decrypted correctly. $\endgroup$
    – hunter
    Feb 20, 2013 at 23:26
  • 1
    $\begingroup$ The passphrase would be used to generate both the MAC key and the encryption key. So in that sense, they're not independent. But if you derive them from a pseudo-random function (PRF) using a master key (which is in turn derived from the passphrase), then the probability of an incorrect passphrase giving a correct MAC key but a incorrect encryption key is negligible. (If your PBKDF algorithm gives you enough bits for both an encryption key and a MAC key, then you can forgo the PRF). $\endgroup$
    – Seth
    Feb 21, 2013 at 0:22
  • $\begingroup$ Ok, that all makes sense. For (1) above, is it a problem to 'split' hash output into two 16-byte sets? I'm currently using $sha512 \longrightarrow bcrypt \longrightarrow sha256$ for my passphrase. Should I simply make the final step $sha512$ and split the output: half to AES and half to the MAC? Also, on (3): I'm storing the IV in the unencrypted header of the file. Why should I tack it onto the end of the ciphertext? $\endgroup$ Feb 21, 2013 at 2:54
  • $\begingroup$ The only reason I didn't set out to use a MAC in the first place is because there doesn't seem to be native support in Python as a part of PyCrypto for doing it. I've heard that doing a MAC wrong also can really open up a security hole. $\endgroup$ Feb 21, 2013 at 2:56
  • 2
    $\begingroup$ You might want to Google PBKDF2 for handling the passphrase. Once you have a random "master" key M, you can do things like set your encryption key to HMAC(M, "Encryption key") and your MAC key to HMAC(M, "MAC key") [truncate the outputs as needed]. Or set the encryption key to AES(M,0x00...0000)AES(M,0x0...0001) and the MAC key to AES(M,0x00...0002). It doesn't matter where you store the IV, but when you compute the MAC, the IV needs to be part of the input string. Otherwise an attacker can mess with the IV to tamper with the plaintext - a common security hole. Not using a MAC is another. :) $\endgroup$
    – Seth
    Feb 21, 2013 at 3:39
3
$\begingroup$

If you use an authenticated encryption mode (which you should be doing anyway) like GCM or EAX, this is a built-in feature.

Otherwise, use an HMAC.

$\endgroup$
4
  • $\begingroup$ Wish I could; Python support for authenticated AES modes is terrible (read 'nonexistent'). $\endgroup$ Feb 21, 2013 at 0:07
  • 1
    $\begingroup$ Python has bindings to OpenSSL, does it not? If you have access to OpenSSL >= 1.0.1, it supports AES in GCM mode. $\endgroup$ Feb 21, 2013 at 0:19
  • $\begingroup$ It does, but I can't seem to find it. Is it in the OpenSSL.crypto package? Can't seem to find it here. $\endgroup$ Feb 21, 2013 at 0:25
  • 1
    $\begingroup$ @TKKocheran pyopenssl.sourceforge.net $\endgroup$
    – mikeazo
    Feb 21, 2013 at 18:20
0
$\begingroup$

With AES-256-CBC, if something is known about the padding used, then it may be possible to determine whether or not a key provided is correct for decrypting a given ciphertext, by focusing on the last block of ciphertext (without using an HMAC or magic value). See How do I detect a failed AES-256 decryption programmatically? for a working example.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.