mirror of
https://github.com/moparisthebest/mail
synced 2024-08-13 16:43:47 -04:00
184 lines
5.1 KiB
JavaScript
184 lines
5.1 KiB
JavaScript
|
/** @fileOverview GCM mode implementation.
|
||
|
*
|
||
|
* @author Juho Vähä-Herttua
|
||
|
*/
|
||
|
|
||
|
/** @namespace Galois/Counter mode. */
|
||
|
sjcl.mode.gcm = {
|
||
|
/** The name of the mode.
|
||
|
* @constant
|
||
|
*/
|
||
|
name: "gcm",
|
||
|
|
||
|
/** Encrypt in GCM 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=128] The desired tag length, in bits.
|
||
|
* @return {bitArray} The encrypted data, an array of bytes.
|
||
|
*/
|
||
|
encrypt: function (prf, plaintext, iv, adata, tlen) {
|
||
|
var out, data = plaintext.slice(0), w=sjcl.bitArray;
|
||
|
tlen = tlen || 128;
|
||
|
adata = adata || [];
|
||
|
|
||
|
// encrypt and tag
|
||
|
out = sjcl.mode.gcm._ctrMode(true, prf, data, adata, iv, tlen);
|
||
|
|
||
|
return w.concat(out.data, out.tag);
|
||
|
},
|
||
|
|
||
|
/** Decrypt in GCM 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} [tlen=128] The desired tag length, in bits.
|
||
|
* @return {bitArray} The decrypted data.
|
||
|
*/
|
||
|
decrypt: function (prf, ciphertext, iv, adata, tlen) {
|
||
|
var out, data = ciphertext.slice(0), tag, w=sjcl.bitArray, l=w.bitLength(data);
|
||
|
tlen = tlen || 128;
|
||
|
adata = adata || [];
|
||
|
|
||
|
// Slice tag out of data
|
||
|
if (tlen <= l) {
|
||
|
tag = w.bitSlice(data, l-tlen);
|
||
|
data = w.bitSlice(data, 0, l-tlen);
|
||
|
} else {
|
||
|
tag = data;
|
||
|
data = [];
|
||
|
}
|
||
|
|
||
|
// decrypt and tag
|
||
|
out = sjcl.mode.gcm._ctrMode(false, prf, data, adata, iv, tlen);
|
||
|
|
||
|
if (!w.equal(out.tag, tag)) {
|
||
|
throw new sjcl.exception.corrupt("gcm: tag doesn't match");
|
||
|
}
|
||
|
return out.data;
|
||
|
},
|
||
|
|
||
|
/* Compute the galois multiplication of X and Y
|
||
|
* @private
|
||
|
*/
|
||
|
_galoisMultiply: function (x, y) {
|
||
|
var i, j, xi, Zi, Vi, lsb_Vi, w=sjcl.bitArray, xor=w._xor4;
|
||
|
|
||
|
Zi = [0,0,0,0];
|
||
|
Vi = y.slice(0);
|
||
|
|
||
|
// Block size is 128 bits, run 128 times to get Z_128
|
||
|
for (i=0; i<128; i++) {
|
||
|
xi = (x[Math.floor(i/32)] & (1 << (31-i%32))) !== 0;
|
||
|
if (xi) {
|
||
|
// Z_i+1 = Z_i ^ V_i
|
||
|
Zi = xor(Zi, Vi);
|
||
|
}
|
||
|
|
||
|
// Store the value of LSB(V_i)
|
||
|
lsb_Vi = (Vi[3] & 1) !== 0;
|
||
|
|
||
|
// V_i+1 = V_i >> 1
|
||
|
for (j=3; j>0; j--) {
|
||
|
Vi[j] = (Vi[j] >>> 1) | ((Vi[j-1]&1) << 31);
|
||
|
}
|
||
|
Vi[0] = Vi[0] >>> 1;
|
||
|
|
||
|
// If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R
|
||
|
if (lsb_Vi) {
|
||
|
Vi[0] = Vi[0] ^ (0xe1 << 24);
|
||
|
}
|
||
|
}
|
||
|
return Zi;
|
||
|
},
|
||
|
|
||
|
_ghash: function(H, Y0, data) {
|
||
|
var Yi, i, l = data.length;
|
||
|
|
||
|
Yi = Y0.slice(0);
|
||
|
for (i=0; i<l; i+=4) {
|
||
|
Yi[0] ^= 0xffffffff&data[i];
|
||
|
Yi[1] ^= 0xffffffff&data[i+1];
|
||
|
Yi[2] ^= 0xffffffff&data[i+2];
|
||
|
Yi[3] ^= 0xffffffff&data[i+3];
|
||
|
Yi = sjcl.mode.gcm._galoisMultiply(Yi, H);
|
||
|
}
|
||
|
return Yi;
|
||
|
},
|
||
|
|
||
|
/** GCM CTR mode.
|
||
|
* Encrypt or decrypt data and tag with the prf in GCM-style CTR mode.
|
||
|
* @param {Boolean} encrypt True if encrypt, false if decrypt.
|
||
|
* @param {Object} prf The PRF.
|
||
|
* @param {bitArray} data The data to be encrypted or decrypted.
|
||
|
* @param {bitArray} iv The initialization vector.
|
||
|
* @param {bitArray} adata The associated data to be tagged.
|
||
|
* @param {Number} tlen The length of the tag, in bits.
|
||
|
*/
|
||
|
_ctrMode: function(encrypt, prf, data, adata, iv, tlen) {
|
||
|
var H, J0, S0, enc, i, ctr, tag, last, l, bl, abl, ivbl, w=sjcl.bitArray, xor=w._xor4;
|
||
|
|
||
|
// Calculate data lengths
|
||
|
l = data.length;
|
||
|
bl = w.bitLength(data);
|
||
|
abl = w.bitLength(adata);
|
||
|
ivbl = w.bitLength(iv);
|
||
|
|
||
|
// Calculate the parameters
|
||
|
H = prf.encrypt([0,0,0,0]);
|
||
|
if (ivbl === 96) {
|
||
|
J0 = iv.slice(0);
|
||
|
J0 = w.concat(J0, [1]);
|
||
|
} else {
|
||
|
J0 = sjcl.mode.gcm._ghash(H, [0,0,0,0], iv);
|
||
|
J0 = sjcl.mode.gcm._ghash(H, J0, [0,0,Math.floor(ivbl/0x100000000),ivbl&0xffffffff]);
|
||
|
}
|
||
|
S0 = sjcl.mode.gcm._ghash(H, [0,0,0,0], adata);
|
||
|
|
||
|
// Initialize ctr and tag
|
||
|
ctr = J0.slice(0);
|
||
|
tag = S0.slice(0);
|
||
|
|
||
|
// If decrypting, calculate hash
|
||
|
if (!encrypt) {
|
||
|
tag = sjcl.mode.gcm._ghash(H, S0, data);
|
||
|
}
|
||
|
|
||
|
// Encrypt all the 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];
|
||
|
}
|
||
|
data = w.clamp(data, bl);
|
||
|
|
||
|
// If encrypting, calculate hash
|
||
|
if (encrypt) {
|
||
|
tag = sjcl.mode.gcm._ghash(H, S0, data);
|
||
|
}
|
||
|
|
||
|
// Calculate last block from bit lengths, ugly because bitwise operations are 32-bit
|
||
|
last = [
|
||
|
Math.floor(abl/0x100000000), abl&0xffffffff,
|
||
|
Math.floor(bl/0x100000000), bl&0xffffffff
|
||
|
];
|
||
|
|
||
|
// Calculate the final tag block
|
||
|
tag = sjcl.mode.gcm._ghash(H, tag, last);
|
||
|
enc = prf.encrypt(J0);
|
||
|
tag[0] ^= enc[0];
|
||
|
tag[1] ^= enc[1];
|
||
|
tag[2] ^= enc[2];
|
||
|
tag[3] ^= enc[3];
|
||
|
|
||
|
return { tag:w.bitSlice(tag, 0, tlen), data:data };
|
||
|
}
|
||
|
};
|