diff options
Diffstat (limited to 'core/ocb2progressive.js')
-rw-r--r-- | core/ocb2progressive.js | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/core/ocb2progressive.js b/core/ocb2progressive.js new file mode 100644 index 0000000..0cb1790 --- /dev/null +++ b/core/ocb2progressive.js @@ -0,0 +1,137 @@ +/** + * OCB2.0 implementation slightly modified by Yifan Gu + * to support progressive encryption + * @author Yifan Gu + */ + +/** @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.ocb2progressive = { + createEncryptor: function(prp, 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, + datacache = [], + pad; + + adata = adata || []; + tlen = tlen || 64; + + return { + process: function(data){ + var datalen = sjcl.bitArray.bitLength(data); + if (datalen == 0){ // empty input natrually gives empty output + return []; + } + var output = []; + datacache = datacache.concat(data); + for (i=0; i+4 < datacache.length; i+=4) { + /* Encrypt a non-final block */ + bi = datacache.slice(i,i+4); + checksum = xor(checksum, bi); + output = output.concat(xor(delta,prp.encrypt(xor(delta, bi)))); + delta = times2(delta); + } + datacache = datacache.slice(i); // at end of each process we ensure size of datacache is smaller than 4 + return output; //spits out the result. + }, + finalize: function(){ + // the final block + bi = datacache; + bl = w.bitLength(bi); + pad = prp.encrypt(xor(delta,[0,0,0,bl])); + bi = w.clamp(xor(bi.concat([0,0,0]),pad), bl); + + /* Checksum the final block, and finalize the checksum */ + checksum = xor(checksum,xor(bi.concat([0,0,0]),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 w.concat(bi, w.clamp(checksum, tlen)); // spits out the last block + } + }; + }, + createDecryptor: function(prp, 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, + datacache = [], + pad; + + adata = adata || []; + + return { + process: function(data){ + if (data.length == 0){ // empty input natrually gives empty output + return []; + } + var output = []; + datacache = datacache.concat(data); + var cachelen = sjcl.bitArray.bitLength(datacache); + for (i=0; i+4 < (cachelen-tlen)/32; i+=4) { + /* Decrypt a non-final block */ + bi = xor(delta, prp.decrypt(xor(delta, datacache.slice(i,i+4)))); + checksum = xor(checksum, bi); + output = output.concat(bi); + delta = times2(delta); + } + datacache = datacache.slice(i); + return output; + }, + finalize: function(){ + /* Chop out and decrypt the final block */ + bl = sjcl.bitArray.bitLength(datacache) - tlen; + pad = prp.encrypt(xor(delta,[0,0,0,bl])); + bi = xor(pad, w.clamp(datacache,bl).concat([0,0,0])); + + /* 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(datacache, bl))) { + throw new sjcl.exception.corrupt("ocb: tag doesn't match"); + } + + return w.clamp(bi,bl); + } + }; + } +} |