173 lines
5.4 KiB
JavaScript
173 lines
5.4 KiB
JavaScript
/** @fileOverview OCB 2.0 implementation
|
|
*
|
|
* @author Emily Stark
|
|
* @author Mike Hamburg
|
|
* @author Dan Boneh
|
|
*/
|
|
|
|
/** @namespace
|
|
* Phil Rogaway's Offset CodeBook mode, version 2.0.
|
|
* May be covered by US and international patents.
|
|
*
|
|
* @author Emily Stark
|
|
* @author Mike Hamburg
|
|
* @author Dan Boneh
|
|
*/
|
|
sjcl.mode.ocb2 = {
|
|
/** The name of the mode.
|
|
* @constant
|
|
*/
|
|
name: "ocb2",
|
|
|
|
/** Encrypt in OCB mode, version 2.0.
|
|
* @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.
|
|
* @param {Number} [tlen=64] the desired tag length, in bits.
|
|
* @param [false] premac 1 if the authentication data is pre-macced with PMAC.
|
|
* @return The encrypted data, an array of bytes.
|
|
* @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits.
|
|
*/
|
|
encrypt: function(prp, plaintext, iv, adata, tlen, premac) {
|
|
if (sjcl.bitArray.bitLength(iv) !== 128) {
|
|
throw new sjcl.exception.invalid("ocb iv must be 128 bits");
|
|
}
|
|
var i,
|
|
times2 = sjcl.mode.ocb2._times2,
|
|
w = sjcl.bitArray,
|
|
xor = w._xor4,
|
|
checksum = [0,0,0,0],
|
|
delta = times2(prp.encrypt(iv)),
|
|
bi, bl,
|
|
output = [],
|
|
pad;
|
|
|
|
adata = adata || [];
|
|
tlen = tlen || 64;
|
|
|
|
for (i=0; i+4 < plaintext.length; i+=4) {
|
|
/* Encrypt a non-final block */
|
|
bi = plaintext.slice(i,i+4);
|
|
checksum = xor(checksum, bi);
|
|
bi = xor(delta,prp.encrypt(xor(delta, bi)));
|
|
output.splice(i,0,bi[0],bi[1],bi[2],bi[3]);
|
|
delta = times2(delta);
|
|
}
|
|
|
|
/* Chop out the final block */
|
|
bi = plaintext.slice(i);
|
|
bl = w.bitLength(bi);
|
|
pad = prp.encrypt(xor(delta,[0,0,0,bl]));
|
|
bi = w.clamp(xor(bi,pad), bl);
|
|
|
|
/* Checksum the final block, and finalize the checksum */
|
|
checksum = xor(checksum,xor(bi,pad));
|
|
checksum = prp.encrypt(xor(checksum,xor(delta,times2(delta))));
|
|
|
|
/* MAC the header */
|
|
if (adata.length) {
|
|
checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
|
|
}
|
|
|
|
return output.concat(w.concat(bi, w.clamp(checksum, tlen)));
|
|
},
|
|
|
|
/** Decrypt in OCB 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.
|
|
* @param {Number} [tlen=64] the desired tag length, in bits.
|
|
* @param {boolean} [premac=false] true if the authentication data is pre-macced with PMAC.
|
|
* @return The decrypted data, an array of bytes.
|
|
* @throws {sjcl.exception.invalid} if the IV isn't exactly 128 bits.
|
|
* @throws {sjcl.exception.corrupt} if if the message is corrupt.
|
|
*/
|
|
decrypt: function(prp, ciphertext, iv, adata, tlen, premac) {
|
|
if (sjcl.bitArray.bitLength(iv) !== 128) {
|
|
throw new sjcl.exception.invalid("ocb iv must be 128 bits");
|
|
}
|
|
tlen = tlen || 64;
|
|
var i,
|
|
times2 = sjcl.mode.ocb2._times2,
|
|
w = sjcl.bitArray,
|
|
xor = w._xor4,
|
|
checksum = [0,0,0,0],
|
|
delta = times2(prp.encrypt(iv)),
|
|
bi, bl,
|
|
len = sjcl.bitArray.bitLength(ciphertext) - tlen,
|
|
output = [],
|
|
pad;
|
|
|
|
adata = adata || [];
|
|
|
|
for (i=0; i+4 < len/32; i+=4) {
|
|
/* Decrypt a non-final block */
|
|
bi = xor(delta, prp.decrypt(xor(delta, ciphertext.slice(i,i+4))));
|
|
checksum = xor(checksum, bi);
|
|
output.splice(i,0,bi[0],bi[1],bi[2],bi[3]);
|
|
delta = times2(delta);
|
|
}
|
|
|
|
/* Chop out and decrypt the final block */
|
|
bl = len-i*32;
|
|
pad = prp.encrypt(xor(delta,[0,0,0,bl]));
|
|
bi = xor(pad, w.clamp(ciphertext.slice(i),bl));
|
|
|
|
/* Checksum the final block, and finalize the checksum */
|
|
checksum = xor(checksum, bi);
|
|
checksum = prp.encrypt(xor(checksum, xor(delta, times2(delta))));
|
|
|
|
/* MAC the header */
|
|
if (adata.length) {
|
|
checksum = xor(checksum, premac ? adata : sjcl.mode.ocb2.pmac(prp, adata));
|
|
}
|
|
|
|
if (!w.equal(w.clamp(checksum, tlen), w.bitSlice(ciphertext, len))) {
|
|
throw new sjcl.exception.corrupt("ocb: tag doesn't match");
|
|
}
|
|
|
|
return output.concat(w.clamp(bi,bl));
|
|
},
|
|
|
|
/** PMAC authentication for OCB associated data.
|
|
* @param {Object} prp The block cipher. It must have a block size of 16 bytes.
|
|
* @param {bitArray} adata The authenticated data.
|
|
*/
|
|
pmac: function(prp, adata) {
|
|
var i,
|
|
times2 = sjcl.mode.ocb2._times2,
|
|
w = sjcl.bitArray,
|
|
xor = w._xor4,
|
|
checksum = [0,0,0,0],
|
|
delta = prp.encrypt([0,0,0,0]),
|
|
bi;
|
|
|
|
delta = xor(delta,times2(times2(delta)));
|
|
|
|
for (i=0; i+4<adata.length; i+=4) {
|
|
delta = times2(delta);
|
|
checksum = xor(checksum, prp.encrypt(xor(delta, adata.slice(i,i+4))));
|
|
}
|
|
|
|
bi = adata.slice(i);
|
|
if (w.bitLength(bi) < 128) {
|
|
delta = xor(delta,times2(delta));
|
|
bi = w.concat(bi,[0x80000000|0]);
|
|
}
|
|
checksum = xor(checksum, bi);
|
|
return prp.encrypt(xor(times2(xor(delta,times2(delta))), checksum));
|
|
},
|
|
|
|
/** Double a block of words, OCB style.
|
|
* @private
|
|
*/
|
|
_times2: function(x) {
|
|
return [x[0]<<1 ^ x[1]>>>31,
|
|
x[1]<<1 ^ x[2]>>>31,
|
|
x[2]<<1 ^ x[3]>>>31,
|
|
x[3]<<1 ^ (x[0]>>>31)*0x87];
|
|
}
|
|
};
|