/** @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); output = output.concat(xor(delta,prp.encrypt(xor(delta, bi)))); 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 = output.concat(bi); 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>>31, x[1]<<1 ^ x[2]>>>31, x[2]<<1 ^ x[3]>>>31, x[3]<<1 ^ (x[0]>>>31)*0x87]; } };