| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- /** @fileOverview CCM mode implementation.
- *
- * Special thanks to Roy Nicholson for pointing out a bug in our
- * implementation.
- *
- * @author Emily Stark
- * @author Mike Hamburg
- * @author Dan Boneh
- */
- /** @namespace CTR mode with CBC MAC. */
- sjcl.mode.ccm = {
- /** The name of the mode.
- * @constant
- */
- name: "ccm",
-
- /** Encrypt in CCM mode.
- * @static
- * @param {Object} prf The pseudorandom function. 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.
- * @return {bitArray} The encrypted data, an array of bytes.
- */
- encrypt: function(prf, plaintext, iv, adata, tlen) {
- var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
- tlen = tlen || 64;
- adata = adata || [];
-
- if (ivl < 7) {
- throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
- }
-
- // compute the length of the length
- for (L=2; L<4 && ol >>> 8*L; L++) {}
- if (L < 15 - ivl) { L = 15-ivl; }
- iv = w.clamp(iv,8*(15-L));
-
- // compute the tag
- tag = sjcl.mode.ccm._computeTag(prf, plaintext, iv, adata, tlen, L);
-
- // encrypt
- out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
-
- return w.concat(out.data, out.tag);
- },
-
- /** Decrypt in CCM mode.
- * @static
- * @param {Object} prf The pseudorandom function. 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} [64] tlen the desired tag length, in bits.
- * @return {bitArray} The decrypted data.
- */
- decrypt: function(prf, ciphertext, iv, adata, tlen) {
- tlen = tlen || 64;
- adata = adata || [];
- var L, i,
- w=sjcl.bitArray,
- ivl = w.bitLength(iv) / 8,
- ol = w.bitLength(ciphertext),
- out = w.clamp(ciphertext, ol - tlen),
- tag = w.bitSlice(ciphertext, ol - tlen), tag2;
-
- ol = (ol - tlen) / 8;
-
- if (ivl < 7) {
- throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
- }
-
- // compute the length of the length
- for (L=2; L<4 && ol >>> 8*L; L++) {}
- if (L < 15 - ivl) { L = 15-ivl; }
- iv = w.clamp(iv,8*(15-L));
-
- // decrypt
- out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
-
- // check the tag
- tag2 = sjcl.mode.ccm._computeTag(prf, out.data, iv, adata, tlen, L);
- if (!w.equal(out.tag, tag2)) {
- throw new sjcl.exception.corrupt("ccm: tag doesn't match");
- }
-
- return out.data;
- },
- /* Compute the (unencrypted) authentication tag, according to the CCM specification
- * @param {Object} prf The pseudorandom function.
- * @param {bitArray} plaintext The plaintext data.
- * @param {bitArray} iv The initialization value.
- * @param {bitArray} adata The authenticated data.
- * @param {Number} tlen the desired tag length, in bits.
- * @return {bitArray} The tag, but not yet encrypted.
- * @private
- */
- _computeTag: function(prf, plaintext, iv, adata, tlen, L) {
- // compute B[0]
- var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
- tlen /= 8;
-
- // check tag length and message length
- if (tlen % 2 || tlen < 4 || tlen > 16) {
- throw new sjcl.exception.invalid("ccm: invalid tag length");
- }
-
- if (adata.length > 0xFFFFFFFF || plaintext.length > 0xFFFFFFFF) {
- // I don't want to deal with extracting high words from doubles.
- throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
- }
- // mac the flags
- mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)];
- // mac the iv and length
- mac = w.concat(mac, iv);
- mac[3] |= w.bitLength(plaintext)/8;
- mac = prf.encrypt(mac);
-
-
- if (adata.length) {
- // mac the associated data. start with its length...
- tmp = w.bitLength(adata)/8;
- if (tmp <= 0xFEFF) {
- macData = [w.partial(16, tmp)];
- } else if (tmp <= 0xFFFFFFFF) {
- macData = w.concat([w.partial(16,0xFFFE)], [tmp]);
- } // else ...
-
- // mac the data itself
- macData = w.concat(macData, adata);
- for (i=0; i<macData.length; i += 4) {
- mac = prf.encrypt(xor(mac, macData.slice(i,i+4)));
- }
- }
-
- // mac the plaintext
- for (i=0; i<plaintext.length; i+=4) {
- mac = prf.encrypt(xor(mac, plaintext.slice(i,i+4)));
- }
- return w.clamp(mac, tlen * 8);
- },
- /** CCM CTR mode.
- * Encrypt or decrypt data and tag with the prf in CCM-style CTR mode.
- * May mutate its arguments.
- * @param {Object} prf The PRF.
- * @param {bitArray} data The data to be encrypted or decrypted.
- * @param {bitArray} iv The initialization vector.
- * @param {bitArray} tag The authentication tag.
- * @param {Number} tlen The length of th etag, in bits.
- * @param {Number} L The CCM L value.
- * @return {Object} An object with data and tag, the en/decryption of data and tag values.
- * @private
- */
- _ctrMode: function(prf, data, iv, tag, tlen, L) {
- var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
- // start the ctr
- ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);
-
- // en/decrypt the tag
- tag = w.bitSlice(xor(tag,prf.encrypt(ctr)), 0, tlen);
-
- // en/decrypt the data
- if (!l) { return {tag:tag, data:[]}; }
-
- for (i=0; i<l; i+=4) {
- ctr[3]++;
- enc = prf.encrypt(ctr);
- data[i] ^= enc[0];
- data[i+1] ^= enc[1];
- data[i+2] ^= enc[2];
- data[i+3] ^= enc[3];
- }
- return { tag:tag, data:w.clamp(data,bl) };
- }
- };
|