initial commit for GitHub
This commit is contained in:
185
core/ccm.js
Normal file
185
core/ccm.js
Normal file
@@ -0,0 +1,185 @@
|
||||
/** @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) };
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user