116 lines
3.8 KiB
JavaScript
116 lines
3.8 KiB
JavaScript
|
/** @fileOverview CBC mode implementation
|
||
|
*
|
||
|
* @author Emily Stark
|
||
|
* @author Mike Hamburg
|
||
|
* @author Dan Boneh
|
||
|
*/
|
||
|
|
||
|
/** @namespace
|
||
|
* Dangerous: CBC mode with PKCS#5 padding.
|
||
|
*
|
||
|
* @author Emily Stark
|
||
|
* @author Mike Hamburg
|
||
|
* @author Dan Boneh
|
||
|
*/
|
||
|
if (sjcl.beware === undefined) {
|
||
|
sjcl.beware = {};
|
||
|
}
|
||
|
sjcl.beware["CBC mode is dangerous because it doesn't protect message integrity."
|
||
|
] = function() {
|
||
|
sjcl.mode.cbc = {
|
||
|
/** The name of the mode.
|
||
|
* @constant
|
||
|
*/
|
||
|
name: "cbc",
|
||
|
|
||
|
/** Encrypt in CBC mode with PKCS#5 padding.
|
||
|
* @param {Object} prp The block cipher. It must have a block size of 16 bytes.
|
||
|
* @param {bitArray} plaintext The plaintext data.
|
||
|
* @param {bitArray} iv The initialization value.
|
||
|
* @param {bitArray} [adata=[]] The authenticated data. Must be empty.
|
||
|
* @return The encrypted data, an array of bytes.
|
||
|
* @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified.
|
||
|
*/
|
||
|
encrypt: function(prp, plaintext, iv, adata) {
|
||
|
if (adata && adata.length) {
|
||
|
throw new sjcl.exception.invalid("cbc can't authenticate data");
|
||
|
}
|
||
|
if (sjcl.bitArray.bitLength(iv) !== 128) {
|
||
|
throw new sjcl.exception.invalid("cbc iv must be 128 bits");
|
||
|
}
|
||
|
var i,
|
||
|
w = sjcl.bitArray,
|
||
|
xor = w._xor4,
|
||
|
bl = w.bitLength(plaintext),
|
||
|
bp = 0,
|
||
|
output = [];
|
||
|
|
||
|
if (bl&7) {
|
||
|
throw new sjcl.exception.invalid("pkcs#5 padding only works for multiples of a byte");
|
||
|
}
|
||
|
|
||
|
for (i=0; bp+128 <= bl; i+=4, bp+=128) {
|
||
|
/* Encrypt a non-final block */
|
||
|
iv = prp.encrypt(xor(iv, plaintext.slice(i,i+4)));
|
||
|
output.splice(i,0,iv[0],iv[1],iv[2],iv[3]);
|
||
|
}
|
||
|
|
||
|
/* Construct the pad. */
|
||
|
bl = (16 - ((bl >> 3) & 15)) * 0x1010101;
|
||
|
|
||
|
/* Pad and encrypt. */
|
||
|
iv = prp.encrypt(xor(iv,w.concat(plaintext,[bl,bl,bl,bl]).slice(i,i+4)));
|
||
|
output.splice(i,0,iv[0],iv[1],iv[2],iv[3]);
|
||
|
return output;
|
||
|
},
|
||
|
|
||
|
/** Decrypt in CBC mode.
|
||
|
* @param {Object} prp The block cipher. It must have a block size of 16 bytes.
|
||
|
* @param {bitArray} ciphertext The ciphertext data.
|
||
|
* @param {bitArray} iv The initialization value.
|
||
|
* @param {bitArray} [adata=[]] The authenticated data. It must be empty.
|
||
|
* @return The decrypted data, an array of bytes.
|
||
|
* @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits, or if any adata is specified.
|
||
|
* @throws {sjcl.exception.corrupt} if if the message is corrupt.
|
||
|
*/
|
||
|
decrypt: function(prp, ciphertext, iv, adata) {
|
||
|
if (adata && adata.length) {
|
||
|
throw new sjcl.exception.invalid("cbc can't authenticate data");
|
||
|
}
|
||
|
if (sjcl.bitArray.bitLength(iv) !== 128) {
|
||
|
throw new sjcl.exception.invalid("cbc iv must be 128 bits");
|
||
|
}
|
||
|
if ((sjcl.bitArray.bitLength(ciphertext) & 127) || !ciphertext.length) {
|
||
|
throw new sjcl.exception.corrupt("cbc ciphertext must be a positive multiple of the block size");
|
||
|
}
|
||
|
var i,
|
||
|
w = sjcl.bitArray,
|
||
|
xor = w._xor4,
|
||
|
bi, bo,
|
||
|
output = [];
|
||
|
|
||
|
adata = adata || [];
|
||
|
|
||
|
for (i=0; i<ciphertext.length; i+=4) {
|
||
|
bi = ciphertext.slice(i,i+4);
|
||
|
bo = xor(iv,prp.decrypt(bi));
|
||
|
output.splice(i,0,bo[0],bo[1],bo[2],bo[3]);
|
||
|
iv = bi;
|
||
|
}
|
||
|
|
||
|
/* check and remove the pad */
|
||
|
bi = output[i-1] & 255;
|
||
|
if (bi == 0 || bi > 16) {
|
||
|
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
|
||
|
}
|
||
|
bo = bi * 0x1010101;
|
||
|
if (!w.equal(w.bitSlice([bo,bo,bo,bo], 0, bi*8),
|
||
|
w.bitSlice(output, output.length*32 - bi*8, output.length*32))) {
|
||
|
throw new sjcl.exception.corrupt("pkcs#5 padding corrupt");
|
||
|
}
|
||
|
|
||
|
return w.bitSlice(output, 0, output.length*32 - bi*8);
|
||
|
}
|
||
|
};
|
||
|
};
|