| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- /** @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<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];
- }
- };
|