javascriptencryptionrncryptorsjcl

SJCL CBC Mode not decrypting


Using RNCryptor which uses SJCL. I am trying to decrypt a hex message but when using CBC mode, things get weird. Apparently one must declare a beware statement when using CBC and I get an error.

function KeyForPassword(password, salt) {
    console.log("Creating key...");
    var hmacSHA256 = function (password) {
        var hasher = new sjcl.misc.hmac(password, sjcl.hash.sha256);
        this.encrypt = function () {
            return hasher.encrypt.apply(hasher, arguments);
        };
    };
    return sjcl.misc.pbkdf2(password, salt, 10000, 32 * 8, hmacSHA256);
};


function decrypt(password, message, options) {

    message = sjcl.codec.hex.toBits(message);

    options = options || {};

    var version = sjcl.bitArray.extract(message, 0 * 8, 8);
    var options = sjcl.bitArray.extract(message, 1 * 8, 8);

    var encryption_salt = sjcl.bitArray.bitSlice(message, 2 * 8, 10 * 8);
    var encryption_key = _this.KeyForPassword(password, encryption_salt);

    var hmac_salt = sjcl.bitArray.bitSlice(message, 10 * 8, 18 * 8);
    var hmac_key = _this.KeyForPassword(password, hmac_salt);

    var iv = sjcl.bitArray.bitSlice(message, 18 * 8, 34 * 8);

    var ciphertext_end = sjcl.bitArray.bitLength(message) - (32 * 8);
    var ciphertext = sjcl.bitArray.bitSlice(message, 34 * 8, ciphertext_end);

    var hmac = sjcl.bitArray.bitSlice(message, ciphertext_end);
    var expected_hmac = new sjcl.misc.hmac(hmac_key).encrypt(sjcl.bitArray.bitSlice(message, 0, ciphertext_end));

    // .equal is of consistent time
    if (! sjcl.bitArray.equal(hmac, expected_hmac)) {
      throw new sjcl.exception.corrupt("HMAC mismatch or bad password.");
    }

    var aes = new sjcl.cipher.aes(encryption_key);
    sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."]()
    var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv);


    return decrypted.toString(CryptoJS.enc.Utf8);
};

Everything matches the encryption on the Python side in terms of salts, keys, and the hashes. But I get this error:

TypeError: Cannot read property 'CBC mode is dangerous because it doesn't protect message integrity.' of undefined

I'm thought the method is deprecated so I then tried to use this CryptoJS method:

var decrypted = CryptoJS.AES.decrypt(ciphertext, encryption_key, {iv:iv});

This just returned a blank string.

I feel like I am really close, just need some help on this last part, thanks.


Solution

  • As Artjom B. stated, cbc.js is needed as well as bitArray.js (necessary part of the decryption and something I was missing). The original code works fine now.

    As an aside on the PBKDF2 iteration count as Rob Napier pointed out, it's slow. For this case (decryption), however, the 10K count worked quickly, but for encryption, I supplemented the kdf with CryptoJS's PBKDF2 at 1000 iterations instead (was getting bitArray error with sjcl's).