Saturnix Saturnix - 12 days ago 8
Javascript Question

CryptoJS, check if AES passphrase is correct

var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");

var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");


Assuming the encryption and decryption phases are separated (happening in separate parts of the program), is there any way I can check if
"Secret Passphrase"
is the correct one in the decryption phase?

I've noticed that decrypting with a wrong passphrase will result in
decrypted.toString()
being an empty string. What then if the original message was an empty string too? Isn't there any other parameter I can use to check if the passphrase is indeed correct?




Side notes

My current solution: always create a non-empty control value, encrypted with the passphrase. If the control values decrypts to "", passphrase was not correct.

EDIT: turns out my solution doesn't work. Very similar passphrases are decrypting the message to different values which are, still, different from an empty string. The security is not compromised, but I still don't have any way to see if the passphrase was correct.

this is my very cool key!
and
this is my very cool key!!
are both returning a string, even though only the first one is the actual passphrase used to encrypt.

EDIT 2: the string returned from the wrong passphrase is not valid UTF-8. Hence, calling
value.toString(CryptoJS.enc.Utf8)
returns an error. I could use this error to check if the passphrase was correct. Still, this solutions sucks very much!

Answer

You have just stumbled onto the subtle but important difference between message confidentiality and message integrity/authentication.

Confidentiality is what you are currently doing it ensures that the message can't be read in transit without the associated key but it can't ensure that the message hasn't been altered during transit (attacks on early crypto systems involved modifying the encrypted message to inject or reveal information about the crypto system). Your problem is a variant of this in that if you happened to find a key that decrypted the message "surrender" to "bombs away" then there is no way to know that they key was switched.

Integrity deals with ensuring that what is decrypted is what was actually encrypted in the first place and can also ensure Authentication (who encrypted it) depending on the model used. Integrity is best achieved by using a mode of operation the supports it intrinsically such as GCM but from a cursory glance crypto-js doesn't support any integrity modes.

This leaves you with a manual approach which requires producing and sending a HMAC along with the encrypted message.

var message = "Message";
var passphrase = "Secret Passphrase";
var encrypted = CryptoJS.AES.encrypt(message, passphrase).toString();
var hmac = CryptoJS.HmacSHA256(encrypted, CryptoJS.SHA256(passphrase)).toString();
var transitmessage = hmac + encrypted;

//other side

var transithmac = transitmessage.substring(0, 64);
var transitencrypted = transitmessage.substring(64);
var decryptedhmac = CryptoJS.HmacSHA256(transitencrypted, CryptoJS.SHA256(passphrase)).toString();
alert(transithmac == decryptedhmac);
var decrypted = CryptoJS.AES.decrypt(transitencrypted, passphrase).toString(CryptoJS.enc.Utf8);
alert(decrypted.toString(CryptoJS.enc.Utf8));

In theory the IV should also be prepended to the encrypted message before the HMAC call but I wasn't sure how to get the IV back out with crypto-js and I was hoping to keep this example simple enough and unless you expect to be a target of the NSA this should be enough as the IV already modifies the encrypted message.