javascriptpythonencryptionrncryptorsjcl

"Invalid AES Block Size" SJCL Decryption


Trying to decrypt AES using RNCryptor-js which uses SJCL. After logging all the steps on each end, (other end is RNCryptor-python) the keys, salts, HMAC hashes, everything matches up. But when I get to the final step:

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

I get the error:

 sjcl.exception.invalid {toString: function, message: "invalid aes block size"}

Here is the full code:

PBKDF2:

this.KeyForPassword = function(password, salt) {

  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);
};

Decryption (takes a hex input):

this.decrypt = function(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, "decryption");

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

  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));

  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 = aes.decrypt(ciphertext, iv);

  return decrypted;
}

The error is thrown on the second to last statement where decrypted is defined.

I looked at the sjcl exception and it looks like it's looking for an input of length 4, which I'm guessing is a WordArray. I'm just lost as to how to get a valid input. Like I said, the ciphertext, iv, hmac tag, salts are all being sliced properly on the javascript end. Might just be an encoding problem.

This error also seems to only be happening on json (format: '{"key":"value"}'), when I tried something like "Hello, world" I got back a 4 word array with no errors.

Any suggestions?


Solution

  •  var decrypted = aes.decrypt(ciphertext, iv);
    

    should be

     var decrypted = sjcl.mode.cbc.decrypt(aes, ciphertext, iv);
    

    I was also having trouble with padding happening in cbc.js (link to source) and it turned out that I hadn't included bitArray.js (link) which includes an important xor function (not to confused with the simple ^ operator).

    So: include bitArray.js

    The output should also be encoded:

    return sjcl.codec.utf8String.fromBits(decrypted);