I am trying to use RNCryptor-JS which uses SJCL but for some reason, SJCL bit array concatenation does not seem to work.
var SALT_SIZE = 64/8;
var plaintext = "Hello, World!";
var password = "myPassword";
function keyForPassword(password, salt){
// Using CryptoJS for pbkdf2, aes, sha256, and random word arrays
var pbkdf2_key = CryptoJS.PBKDF2(
password,
salt,
{
keySize: 256/32,
iterations: 1000,
hasher: CryptoJS.algo.SHA256
}
);
return pbkdf2_key;
}
var encryption_salt = CryptoJS.lib.WordArray.random(SALT_SIZE);
var encryption_key = keyForPassword(password, encryption_salt);
var hmac_salt = CryptoJS.lib.WordArray.random(SALT_SIZE);
var hmac_key = keyForPassword(password, hmac_salt);
var iv = CryptoJS.lib.WordArray.random(128/8);
var version = sjcl.codec.hex.toBits("03");
var options = sjcl.codec.hex.toBits("01");
var message = sjcl.bitArray.concat(version, iv);
message = sjcl.bitArray.concat(message, encryption_salt);
message = sjcl.bitArray.concat(message, hmac_salt);
message = sjcl.bitArray.concat(message, iv);
// Progressive cipher
var aesEncryptor = CryptoJS.algo.AES.createEncryptor(encryption_key, {iv: iv});
var ciphertext = aesEncryptor.process(plaintext);
message = sjcl.bitArray.concat(message, ciphertext);
var hmac = new sjcl.misc.hmac(hmac_key).encrypt(message);
var encrypted_data = sjcl.bitArray.concat(message, hmac);
var output = sjcl.codec.hex.fromBits(encrypted_data);
console.log(output);
When I log the output of message
after the first set of sjcl.bitArray.concat
is done, all that returns is the first concatenation of version
and iv
. The final hex output is just that first concatenation and hmac
concatenated. This reinforces my suspicion that it might be CryptoJS's fault because the output concatenation works and is between two sjcl variables.
I tried using SJCL random bit arrays but had some trouble. SJCL's generator, prng
, did not work when using
new sjcl.prng.randomWords(32/4);
or
new sjcl.prng(32/4);
And sjcl.random.randomWords
does not seem to work anymore.
CryptoJS (WordArray
) and SJCL (bitArray
) have different internal representations of data. You can't simply concatenate them.
The easiest way would be probably to encode it into an intermediate format such as Hex and let the other side decode into its internal format:
message = sjcl.bitArray.concat(version, sjcl.codec.hex.toBits(iv.toString()));
WordArray#toString()
automatically uses Hex encoding. You would have to do this for all lines, but this is a little overkill, since you can concatenate Hex strings as strings:
message = sjcl.codec.hex.toBits("03" + iv + encryption_salt + hmac_salt + iv);
This should work as expected, because adding a WordArray
such as iv
to a string automatically calls its toString()
function which in turn produces a big-endian hex-encoded string.
I wonder why you're using iv
twice. Perhaps you meant options
on one of them.
What needs to change:
function convert(wordArray){
return sjcl.codec.hex.toBits(wordArray.toString());
}
var message = "0301" + encryption_salt + hmac_salt + iv;
var ciphertext = CryptoJS.AES.encrypt(plaintext, encryption_key, {iv: iv}).ciphertext;
message += ciphertext;
message = sjcl.codec.hex.toBits(message);
var hmac = new sjcl.misc.hmac(convert(hmac_key)).encrypt(message);
var encrypted_data = sjcl.bitArray.concat(message, hmac);
var output = sjcl.codec.hex.fromBits(encrypted_data);
console.log(output);