warspyking warspyking - 6 months ago 23
Javascript Question

How does CryptoJS get an IV when none is specified?

When using

CryptoJS.AES.encrypt
how does it come up with an Initialization Vector if the third argument is not passed to the function? Is there a way to get it out of the encrypted string?

The reason I need this is I need to decrypt something
CryptoJS.AES.encrypt
returned using Lua, but I only have the key that was provided.

Answer

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

  1. 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).

  2. 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.