UNPKG

sjcl-aws

Version:

Stanford Javascript Crypto Library

188 lines (161 loc) 5.19 kB
/** @fileOverview GCM mode implementation. * * @author Juho Vähä-Herttua */ /** * Galois/Counter mode. * @namespace */ 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; // 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 }; } };