1 /** @fileOverview CCM mode implementation.
  2  *
  3  * Special thanks to Roy Nicholson for pointing out a bug in our
  4  * implementation.
  5  *
  6  * @author Emily Stark
  7  * @author Mike Hamburg
  8  * @author Dan Boneh
  9  */
 10 
 11 /** @namespace CTR mode with CBC MAC. */
 12 sjcl.mode.ccm = {
 13   /** The name of the mode.
 14    * @constant
 15    */
 16   name: "ccm",
 17   
 18   /** Encrypt in CCM mode.
 19    * @static
 20    * @param {Object} prf The pseudorandom function.  It must have a block size of 16 bytes.
 21    * @param {bitArray} plaintext The plaintext data.
 22    * @param {bitArray} iv The initialization value.
 23    * @param {bitArray} [adata=[]] The authenticated data.
 24    * @param {Number} [tlen=64] the desired tag length, in bits.
 25    * @return {bitArray} The encrypted data, an array of bytes.
 26    */
 27   encrypt: function(prf, plaintext, iv, adata, tlen) {
 28     var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
 29     tlen = tlen || 64;
 30     adata = adata || [];
 31     
 32     if (ivl < 7) {
 33       throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
 34     }
 35     
 36     // compute the length of the length
 37     for (L=2; L<4 && ol >>> 8*L; L++) {}
 38     if (L < 15 - ivl) { L = 15-ivl; }
 39     iv = w.clamp(iv,8*(15-L));
 40     
 41     // compute the tag
 42     tag = sjcl.mode.ccm._computeTag(prf, plaintext, iv, adata, tlen, L);
 43     
 44     // encrypt
 45     out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
 46     
 47     return w.concat(out.data, out.tag);
 48   },
 49   
 50   /** Decrypt in CCM mode.
 51    * @static
 52    * @param {Object} prf The pseudorandom function.  It must have a block size of 16 bytes.
 53    * @param {bitArray} ciphertext The ciphertext data.
 54    * @param {bitArray} iv The initialization value.
 55    * @param {bitArray} [[]] adata The authenticated data.
 56    * @param {Number} [64] tlen the desired tag length, in bits.
 57    * @return {bitArray} The decrypted data.
 58    */
 59   decrypt: function(prf, ciphertext, iv, adata, tlen) {
 60     tlen = tlen || 64;
 61     adata = adata || [];
 62     var L, i, 
 63         w=sjcl.bitArray,
 64         ivl = w.bitLength(iv) / 8,
 65         ol = w.bitLength(ciphertext), 
 66         out = w.clamp(ciphertext, ol - tlen),
 67         tag = w.bitSlice(ciphertext, ol - tlen), tag2;
 68     
 69 
 70     ol = (ol - tlen) / 8;
 71         
 72     if (ivl < 7) {
 73       throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
 74     }
 75     
 76     // compute the length of the length
 77     for (L=2; L<4 && ol >>> 8*L; L++) {}
 78     if (L < 15 - ivl) { L = 15-ivl; }
 79     iv = w.clamp(iv,8*(15-L));
 80     
 81     // decrypt
 82     out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
 83     
 84     // check the tag
 85     tag2 = sjcl.mode.ccm._computeTag(prf, out.data, iv, adata, tlen, L);
 86     if (!w.equal(out.tag, tag2)) {
 87       throw new sjcl.exception.corrupt("ccm: tag doesn't match");
 88     }
 89     
 90     return out.data;
 91   },
 92 
 93   /* Compute the (unencrypted) authentication tag, according to the CCM specification
 94    * @param {Object} prf The pseudorandom function.
 95    * @param {bitArray} plaintext The plaintext data.
 96    * @param {bitArray} iv The initialization value.
 97    * @param {bitArray} adata The authenticated data.
 98    * @param {Number} tlen the desired tag length, in bits.
 99    * @return {bitArray} The tag, but not yet encrypted.
100    * @private
101    */
102   _computeTag: function(prf, plaintext, iv, adata, tlen, L) {
103     // compute B[0]
104     var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
105 
106     tlen /= 8;
107   
108     // check tag length and message length
109     if (tlen % 2 || tlen < 4 || tlen > 16) {
110       throw new sjcl.exception.invalid("ccm: invalid tag length");
111     }
112   
113     if (adata.length > 0xFFFFFFFF || plaintext.length > 0xFFFFFFFF) {
114       // I don't want to deal with extracting high words from doubles.
115       throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
116     }
117 
118     // mac the flags
119     mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)];
120 
121     // mac the iv and length
122     mac = w.concat(mac, iv);
123     mac[3] |= w.bitLength(plaintext)/8;
124     mac = prf.encrypt(mac);
125     
126   
127     if (adata.length) {
128       // mac the associated data.  start with its length...
129       tmp = w.bitLength(adata)/8;
130       if (tmp <= 0xFEFF) {
131         macData = [w.partial(16, tmp)];
132       } else if (tmp <= 0xFFFFFFFF) {
133         macData = w.concat([w.partial(16,0xFFFE)], [tmp]);
134       } // else ...
135     
136       // mac the data itself
137       macData = w.concat(macData, adata);
138       for (i=0; i<macData.length; i += 4) {
139         mac = prf.encrypt(xor(mac, macData.slice(i,i+4).concat([0,0,0])));
140       }
141     }
142   
143     // mac the plaintext
144     for (i=0; i<plaintext.length; i+=4) {
145       mac = prf.encrypt(xor(mac, plaintext.slice(i,i+4).concat([0,0,0])));
146     }
147 
148     return w.clamp(mac, tlen * 8);
149   },
150 
151   /** CCM CTR mode.
152    * Encrypt or decrypt data and tag with the prf in CCM-style CTR mode.
153    * May mutate its arguments.
154    * @param {Object} prf The PRF.
155    * @param {bitArray} data The data to be encrypted or decrypted.
156    * @param {bitArray} iv The initialization vector.
157    * @param {bitArray} tag The authentication tag.
158    * @param {Number} tlen The length of th etag, in bits.
159    * @param {Number} L The CCM L value.
160    * @return {Object} An object with data and tag, the en/decryption of data and tag values.
161    * @private
162    */
163   _ctrMode: function(prf, data, iv, tag, tlen, L) {
164     var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
165 
166     // start the ctr
167     ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);
168     
169     // en/decrypt the tag
170     tag = w.bitSlice(xor(tag,prf.encrypt(ctr)), 0, tlen);
171   
172     // en/decrypt the data
173     if (!l) { return {tag:tag, data:[]}; }
174     
175     for (i=0; i<l; i+=4) {
176       ctr[3]++;
177       enc = prf.encrypt(ctr);
178       data[i]   ^= enc[0];
179       data[i+1] ^= enc[1];
180       data[i+2] ^= enc[2];
181       data[i+3] ^= enc[3];
182     }
183     return { tag:tag, data:w.clamp(data,bl) };
184   }
185 };
186