Monday, June 14, 2021

How does CryptoJS get an IV when none is specified?

CryptoJS' CryptoJS.<BlockCipher>.encrypt has two modes of encryption.


If you pass in a key that is not a string, but rather a WordArray (CryptoJS's internal representation format for binary data), the key is taken as-is. This mode expects an IV for all modes of operation except ECB, which doesn't use an IV, so you don't have to specify one. If no IV is passed, it will default (through some JavaScript magic) to a zero filled IV (consisting of a full block of 0x00 bytes).


If you pass in a "key" that is a string, it will assume the "key" is a password. In order to derive a key from the password, it uses the OpenSSL-compatible derivation function EVP_BytesToKey. This mode generates a new 8 byte random salt and uses it along with the password to generate a key and IV. Even if you explicitly pass in an IV, it won't be used.


 CryptoJS.AES.encrypt(msg, password).toString()

results in a Base64-encoded ciphertext that contains the string "Salted__" at the beginning followed by the 8 byte salt and the actual ciphertext. You can explicitly split this before use with:

 var ct = CryptoJS.AES.encrypt(msg, password);

 var saltHex = ct.salt.toString();     // random salt

 var ctHex = ct.ciphertext.toString(); // actual ciphertext

 var ivHex = ct.iv.toString();         // generated IV

If you need to recreate the same key derivation. Have a look at the code and the specification.


Keys should have high entropy and be indistinguishable from random noise, which makes it hard for them to be brute-forced. The above mentioned EVP_BytesToKey is not secure, because MD5 hashing is very fast, which enables an attacker to brute-force the password. You either need to use a really long password (20-30 characters) or use an appropriate key derivation function such as PBKDF2, which CryptoJS provides.


references:

https://stackoverflow.com/questions/35472396/how-does-cryptojs-get-an-iv-when-none-is-specified

No comments:

Post a Comment