ccm.js 5.8 KB

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