/** * @preserve A JavaScript implementation of the SHA family of hashes, as * defined in FIPS PUB 180-4 and FIPS PUB 202, as well as the corresponding * HMAC implementation as defined in FIPS PUB 198a * * Copyright Brian Turek 2008-2017 * Distributed under the BSD License * See http://caligatio.github.com/jsSHA/ for more information * * Several functions taken from Paul Johnston */ /*jslint bitwise: true, multivar: true, for: true, this: true, sub: true, esversion: 3 */ /** * SUPPORTED_ALGS is the stub for a compile flag that will cause pruning of * functions that are not needed when a limited number of SHA families are * selected * * @define {number} ORed value of SHA variants to be supported * 1 = SHA-1, 2 = SHA-224/SHA-256, 4 = SHA-384/SHA-512, 8 = SHA3 */ var SUPPORTED_ALGS = 8 | 4 | 2 | 1; (function (global) { "use strict"; /* Globals */ var TWO_PWR_32 = 4294967296; /** * Int_64 is a object for 2 32-bit numbers emulating a 64-bit number * * @private * @constructor * @this {Int_64} * @param {number} msint_32 The most significant 32-bits of a 64-bit number * @param {number} lsint_32 The least significant 32-bits of a 64-bit number */ function Int_64(msint_32, lsint_32) { this.highOrder = msint_32; this.lowOrder = lsint_32; } /** * Convert a string to an array of big-endian words * * There is a known bug with an odd number of existing bytes and using a * UTF-16 encoding. However, this function is used such that the existing * bytes are always a result of a previous UTF-16 str2packed call and * therefore there should never be an odd number of existing bytes * * @private * @param {string} str String to be converted to binary representation * @param {string} utfType The Unicode type, UTF8 or UTF16BE, UTF16LE, to * use to encode the source string * @param {Array} existingPacked A packed int array of bytes to * append the results to * @param {number} existingPackedLen The number of bits in the existingPacked * array * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ function str2packed(str, utfType, existingPacked, existingPackedLen, bigEndianMod) { var packed, codePnt, codePntArr, byteCnt = 0, i, j, existingByteLen, intOffset, byteOffset, shiftModifier, transposeBytes; packed = existingPacked || [0]; existingPackedLen = existingPackedLen || 0; existingByteLen = existingPackedLen >>> 3; if ("UTF8" === utfType) { shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < str.length; i += 1) { codePnt = str.charCodeAt(i); codePntArr = []; if (0x80 > codePnt) { codePntArr.push(codePnt); } else if (0x800 > codePnt) { codePntArr.push(0xC0 | (codePnt >>> 6)); codePntArr.push(0x80 | (codePnt & 0x3F)); } else if ((0xd800 > codePnt) || (0xe000 <= codePnt)) { codePntArr.push( 0xe0 | (codePnt >>> 12), 0x80 | ((codePnt >>> 6) & 0x3f), 0x80 | (codePnt & 0x3f) ); } else { i += 1; codePnt = 0x10000 + (((codePnt & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff)); codePntArr.push( 0xf0 | (codePnt >>> 18), 0x80 | ((codePnt >>> 12) & 0x3f), 0x80 | ((codePnt >>> 6) & 0x3f), 0x80 | (codePnt & 0x3f) ); } for (j = 0; j < codePntArr.length; j += 1) { byteOffset = byteCnt + existingByteLen; intOffset = byteOffset >>> 2; while (packed.length <= intOffset) { packed.push(0); } /* Known bug kicks in here */ packed[intOffset] |= codePntArr[j] << (8 * (shiftModifier + bigEndianMod * (byteOffset % 4))); byteCnt += 1; } } } else if (("UTF16BE" === utfType) || "UTF16LE" === utfType) { shiftModifier = (bigEndianMod === -1) ? 2 : 0; /* Internally strings are UTF-16BE so transpose bytes under two conditions: * need LE and not switching endianness due to SHA-3 * need BE and switching endianness due to SHA-3 */ transposeBytes = (("UTF16LE" === utfType) && (bigEndianMod !== 1)) || (("UTF16LE" !== utfType) && (bigEndianMod === 1)); for (i = 0; i < str.length; i += 1) { codePnt = str.charCodeAt(i); if (transposeBytes === true) { j = codePnt & 0xFF; codePnt = (j << 8) | (codePnt >>> 8); } byteOffset = byteCnt + existingByteLen; intOffset = byteOffset >>> 2; while (packed.length <= intOffset) { packed.push(0); } packed[intOffset] |= codePnt << (8 * (shiftModifier + bigEndianMod * (byteOffset % 4))); byteCnt += 2; } } return {"value" : packed, "binLen" : byteCnt * 8 + existingPackedLen}; } /** * Convert a hex string to an array of big-endian words * * @private * @param {string} str String to be converted to binary representation * @param {Array} existingPacked A packed int array of bytes to * append the results to * @param {number} existingPackedLen The number of bits in the existingPacked * array * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ function hex2packed(str, existingPacked, existingPackedLen, bigEndianMod) { var packed, length = str.length, i, num, intOffset, byteOffset, existingByteLen, shiftModifier; if (0 !== (length % 2)) { throw new Error("String of HEX type must be in byte increments"); } packed = existingPacked || [0]; existingPackedLen = existingPackedLen || 0; existingByteLen = existingPackedLen >>> 3; shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < length; i += 2) { num = parseInt(str.substr(i, 2), 16); if (!isNaN(num)) { byteOffset = (i >>> 1) + existingByteLen; intOffset = byteOffset >>> 2; while (packed.length <= intOffset) { packed.push(0); } packed[intOffset] |= num << (8 * (shiftModifier + bigEndianMod * (byteOffset % 4))); } else { throw new Error("String of HEX type contains invalid characters"); } } return {"value" : packed, "binLen" : length * 4 + existingPackedLen}; } /** * Convert a string of raw bytes to an array of big-endian words * * @private * @param {string} str String of raw bytes to be converted to binary representation * @param {Array} existingPacked A packed int array of bytes to * append the results to * @param {number} existingPackedLen The number of bits in the existingPacked * array * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ function bytes2packed(str, existingPacked, existingPackedLen, bigEndianMod) { var packed, codePnt, i, existingByteLen, intOffset, byteOffset, shiftModifier; packed = existingPacked || [0]; existingPackedLen = existingPackedLen || 0; existingByteLen = existingPackedLen >>> 3; shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < str.length; i += 1) { codePnt = str.charCodeAt(i); byteOffset = i + existingByteLen; intOffset = byteOffset >>> 2; if (packed.length <= intOffset) { packed.push(0); } packed[intOffset] |= codePnt << (8 * (shiftModifier + bigEndianMod * (byteOffset % 4))); } return {"value" : packed, "binLen" : str.length * 8 + existingPackedLen}; } /** * Convert a base-64 string to an array of big-endian words * * @private * @param {string} str String to be converted to binary representation * @param {Array} existingPacked A packed int array of bytes to * append the results to * @param {number} existingPackedLen The number of bits in the existingPacked * array * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ function b642packed(str, existingPacked, existingPackedLen, bigEndianMod) { var packed, byteCnt = 0, index, i, j, tmpInt, strPart, firstEqual, b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", existingByteLen, intOffset, byteOffset, shiftModifier; if (-1 === str.search(/^[a-zA-Z0-9=+\/]+$/)) { throw new Error("Invalid character in base-64 string"); } firstEqual = str.indexOf("="); str = str.replace(/\=/g, ""); if ((-1 !== firstEqual) && (firstEqual < str.length)) { throw new Error("Invalid '=' found in base-64 string"); } packed = existingPacked || [0]; existingPackedLen = existingPackedLen || 0; existingByteLen = existingPackedLen >>> 3; shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < str.length; i += 4) { strPart = str.substr(i, 4); tmpInt = 0; for (j = 0; j < strPart.length; j += 1) { index = b64Tab.indexOf(strPart[j]); tmpInt |= index << (18 - (6 * j)); } for (j = 0; j < strPart.length - 1; j += 1) { byteOffset = byteCnt + existingByteLen; intOffset = byteOffset >>> 2; while (packed.length <= intOffset) { packed.push(0); } packed[intOffset] |= ((tmpInt >>> (16 - (j * 8))) & 0xFF) << (8 * (shiftModifier + bigEndianMod * (byteOffset % 4))); byteCnt += 1; } } return {"value" : packed, "binLen" : byteCnt * 8 + existingPackedLen}; } /** * Convert an ArrayBuffer to an array of big-endian words * * @private * @param {ArrayBuffer} arr ArrayBuffer to be converted to binary * representation * @param {Array} existingPacked A packed int array of bytes to * append the results to * @param {number} existingPackedLen The number of bits in the existingPacked * array * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ function arraybuffer2packed(arr, existingPacked, existingPackedLen, bigEndianMod) { var packed, i, existingByteLen, intOffset, byteOffset, shiftModifier, arrView; packed = existingPacked || [0]; existingPackedLen = existingPackedLen || 0; existingByteLen = existingPackedLen >>> 3; shiftModifier = (bigEndianMod === -1) ? 3 : 0; arrView = new Uint8Array(arr); for (i = 0; i < arr.byteLength; i += 1) { byteOffset = i + existingByteLen; intOffset = byteOffset >>> 2; if (packed.length <= intOffset) { packed.push(0); } packed[intOffset] |= arrView[i] << (8 * (shiftModifier + bigEndianMod * (byteOffset % 4))); } return {"value" : packed, "binLen" : arr.byteLength * 8 + existingPackedLen}; } /** * Convert an array of big-endian words to a hex string. * * @private * @param {Array} packed Array of integers to be converted to * hexidecimal representation * @param {number} outputLength Length of output in bits * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @param {{outputUpper : boolean, b64Pad : string}} formatOpts Hash list * containing validated output formatting options * @return {string} Hexidecimal representation of the parameter in string * form */ function packed2hex(packed, outputLength, bigEndianMod, formatOpts) { var hex_tab = "0123456789abcdef", str = "", length = outputLength / 8, i, srcByte, shiftModifier; shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < length; i += 1) { /* The below is more than a byte but it gets taken care of later */ srcByte = packed[i >>> 2] >>> (8 * (shiftModifier + bigEndianMod * (i % 4))); str += hex_tab.charAt((srcByte >>> 4) & 0xF) + hex_tab.charAt(srcByte & 0xF); } return (formatOpts["outputUpper"]) ? str.toUpperCase() : str; } /** * Convert an array of big-endian words to a base-64 string * * @private * @param {Array} packed Array of integers to be converted to * base-64 representation * @param {number} outputLength Length of output in bits * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @param {{outputUpper : boolean, b64Pad : string}} formatOpts Hash list * containing validated output formatting options * @return {string} Base-64 encoded representation of the parameter in * string form */ function packed2b64(packed, outputLength, bigEndianMod, formatOpts) { var str = "", length = outputLength / 8, i, j, triplet, int1, int2, shiftModifier, b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < length; i += 3) { int1 = ((i + 1) < length) ? packed[(i + 1) >>> 2] : 0; int2 = ((i + 2) < length) ? packed[(i + 2) >>> 2] : 0; triplet = (((packed[i >>> 2] >>> (8 * (shiftModifier + bigEndianMod * (i % 4)))) & 0xFF) << 16) | (((int1 >>> (8 * (shiftModifier + bigEndianMod * ((i + 1) % 4)))) & 0xFF) << 8) | ((int2 >>> (8 * (shiftModifier + bigEndianMod * ((i + 2) % 4)))) & 0xFF); for (j = 0; j < 4; j += 1) { if (i * 8 + j * 6 <= outputLength) { str += b64Tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); } else { str += formatOpts["b64Pad"]; } } } return str; } /** * Convert an array of big-endian words to raw bytes string * * @private * @param {Array} packed Array of integers to be converted to * a raw bytes string representation * @param {number} outputLength Length of output in bits * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {string} Raw bytes representation of the parameter in string * form */ function packed2bytes(packed, outputLength, bigEndianMod) { var str = "", length = outputLength / 8, i, srcByte, shiftModifier; shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < length; i += 1) { srcByte = (packed[i >>> 2] >>> (8 * (shiftModifier + bigEndianMod * (i % 4)))) & 0xFF; str += String.fromCharCode(srcByte); } return str; } /** * Convert an array of big-endian words to an ArrayBuffer * * @private * @param {Array} packed Array of integers to be converted to * an ArrayBuffer * @param {number} outputLength Length of output in bits * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {ArrayBuffer} Raw bytes representation of the parameter in an * ArrayBuffer */ function packed2arraybuffer(packed, outputLength, bigEndianMod) { var length = outputLength / 8, i, retVal = new ArrayBuffer(length), shiftModifier, arrView; arrView = new Uint8Array(retVal); shiftModifier = (bigEndianMod === -1) ? 3 : 0; for (i = 0; i < length; i += 1) { arrView[i] = (packed[i >>> 2] >>> (8 * (shiftModifier + bigEndianMod * (i % 4)))) & 0xFF; } return retVal; } /** * Validate hash list containing output formatting options, ensuring * presence of every option or adding the default value * * @private * @param {{outputUpper : (boolean|undefined), b64Pad : (string|undefined), * shakeLen : (number|undefined)}=} options Hash list of output formatting options * @return {{outputUpper : boolean, b64Pad : string, shakeLen : number}} Validated * hash list containing output formatting options */ function getOutputOpts(options) { var retVal = {"outputUpper" : false, "b64Pad" : "=", "shakeLen" : -1}, outputOptions; outputOptions = options || {}; retVal["outputUpper"] = outputOptions["outputUpper"] || false; if (true === outputOptions.hasOwnProperty("b64Pad")) { retVal["b64Pad"] = outputOptions["b64Pad"]; } if ((true === outputOptions.hasOwnProperty("shakeLen")) && ((8 & SUPPORTED_ALGS) !== 0)) { if (outputOptions["shakeLen"] % 8 !== 0) { throw new Error("shakeLen must be a multiple of 8"); } retVal["shakeLen"] = outputOptions["shakeLen"]; } if ("boolean" !== typeof(retVal["outputUpper"])) { throw new Error("Invalid outputUpper formatting option"); } if ("string" !== typeof(retVal["b64Pad"])) { throw new Error("Invalid b64Pad formatting option"); } return retVal; } /** * Function that takes an input format and UTF encoding and returns the * appropriate function used to convert the input. * * @private * @param {string} format The format of the string to be converted * @param {string} utfType The string encoding to use (UTF8, UTF16BE, * UTF16LE) * @param {number} bigEndianMod Modifier for whether hash function is * big or small endian * @return {function((string|ArrayBuffer), Array=, number=): {value : * Array, binLen : number}} Function that will convert an input * string to a packed int array */ function getStrConverter(format, utfType, bigEndianMod) { var retVal; /* Validate encoding */ switch (utfType) { case "UTF8": /* Fallthrough */ case "UTF16BE": /* Fallthrough */ case "UTF16LE": /* Fallthrough */ break; default: throw new Error("encoding must be UTF8, UTF16BE, or UTF16LE"); } /* Map inputFormat to the appropriate converter */ switch (format) { case "HEX": /** * @param {string} str String of raw bytes to be converted to binary representation * @param {Array} existingBin A packed int array of bytes to * append the results to * @param {number} existingBinLen The number of bits in the existingBin * array * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ retVal = function(str, existingBin, existingBinLen) { return hex2packed(str, existingBin, existingBinLen, bigEndianMod); }; break; case "TEXT": /** * @param {string} str String of raw bytes to be converted to binary representation * @param {Array} existingBin A packed int array of bytes to * append the results to * @param {number} existingBinLen The number of bits in the existingBin * array * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ retVal = function(str, existingBin, existingBinLen) { return str2packed(str, utfType, existingBin, existingBinLen, bigEndianMod); }; break; case "B64": /** * @param {string} str String of raw bytes to be converted to binary representation * @param {Array} existingBin A packed int array of bytes to * append the results to * @param {number} existingBinLen The number of bits in the existingBin * array * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ retVal = function(str, existingBin, existingBinLen) { return b642packed(str, existingBin, existingBinLen, bigEndianMod); }; break; case "BYTES": /** * @param {string} str String of raw bytes to be converted to binary representation * @param {Array} existingBin A packed int array of bytes to * append the results to * @param {number} existingBinLen The number of bits in the existingBin * array * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ retVal = function(str, existingBin, existingBinLen) { return bytes2packed(str, existingBin, existingBinLen, bigEndianMod); }; break; case "ARRAYBUFFER": try { retVal = new ArrayBuffer(0); } catch(ignore) { throw new Error("ARRAYBUFFER not supported by this environment"); } /** * @param {ArrayBuffer} arr ArrayBuffer to be converted to binary * representation * @param {Array} existingBin A packed int array of bytes to * append the results to * @param {number} existingBinLen The number of bits in the existingBin * array * @return {{value : Array, binLen : number}} Hash list where * "value" contains the output number array and "binLen" is the binary * length of "value" */ retVal = function(arr, existingBin, existingBinLen) { return arraybuffer2packed(arr, existingBin, existingBinLen, bigEndianMod); }; break; default: throw new Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER"); } return retVal; } /** * The 32-bit implementation of circular rotate left * * @private * @param {number} x The 32-bit integer argument * @param {number} n The number of bits to shift * @return {number} The x shifted circularly by n bits */ function rotl_32(x, n) { return (x << n) | (x >>> (32 - n)); } /** * The 64-bit implementation of circular rotate left * * @private * @param {Int_64} x The 64-bit integer argument * @param {number} n The number of bits to shift * @return {Int_64} The x shifted circularly by n bits */ function rotl_64(x, n) { if (n > 32) { n = n - 32; return new Int_64( x.lowOrder << n | x.highOrder >>> (32 - n), x.highOrder << n | x.lowOrder >>> (32 - n) ); } else if (0 !== n) { return new Int_64( x.highOrder << n | x.lowOrder >>> (32 - n), x.lowOrder << n | x.highOrder >>> (32 - n) ); } else { return x; } } /** * The 32-bit implementation of circular rotate right * * @private * @param {number} x The 32-bit integer argument * @param {number} n The number of bits to shift * @return {number} The x shifted circularly by n bits */ function rotr_32(x, n) { return (x >>> n) | (x << (32 - n)); } /** * The 64-bit implementation of circular rotate right * * @private * @param {Int_64} x The 64-bit integer argument * @param {number} n The number of bits to shift * @return {Int_64} The x shifted circularly by n bits */ function rotr_64(x, n) { var retVal = null, tmp = new Int_64(x.highOrder, x.lowOrder); if (32 >= n) { retVal = new Int_64( (tmp.highOrder >>> n) | ((tmp.lowOrder << (32 - n)) & 0xFFFFFFFF), (tmp.lowOrder >>> n) | ((tmp.highOrder << (32 - n)) & 0xFFFFFFFF) ); } else { retVal = new Int_64( (tmp.lowOrder >>> (n - 32)) | ((tmp.highOrder << (64 - n)) & 0xFFFFFFFF), (tmp.highOrder >>> (n - 32)) | ((tmp.lowOrder << (64 - n)) & 0xFFFFFFFF) ); } return retVal; } /** * The 32-bit implementation of shift right * * @private * @param {number} x The 32-bit integer argument * @param {number} n The number of bits to shift * @return {number} The x shifted by n bits */ function shr_32(x, n) { return x >>> n; } /** * The 64-bit implementation of shift right * * @private * @param {Int_64} x The 64-bit integer argument * @param {number} n The number of bits to shift * @return {Int_64} The x shifted by n bits */ function shr_64(x, n) { var retVal = null; if (32 >= n) { retVal = new Int_64( x.highOrder >>> n, x.lowOrder >>> n | ((x.highOrder << (32 - n)) & 0xFFFFFFFF) ); } else { retVal = new Int_64( 0, x.highOrder >>> (n - 32) ); } return retVal; } /** * The 32-bit implementation of the NIST specified Parity function * * @private * @param {number} x The first 32-bit integer argument * @param {number} y The second 32-bit integer argument * @param {number} z The third 32-bit integer argument * @return {number} The NIST specified output of the function */ function parity_32(x, y, z) { return x ^ y ^ z; } /** * The 32-bit implementation of the NIST specified Ch function * * @private * @param {number} x The first 32-bit integer argument * @param {number} y The second 32-bit integer argument * @param {number} z The third 32-bit integer argument * @return {number} The NIST specified output of the function */ function ch_32(x, y, z) { return (x & y) ^ (~x & z); } /** * The 64-bit implementation of the NIST specified Ch function * * @private * @param {Int_64} x The first 64-bit integer argument * @param {Int_64} y The second 64-bit integer argument * @param {Int_64} z The third 64-bit integer argument * @return {Int_64} The NIST specified output of the function */ function ch_64(x, y, z) { return new Int_64( (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder), (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder) ); } /** * The 32-bit implementation of the NIST specified Maj function * * @private * @param {number} x The first 32-bit integer argument * @param {number} y The second 32-bit integer argument * @param {number} z The third 32-bit integer argument * @return {number} The NIST specified output of the function */ function maj_32(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } /** * The 64-bit implementation of the NIST specified Maj function * * @private * @param {Int_64} x The first 64-bit integer argument * @param {Int_64} y The second 64-bit integer argument * @param {Int_64} z The third 64-bit integer argument * @return {Int_64} The NIST specified output of the function */ function maj_64(x, y, z) { return new Int_64( (x.highOrder & y.highOrder) ^ (x.highOrder & z.highOrder) ^ (y.highOrder & z.highOrder), (x.lowOrder & y.lowOrder) ^ (x.lowOrder & z.lowOrder) ^ (y.lowOrder & z.lowOrder) ); } /** * The 32-bit implementation of the NIST specified Sigma0 function * * @private * @param {number} x The 32-bit integer argument * @return {number} The NIST specified output of the function */ function sigma0_32(x) { return rotr_32(x, 2) ^ rotr_32(x, 13) ^ rotr_32(x, 22); } /** * The 64-bit implementation of the NIST specified Sigma0 function * * @private * @param {Int_64} x The 64-bit integer argument * @return {Int_64} The NIST specified output of the function */ function sigma0_64(x) { var rotr28 = rotr_64(x, 28), rotr34 = rotr_64(x, 34), rotr39 = rotr_64(x, 39); return new Int_64( rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder, rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder); } /** * The 32-bit implementation of the NIST specified Sigma1 function * * @private * @param {number} x The 32-bit integer argument * @return {number} The NIST specified output of the function */ function sigma1_32(x) { return rotr_32(x, 6) ^ rotr_32(x, 11) ^ rotr_32(x, 25); } /** * The 64-bit implementation of the NIST specified Sigma1 function * * @private * @param {Int_64} x The 64-bit integer argument * @return {Int_64} The NIST specified output of the function */ function sigma1_64(x) { var rotr14 = rotr_64(x, 14), rotr18 = rotr_64(x, 18), rotr41 = rotr_64(x, 41); return new Int_64( rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder, rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder); } /** * The 32-bit implementation of the NIST specified Gamma0 function * * @private * @param {number} x The 32-bit integer argument * @return {number} The NIST specified output of the function */ function gamma0_32(x) { return rotr_32(x, 7) ^ rotr_32(x, 18) ^ shr_32(x, 3); } /** * The 64-bit implementation of the NIST specified Gamma0 function * * @private * @param {Int_64} x The 64-bit integer argument * @return {Int_64} The NIST specified output of the function */ function gamma0_64(x) { var rotr1 = rotr_64(x, 1), rotr8 = rotr_64(x, 8), shr7 = shr_64(x, 7); return new Int_64( rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder, rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder ); } /** * The 32-bit implementation of the NIST specified Gamma1 function * * @private * @param {number} x The 32-bit integer argument * @return {number} The NIST specified output of the function */ function gamma1_32(x) { return rotr_32(x, 17) ^ rotr_32(x, 19) ^ shr_32(x, 10); } /** * The 64-bit implementation of the NIST specified Gamma1 function * * @private * @param {Int_64} x The 64-bit integer argument * @return {Int_64} The NIST specified output of the function */ function gamma1_64(x) { var rotr19 = rotr_64(x, 19), rotr61 = rotr_64(x, 61), shr6 = shr_64(x, 6); return new Int_64( rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder, rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder ); } /** * Add two 32-bit integers, wrapping at 2^32. This uses 16-bit operations * internally to work around bugs in some JS interpreters. * * @private * @param {number} a The first 32-bit integer argument to be added * @param {number} b The second 32-bit integer argument to be added * @return {number} The sum of a + b */ function safeAdd_32_2(a, b) { var lsw = (a & 0xFFFF) + (b & 0xFFFF), msw = (a >>> 16) + (b >>> 16) + (lsw >>> 16); return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); } /** * Add four 32-bit integers, wrapping at 2^32. This uses 16-bit operations * internally to work around bugs in some JS interpreters. * * @private * @param {number} a The first 32-bit integer argument to be added * @param {number} b The second 32-bit integer argument to be added * @param {number} c The third 32-bit integer argument to be added * @param {number} d The fourth 32-bit integer argument to be added * @return {number} The sum of a + b + c + d */ function safeAdd_32_4(a, b, c, d) { var lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF), msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + (lsw >>> 16); return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); } /** * Add five 32-bit integers, wrapping at 2^32. This uses 16-bit operations * internally to work around bugs in some JS interpreters. * * @private * @param {number} a The first 32-bit integer argument to be added * @param {number} b The second 32-bit integer argument to be added * @param {number} c The third 32-bit integer argument to be added * @param {number} d The fourth 32-bit integer argument to be added * @param {number} e The fifth 32-bit integer argument to be added * @return {number} The sum of a + b + c + d + e */ function safeAdd_32_5(a, b, c, d, e) { var lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF) + (e & 0xFFFF), msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + (e >>> 16) + (lsw >>> 16); return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); } /** * Add two 64-bit integers, wrapping at 2^64. This uses 16-bit operations * internally to work around bugs in some JS interpreters. * * @private * @param {Int_64} x The first 64-bit integer argument to be added * @param {Int_64} y The second 64-bit integer argument to be added * @return {Int_64} The sum of x + y */ function safeAdd_64_2(x, y) { var lsw, msw, lowOrder, highOrder; lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF); msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16); lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16); msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16); highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); return new Int_64(highOrder, lowOrder); } /** * Add four 64-bit integers, wrapping at 2^64. This uses 16-bit operations * internally to work around bugs in some JS interpreters. * * @private * @param {Int_64} a The first 64-bit integer argument to be added * @param {Int_64} b The second 64-bit integer argument to be added * @param {Int_64} c The third 64-bit integer argument to be added * @param {Int_64} d The fouth 64-bit integer argument to be added * @return {Int_64} The sum of a + b + c + d */ function safeAdd_64_4(a, b, c, d) { var lsw, msw, lowOrder, highOrder; lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF); msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16); lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16); msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16); highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); return new Int_64(highOrder, lowOrder); } /** * Add five 64-bit integers, wrapping at 2^64. This uses 16-bit operations * internally to work around bugs in some JS interpreters. * * @private * @param {Int_64} a The first 64-bit integer argument to be added * @param {Int_64} b The second 64-bit integer argument to be added * @param {Int_64} c The third 64-bit integer argument to be added * @param {Int_64} d The fouth 64-bit integer argument to be added * @param {Int_64} e The fouth 64-bit integer argument to be added * @return {Int_64} The sum of a + b + c + d + e */ function safeAdd_64_5(a, b, c, d, e) { var lsw, msw, lowOrder, highOrder; lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + (e.lowOrder & 0xFFFF); msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + (lsw >>> 16); lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (e.highOrder & 0xFFFF) + (msw >>> 16); msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (e.highOrder >>> 16) + (lsw >>> 16); highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); return new Int_64(highOrder, lowOrder); } /** * XORs two given arguments. * * @private * @param {Int_64} a First argument to be XORed * @param {Int_64} b Second argument to be XORed * @return {Int_64} The XOR of the arguments */ function xor_64_2(a, b) { return new Int_64( a.highOrder ^ b.highOrder, a.lowOrder ^ b.lowOrder ); } /** * XORs five given arguments. * * @private * @param {Int_64} a First argument to be XORed * @param {Int_64} b Second argument to be XORed * @param {Int_64} c Third argument to be XORed * @param {Int_64} d Fourth argument to be XORed * @param {Int_64} e Fifth argument to be XORed * @return {Int_64} The XOR of the arguments */ function xor_64_5(a, b, c, d, e) { return new Int_64( a.highOrder ^ b.highOrder ^ c.highOrder ^ d.highOrder ^ e.highOrder, a.lowOrder ^ b.lowOrder ^ c.lowOrder ^ d.lowOrder ^ e.lowOrder ); } /** * Returns a clone of the given SHA3 state * * @private * @param {Array>} state The state to be cloned * @return {Array>} The cloned state */ function cloneSHA3State(state) { var clone = [], i; for (i = 0; i < 5; i += 1) { clone[i] = state[i].slice(); } return clone; } /** * Gets the state values for the specified SHA variant * * @param {string} variant The SHA variant * @return {Array>} The initial state values */ function getNewState(variant) { var retVal = [], H_trunc, H_full, i; if (("SHA-1" === variant) && ((1 & SUPPORTED_ALGS) !== 0)) { retVal = [ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 ]; } else if ((variant.lastIndexOf("SHA-", 0) === 0) && ((6 & SUPPORTED_ALGS) !== 0)) { H_trunc = [ 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 ]; H_full = [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ]; switch (variant) { case "SHA-224": retVal = H_trunc; break; case "SHA-256": retVal = H_full; break; case "SHA-384": retVal = [ new Int_64(0xcbbb9d5d, H_trunc[0]), new Int_64(0x0629a292a, H_trunc[1]), new Int_64(0x9159015a, H_trunc[2]), new Int_64(0x0152fecd8, H_trunc[3]), new Int_64(0x67332667, H_trunc[4]), new Int_64(0x98eb44a87, H_trunc[5]), new Int_64(0xdb0c2e0d, H_trunc[6]), new Int_64(0x047b5481d, H_trunc[7]) ]; break; case "SHA-512": retVal = [ new Int_64(H_full[0], 0xf3bcc908), new Int_64(H_full[1], 0x84caa73b), new Int_64(H_full[2], 0xfe94f82b), new Int_64(H_full[3], 0x5f1d36f1), new Int_64(H_full[4], 0xade682d1), new Int_64(H_full[5], 0x2b3e6c1f), new Int_64(H_full[6], 0xfb41bd6b), new Int_64(H_full[7], 0x137e2179) ]; break; default: throw new Error("Unknown SHA variant"); } } else if (((variant.lastIndexOf("SHA3-", 0) === 0) || (variant.lastIndexOf("SHAKE", 0) === 0)) && ((8 & SUPPORTED_ALGS) !== 0)) { for (i = 0; i < 5; i += 1) { retVal[i] = [new Int_64(0, 0), new Int_64(0, 0), new Int_64(0, 0), new Int_64(0, 0), new Int_64(0, 0)]; } } else { throw new Error("No SHA variants supported"); } return retVal; } /** * Performs a round of SHA-1 hashing over a 512-byte block * * @private * @param {Array} block The binary array representation of the * block to hash * @param {Array} H The intermediate H values from a previous * round * @return {Array} The resulting H values */ function roundSHA1(block, H) { var W = [], a, b, c, d, e, T, ch = ch_32, parity = parity_32, maj = maj_32, rotl = rotl_32, safeAdd_2 = safeAdd_32_2, t, safeAdd_5 = safeAdd_32_5; a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; for (t = 0; t < 80; t += 1) { if (t < 16) { W[t] = block[t]; } else { W[t] = rotl(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); } if (t < 20) { T = safeAdd_5(rotl(a, 5), ch(b, c, d), e, 0x5a827999, W[t]); } else if (t < 40) { T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, 0x6ed9eba1, W[t]); } else if (t < 60) { T = safeAdd_5(rotl(a, 5), maj(b, c, d), e, 0x8f1bbcdc, W[t]); } else { T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, 0xca62c1d6, W[t]); } e = d; d = c; c = rotl(b, 30); b = a; a = T; } H[0] = safeAdd_2(a, H[0]); H[1] = safeAdd_2(b, H[1]); H[2] = safeAdd_2(c, H[2]); H[3] = safeAdd_2(d, H[3]); H[4] = safeAdd_2(e, H[4]); return H; } /** * Finalizes the SHA-1 hash * * @private * @param {Array} remainder Any leftover unprocessed packed ints * that still need to be processed * @param {number} remainderBinLen The number of bits in remainder * @param {number} processedBinLen The number of bits already * processed * @param {Array} H The intermediate H values from a previous * round * @param {number} outputLen Unused for this variant * @return {Array} The array of integers representing the SHA-1 * hash of message */ function finalizeSHA1(remainder, remainderBinLen, processedBinLen, H, outputLen) { var i, appendedMessageLength, offset, totalLen; /* The 65 addition is a hack but it works. The correct number is actually 72 (64 + 8) but the below math fails if remainderBinLen + 72 % 512 = 0. Since remainderBinLen % 8 = 0, "shorting" the addition is OK. */ offset = (((remainderBinLen + 65) >>> 9) << 4) + 15; while (remainder.length <= offset) { remainder.push(0); } /* Append '1' at the end of the binary string */ remainder[remainderBinLen >>> 5] |= 0x80 << (24 - (remainderBinLen % 32)); /* Append length of binary string in the position such that the new * length is a multiple of 512. Logic does not work for even multiples * of 512 but there can never be even multiples of 512. JavaScript * numbers are limited to 2^53 so it's "safe" to treat the totalLen as * a 64-bit integer. */ totalLen = remainderBinLen + processedBinLen; remainder[offset] = totalLen & 0xFFFFFFFF; /* Bitwise operators treat the operand as a 32-bit number so need to * use hacky division and round to get access to upper 32-ish bits */ remainder[offset - 1] = (totalLen / TWO_PWR_32) | 0; appendedMessageLength = remainder.length; /* This will always be at least 1 full chunk */ for (i = 0; i < appendedMessageLength; i += 16) { H = roundSHA1(remainder.slice(i, i + 16), H); } return H; } /* Put this here so the K arrays aren't put on the stack for every block */ var K_sha2, K_sha512, r_sha3, rc_sha3; if ((6 & SUPPORTED_ALGS) !== 0) { K_sha2 = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 ]; if ((4 & SUPPORTED_ALGS) !== 0) { K_sha512 = [ new Int_64(K_sha2[ 0], 0xd728ae22), new Int_64(K_sha2[ 1], 0x23ef65cd), new Int_64(K_sha2[ 2], 0xec4d3b2f), new Int_64(K_sha2[ 3], 0x8189dbbc), new Int_64(K_sha2[ 4], 0xf348b538), new Int_64(K_sha2[ 5], 0xb605d019), new Int_64(K_sha2[ 6], 0xaf194f9b), new Int_64(K_sha2[ 7], 0xda6d8118), new Int_64(K_sha2[ 8], 0xa3030242), new Int_64(K_sha2[ 9], 0x45706fbe), new Int_64(K_sha2[10], 0x4ee4b28c), new Int_64(K_sha2[11], 0xd5ffb4e2), new Int_64(K_sha2[12], 0xf27b896f), new Int_64(K_sha2[13], 0x3b1696b1), new Int_64(K_sha2[14], 0x25c71235), new Int_64(K_sha2[15], 0xcf692694), new Int_64(K_sha2[16], 0x9ef14ad2), new Int_64(K_sha2[17], 0x384f25e3), new Int_64(K_sha2[18], 0x8b8cd5b5), new Int_64(K_sha2[19], 0x77ac9c65), new Int_64(K_sha2[20], 0x592b0275), new Int_64(K_sha2[21], 0x6ea6e483), new Int_64(K_sha2[22], 0xbd41fbd4), new Int_64(K_sha2[23], 0x831153b5), new Int_64(K_sha2[24], 0xee66dfab), new Int_64(K_sha2[25], 0x2db43210), new Int_64(K_sha2[26], 0x98fb213f), new Int_64(K_sha2[27], 0xbeef0ee4), new Int_64(K_sha2[28], 0x3da88fc2), new Int_64(K_sha2[29], 0x930aa725), new Int_64(K_sha2[30], 0xe003826f), new Int_64(K_sha2[31], 0x0a0e6e70), new Int_64(K_sha2[32], 0x46d22ffc), new Int_64(K_sha2[33], 0x5c26c926), new Int_64(K_sha2[34], 0x5ac42aed), new Int_64(K_sha2[35], 0x9d95b3df), new Int_64(K_sha2[36], 0x8baf63de), new Int_64(K_sha2[37], 0x3c77b2a8), new Int_64(K_sha2[38], 0x47edaee6), new Int_64(K_sha2[39], 0x1482353b), new Int_64(K_sha2[40], 0x4cf10364), new Int_64(K_sha2[41], 0xbc423001), new Int_64(K_sha2[42], 0xd0f89791), new Int_64(K_sha2[43], 0x0654be30), new Int_64(K_sha2[44], 0xd6ef5218), new Int_64(K_sha2[45], 0x5565a910), new Int_64(K_sha2[46], 0x5771202a), new Int_64(K_sha2[47], 0x32bbd1b8), new Int_64(K_sha2[48], 0xb8d2d0c8), new Int_64(K_sha2[49], 0x5141ab53), new Int_64(K_sha2[50], 0xdf8eeb99), new Int_64(K_sha2[51], 0xe19b48a8), new Int_64(K_sha2[52], 0xc5c95a63), new Int_64(K_sha2[53], 0xe3418acb), new Int_64(K_sha2[54], 0x7763e373), new Int_64(K_sha2[55], 0xd6b2b8a3), new Int_64(K_sha2[56], 0x5defb2fc), new Int_64(K_sha2[57], 0x43172f60), new Int_64(K_sha2[58], 0xa1f0ab72), new Int_64(K_sha2[59], 0x1a6439ec), new Int_64(K_sha2[60], 0x23631e28), new Int_64(K_sha2[61], 0xde82bde9), new Int_64(K_sha2[62], 0xb2c67915), new Int_64(K_sha2[63], 0xe372532b), new Int_64(0xca273ece, 0xea26619c), new Int_64(0xd186b8c7, 0x21c0c207), new Int_64(0xeada7dd6, 0xcde0eb1e), new Int_64(0xf57d4f7f, 0xee6ed178), new Int_64(0x06f067aa, 0x72176fba), new Int_64(0x0a637dc5, 0xa2c898a6), new Int_64(0x113f9804, 0xbef90dae), new Int_64(0x1b710b35, 0x131c471b), new Int_64(0x28db77f5, 0x23047d84), new Int_64(0x32caab7b, 0x40c72493), new Int_64(0x3c9ebe0a, 0x15c9bebc), new Int_64(0x431d67c4, 0x9c100d4c), new Int_64(0x4cc5d4be, 0xcb3e42b6), new Int_64(0x597f299c, 0xfc657e2a), new Int_64(0x5fcb6fab, 0x3ad6faec), new Int_64(0x6c44198c, 0x4a475817) ]; } } if ((8 & SUPPORTED_ALGS) !== 0) { rc_sha3 = [ new Int_64(0x00000000, 0x00000001), new Int_64(0x00000000, 0x00008082), new Int_64(0x80000000, 0x0000808A), new Int_64(0x80000000, 0x80008000), new Int_64(0x00000000, 0x0000808B), new Int_64(0x00000000, 0x80000001), new Int_64(0x80000000, 0x80008081), new Int_64(0x80000000, 0x00008009), new Int_64(0x00000000, 0x0000008A), new Int_64(0x00000000, 0x00000088), new Int_64(0x00000000, 0x80008009), new Int_64(0x00000000, 0x8000000A), new Int_64(0x00000000, 0x8000808B), new Int_64(0x80000000, 0x0000008B), new Int_64(0x80000000, 0x00008089), new Int_64(0x80000000, 0x00008003), new Int_64(0x80000000, 0x00008002), new Int_64(0x80000000, 0x00000080), new Int_64(0x00000000, 0x0000800A), new Int_64(0x80000000, 0x8000000A), new Int_64(0x80000000, 0x80008081), new Int_64(0x80000000, 0x00008080), new Int_64(0x00000000, 0x80000001), new Int_64(0x80000000, 0x80008008) ]; r_sha3 = [ [ 0, 36, 3, 41, 18], [ 1, 44, 10, 45, 2], [62, 6, 43, 15, 61], [28, 55, 25, 21, 56], [27, 20, 39, 8, 14] ]; } /** * Performs a round of SHA-2 hashing over a block * * @private * @param {Array} block The binary array representation of the * block to hash * @param {Array} H The intermediate H values from a previous * round * @param {string} variant The desired SHA-2 variant * @return {Array} The resulting H values */ function roundSHA2(block, H, variant) { var a, b, c, d, e, f, g, h, T1, T2, numRounds, t, binaryStringMult, safeAdd_2, safeAdd_4, safeAdd_5, gamma0, gamma1, sigma0, sigma1, ch, maj, Int, W = [], int1, int2, offset, K; /* Set up the various function handles and variable for the specific * variant */ if ((variant === "SHA-224" || variant === "SHA-256") && ((2 & SUPPORTED_ALGS) !== 0)) { /* 32-bit variant */ numRounds = 64; binaryStringMult = 1; Int = Number; safeAdd_2 = safeAdd_32_2; safeAdd_4 = safeAdd_32_4; safeAdd_5 = safeAdd_32_5; gamma0 = gamma0_32; gamma1 = gamma1_32; sigma0 = sigma0_32; sigma1 = sigma1_32; maj = maj_32; ch = ch_32; K = K_sha2; } else if ((variant === "SHA-384" || variant === "SHA-512") && ((4 & SUPPORTED_ALGS) !== 0)) { /* 64-bit variant */ numRounds = 80; binaryStringMult = 2; Int = Int_64; safeAdd_2 = safeAdd_64_2; safeAdd_4 = safeAdd_64_4; safeAdd_5 = safeAdd_64_5; gamma0 = gamma0_64; gamma1 = gamma1_64; sigma0 = sigma0_64; sigma1 = sigma1_64; maj = maj_64; ch = ch_64; K = K_sha512; } else { throw new Error("Unexpected error in SHA-2 implementation"); } a = H[0]; b = H[1]; c = H[2]; d = H[3]; e = H[4]; f = H[5]; g = H[6]; h = H[7]; for (t = 0; t < numRounds; t += 1) { if (t < 16) { offset = t * binaryStringMult; int1 = (block.length <= offset) ? 0 : block[offset]; int2 = (block.length <= offset + 1) ? 0 : block[offset + 1]; /* Bit of a hack - for 32-bit, the second term is ignored */ W[t] = new Int(int1, int2); } else { W[t] = safeAdd_4( gamma1(W[t - 2]), W[t - 7], gamma0(W[t - 15]), W[t - 16] ); } T1 = safeAdd_5(h, sigma1(e), ch(e, f, g), K[t], W[t]); T2 = safeAdd_2(sigma0(a), maj(a, b, c)); h = g; g = f; f = e; e = safeAdd_2(d, T1); d = c; c = b; b = a; a = safeAdd_2(T1, T2); } H[0] = safeAdd_2(a, H[0]); H[1] = safeAdd_2(b, H[1]); H[2] = safeAdd_2(c, H[2]); H[3] = safeAdd_2(d, H[3]); H[4] = safeAdd_2(e, H[4]); H[5] = safeAdd_2(f, H[5]); H[6] = safeAdd_2(g, H[6]); H[7] = safeAdd_2(h, H[7]); return H; } /** * Finalizes the SHA-2 hash * * @private * @param {Array} remainder Any leftover unprocessed packed ints * that still need to be processed * @param {number} remainderBinLen The number of bits in remainder * @param {number} processedBinLen The number of bits already * processed * @param {Array} H The intermediate H values from a previous * round * @param {string} variant The desired SHA-2 variant * @param {number} outputLen Unused for this variant * @return {Array} The array of integers representing the SHA-2 * hash of message */ function finalizeSHA2(remainder, remainderBinLen, processedBinLen, H, variant, outputLen) { var i, appendedMessageLength, offset, retVal, binaryStringInc, totalLen; if ((variant === "SHA-224" || variant === "SHA-256") && ((2 & SUPPORTED_ALGS) !== 0)) { /* 32-bit variant */ /* The 65 addition is a hack but it works. The correct number is actually 72 (64 + 8) but the below math fails if remainderBinLen + 72 % 512 = 0. Since remainderBinLen % 8 = 0, "shorting" the addition is OK. */ offset = (((remainderBinLen + 65) >>> 9) << 4) + 15; binaryStringInc = 16; } else if ((variant === "SHA-384" || variant === "SHA-512") && ((4 & SUPPORTED_ALGS) !== 0)) { /* 64-bit variant */ /* The 129 addition is a hack but it works. The correct number is actually 136 (128 + 8) but the below math fails if remainderBinLen + 136 % 1024 = 0. Since remainderBinLen % 8 = 0, "shorting" the addition is OK. */ offset = (((remainderBinLen + 129) >>> 10) << 5) + 31; binaryStringInc = 32; } else { throw new Error("Unexpected error in SHA-2 implementation"); } while (remainder.length <= offset) { remainder.push(0); } /* Append '1' at the end of the binary string */ remainder[remainderBinLen >>> 5] |= 0x80 << (24 - remainderBinLen % 32); /* Append length of binary string in the position such that the new * length is correct. JavaScript numbers are limited to 2^53 so it's * "safe" to treat the totalLen as a 64-bit integer. */ totalLen = remainderBinLen + processedBinLen; remainder[offset] = totalLen & 0xFFFFFFFF; /* Bitwise operators treat the operand as a 32-bit number so need to * use hacky division and round to get access to upper 32-ish bits */ remainder[offset - 1] = (totalLen / TWO_PWR_32) | 0; appendedMessageLength = remainder.length; /* This will always be at least 1 full chunk */ for (i = 0; i < appendedMessageLength; i += binaryStringInc) { H = roundSHA2(remainder.slice(i, i + binaryStringInc), H, variant); } if (("SHA-224" === variant) && ((2 & SUPPORTED_ALGS) !== 0)) { retVal = [ H[0], H[1], H[2], H[3], H[4], H[5], H[6] ]; } else if (("SHA-256" === variant) && ((2 & SUPPORTED_ALGS) !== 0)) { retVal = H; } else if (("SHA-384" === variant) && ((4 & SUPPORTED_ALGS) !== 0)) { retVal = [ H[0].highOrder, H[0].lowOrder, H[1].highOrder, H[1].lowOrder, H[2].highOrder, H[2].lowOrder, H[3].highOrder, H[3].lowOrder, H[4].highOrder, H[4].lowOrder, H[5].highOrder, H[5].lowOrder ]; } else if (("SHA-512" === variant) && ((4 & SUPPORTED_ALGS) !== 0)) { retVal = [ H[0].highOrder, H[0].lowOrder, H[1].highOrder, H[1].lowOrder, H[2].highOrder, H[2].lowOrder, H[3].highOrder, H[3].lowOrder, H[4].highOrder, H[4].lowOrder, H[5].highOrder, H[5].lowOrder, H[6].highOrder, H[6].lowOrder, H[7].highOrder, H[7].lowOrder ]; } else /* This should never be reached */ { throw new Error("Unexpected error in SHA-2 implementation"); } return retVal; } /** * Performs a round of SHA-3 hashing over a block * * @private * @param {Array|null} block The binary array representation of the * block to hash * @param {Array>} state The binary array representation of the * block to hash * @return {Array>} The resulting state value */ function roundSHA3(block, state) { var round, x, y, B, C = [], D = []; if (null !== block) { for (x = 0; x < block.length; x+=2) { state[(x >>> 1) % 5][((x >>> 1) / 5) | 0] = xor_64_2( state[(x >>> 1) % 5][((x >>> 1) / 5) | 0], new Int_64(block[x + 1], block[x]) ); } } for (round = 0; round < 24; round += 1) { /* getNewState doesn't care about variant beyond SHA3 so feed it a value that triggers the getNewState "if" statement */ B = getNewState("SHA3-"); /* Perform theta step */ for (x = 0; x < 5; x += 1) { C[x] = xor_64_5(state[x][0], state[x][1], state[x][2], state[x][3], state[x][4]); } for (x = 0; x < 5; x += 1) { D[x] = xor_64_2(C[(x + 4) % 5], rotl_64(C[(x + 1) % 5], 1)); } for (x = 0; x < 5; x += 1) { for (y = 0; y < 5; y += 1) { state[x][y] = xor_64_2(state[x][y], D[x]); } } /* Perform combined ro and pi steps */ for (x = 0; x < 5; x += 1) { for (y = 0; y < 5; y += 1) { B[y][(2 * x + 3 * y) % 5] = rotl_64( state[x][y], r_sha3[x][y] ); } } /* Perform chi step */ for (x = 0; x < 5; x += 1) { for (y = 0; y < 5; y += 1) { state[x][y] = xor_64_2( B[x][y], new Int_64( ~(B[(x + 1) % 5][y].highOrder) & B[(x + 2) % 5][y].highOrder, ~(B[(x + 1) % 5][y].lowOrder) & B[(x + 2) % 5][y].lowOrder ) ); } } /* Perform iota step */ state[0][0] = xor_64_2(state[0][0], rc_sha3[round]); } return state; } /** * Finalizes the SHA-3 hash * * @private * @param {Array} remainder Any leftover unprocessed packed ints * that still need to be processed * @param {number} remainderBinLen The number of bits in remainder * @param {number} processedBinLen The number of bits already * processed * @param {Array>} state The state from a previous round * @param {number} blockSize The block size/rate of the variant in bits * @param {number} delimiter The delimiter value for the variant * @param {number} outputLen The output length for the variant in bits * @return {Array} The array of integers representing the SHA-3 * hash of message */ function finalizeSHA3(remainder, remainderBinLen, processedBinLen, state, blockSize, delimiter, outputLen) { var i, retVal = [], binaryStringInc = blockSize >>> 5, state_offset = 0, remainderIntLen = remainderBinLen >>> 5, temp; /* Process as many blocks as possible, some may be here for multiple rounds with SHAKE */ for (i = 0; i < remainderIntLen && remainderBinLen >= blockSize; i += binaryStringInc) { state = roundSHA3(remainder.slice(i, i + binaryStringInc), state); remainderBinLen -= blockSize; } remainder = remainder.slice(i); remainderBinLen = remainderBinLen % blockSize; /* Pad out the remainder to a full block */ while (remainder.length < binaryStringInc) { remainder.push(0); } /* Find the next "empty" byte for the 0x80 and append it via an xor */ i = remainderBinLen >>> 3; remainder[i >> 2] ^= delimiter << (8 * (i % 4)); remainder[binaryStringInc - 1] ^= 0x80000000; state = roundSHA3(remainder, state); while (retVal.length * 32 < outputLen) { temp = state[state_offset % 5][(state_offset / 5) | 0]; retVal.push(temp.lowOrder); if (retVal.length * 32 >= outputLen) { break; } retVal.push(temp.highOrder); state_offset += 1; if (0 === ((state_offset * 64) % blockSize)) { roundSHA3(null, state); } } return retVal; } /** * jsSHA is the workhorse of the library. Instantiate it with the string to * be hashed as the parameter * * @constructor * @this {jsSHA} * @param {string} variant The desired SHA variant (SHA-1, SHA-224, SHA-256, * SHA-384, SHA-512, SHA3-224, SHA3-256, SHA3-384, or SHA3-512) * @param {string} inputFormat The format of srcString: HEX, TEXT, B64, * BYTES, or ARRAYBUFFER * @param {{encoding: (string|undefined), numRounds: (number|undefined)}=} * options Optional values */ var jsSHA = function(variant, inputFormat, options) { var processedLen = 0, remainder = [], remainderLen = 0, utfType, intermediateState, converterFunc, shaVariant = variant, outputBinLen, variantBlockSize, roundFunc, finalizeFunc, stateCloneFunc, hmacKeySet = false, keyWithIPad = [], keyWithOPad = [], numRounds, updatedCalled = false, inputOptions, isSHAKE = false, bigEndianMod = -1; inputOptions = options || {}; utfType = inputOptions["encoding"] || "UTF8"; numRounds = inputOptions["numRounds"] || 1; if ((numRounds !== parseInt(numRounds, 10)) || (1 > numRounds)) { throw new Error("numRounds must a integer >= 1"); } if (("SHA-1" === shaVariant) && ((1 & SUPPORTED_ALGS) !== 0)) { variantBlockSize = 512; roundFunc = roundSHA1; finalizeFunc = finalizeSHA1; outputBinLen = 160; stateCloneFunc = function(state) { return state.slice();}; } else if ((shaVariant.lastIndexOf("SHA-", 0) === 0) && ((6 & SUPPORTED_ALGS) !== 0)) { roundFunc = function (block, H) { return roundSHA2(block, H, shaVariant); }; finalizeFunc = function (remainder, remainderBinLen, processedBinLen, H, outputLen) { return finalizeSHA2(remainder, remainderBinLen, processedBinLen, H, shaVariant, outputLen); }; stateCloneFunc = function(state) { return state.slice(); }; if (("SHA-224" === shaVariant) && ((2 & SUPPORTED_ALGS) !== 0)) { variantBlockSize = 512; outputBinLen = 224; } else if (("SHA-256" === shaVariant) && ((2 & SUPPORTED_ALGS) !== 0)) { variantBlockSize = 512; outputBinLen = 256; } else if (("SHA-384" === shaVariant) && ((4 & SUPPORTED_ALGS) !== 0)) { variantBlockSize = 1024; outputBinLen = 384; } else if (("SHA-512" === shaVariant) && ((4 & SUPPORTED_ALGS) !== 0)) { variantBlockSize = 1024; outputBinLen = 512; } else { throw new Error("Chosen SHA variant is not supported"); } } else if (((shaVariant.lastIndexOf("SHA3-", 0) === 0) || (shaVariant.lastIndexOf("SHAKE", 0) === 0)) && ((8 & SUPPORTED_ALGS) !== 0)) { var delimiter = 0x06; roundFunc = roundSHA3; stateCloneFunc = function(state) { return cloneSHA3State(state);}; bigEndianMod = 1; if ("SHA3-224" === shaVariant) { variantBlockSize = 1152; outputBinLen = 224; } else if ("SHA3-256" === shaVariant) { variantBlockSize = 1088; outputBinLen = 256; } else if ("SHA3-384" === shaVariant) { variantBlockSize = 832; outputBinLen = 384; } else if ("SHA3-512" === shaVariant) { variantBlockSize = 576; outputBinLen = 512; } else if ("SHAKE128" === shaVariant) { variantBlockSize = 1344; outputBinLen = -1; delimiter = 0x1F; isSHAKE = true; } else if ("SHAKE256" === shaVariant) { variantBlockSize = 1088; outputBinLen = -1; delimiter = 0x1F; isSHAKE = true; } else { throw new Error("Chosen SHA variant is not supported"); } finalizeFunc = function (remainder, remainderBinLen, processedBinLen, state, outputLen) { return finalizeSHA3(remainder, remainderBinLen, processedBinLen, state, variantBlockSize, delimiter, outputLen); }; } else { throw new Error("Chosen SHA variant is not supported"); } converterFunc = getStrConverter(inputFormat, utfType, bigEndianMod); intermediateState = getNewState(shaVariant); /** * Sets the HMAC key for an eventual getHMAC call. Must be called * immediately after jsSHA object instantiation * * @expose * @param {string|ArrayBuffer} key The key used to calculate the HMAC * @param {string} inputFormat The format of key, HEX, TEXT, B64, BYTES, * or ARRAYBUFFER * @param {{encoding : (string|undefined)}=} options Associative array * of input format options */ this.setHMACKey = function(key, inputFormat, options) { var keyConverterFunc, convertRet, keyBinLen, keyToUse, blockByteSize, i, lastArrayIndex, keyOptions; if (true === hmacKeySet) { throw new Error("HMAC key already set"); } if (true === updatedCalled) { throw new Error("Cannot set HMAC key after calling update"); } if ((isSHAKE === true) && ((8 & SUPPORTED_ALGS) !== 0)) { throw new Error("SHAKE is not supported for HMAC"); } keyOptions = options || {}; utfType = keyOptions["encoding"] || "UTF8"; keyConverterFunc = getStrConverter(inputFormat, utfType, bigEndianMod); convertRet = keyConverterFunc(key); keyBinLen = convertRet["binLen"]; keyToUse = convertRet["value"]; blockByteSize = variantBlockSize >>> 3; /* These are used multiple times, calculate and store them */ lastArrayIndex = (blockByteSize / 4) - 1; /* Figure out what to do with the key based on its size relative to * the hash's block size */ if (blockByteSize < (keyBinLen / 8)) { keyToUse = finalizeFunc(keyToUse, keyBinLen, 0,getNewState(shaVariant), outputBinLen); /* For all variants, the block size is bigger than the output * size so there will never be a useful byte at the end of the * string */ while (keyToUse.length <= lastArrayIndex) { keyToUse.push(0); } keyToUse[lastArrayIndex] &= 0xFFFFFF00; } else if (blockByteSize > (keyBinLen / 8)) { /* If the blockByteSize is greater than the key length, there * will always be at LEAST one "useless" byte at the end of the * string */ while (keyToUse.length <= lastArrayIndex) { keyToUse.push(0); } keyToUse[lastArrayIndex] &= 0xFFFFFF00; } /* Create ipad and opad */ for (i = 0; i <= lastArrayIndex; i += 1) { keyWithIPad[i] = keyToUse[i] ^ 0x36363636; keyWithOPad[i] = keyToUse[i] ^ 0x5C5C5C5C; } intermediateState = roundFunc(keyWithIPad, intermediateState); processedLen = variantBlockSize; hmacKeySet = true; }; /** * Takes strString and hashes as many blocks as possible. Stores the * rest for either a future update or getHash call. * * @expose * @param {string|ArrayBuffer} srcString The string to be hashed */ this.update = function(srcString) { var convertRet, chunkBinLen, chunkIntLen, chunk, i, updateProcessedLen = 0, variantBlockIntInc = variantBlockSize >>> 5; convertRet = converterFunc(srcString, remainder, remainderLen); chunkBinLen = convertRet["binLen"]; chunk = convertRet["value"]; chunkIntLen = chunkBinLen >>> 5; for (i = 0; i < chunkIntLen; i += variantBlockIntInc) { if (updateProcessedLen + variantBlockSize <= chunkBinLen) { intermediateState = roundFunc( chunk.slice(i, i + variantBlockIntInc), intermediateState ); updateProcessedLen += variantBlockSize; } } processedLen += updateProcessedLen; remainder = chunk.slice(updateProcessedLen >>> 5); remainderLen = chunkBinLen % variantBlockSize; updatedCalled = true; }; /** * Returns the desired SHA hash of the string specified at instantiation * using the specified parameters * * @expose * @param {string} format The desired output formatting (B64, HEX, * BYTES, or ARRAYBUFFER) * @param {{outputUpper : (boolean|undefined), b64Pad : (string|undefined), * shakeLen : (number|undefined)}=} options Hash list of output formatting options * @return {string|ArrayBuffer} The string representation of the hash * in the format specified. */ this.getHash = function(format, options) { var formatFunc, i, outputOptions, finalizedState; if (true === hmacKeySet) { throw new Error("Cannot call getHash after setting HMAC key"); } outputOptions = getOutputOpts(options); if ((isSHAKE === true) && ((8 & SUPPORTED_ALGS) !== 0)) { if (outputOptions["shakeLen"] === -1) { throw new Error("shakeLen must be specified in options"); } outputBinLen = outputOptions["shakeLen"]; } /* Validate the output format selection */ switch (format) { case "HEX": formatFunc = function(binarray) {return packed2hex(binarray, outputBinLen, bigEndianMod, outputOptions);}; break; case "B64": formatFunc = function(binarray) {return packed2b64(binarray, outputBinLen, bigEndianMod, outputOptions);}; break; case "BYTES": formatFunc = function(binarray) {return packed2bytes(binarray, outputBinLen, bigEndianMod);}; break; case "ARRAYBUFFER": try { i = new ArrayBuffer(0); } catch (ignore) { throw new Error("ARRAYBUFFER not supported by this environment"); } formatFunc = function(binarray) {return packed2arraybuffer(binarray, outputBinLen, bigEndianMod);}; break; default: throw new Error("format must be HEX, B64, BYTES, or ARRAYBUFFER"); } finalizedState = finalizeFunc(remainder.slice(), remainderLen, processedLen, stateCloneFunc(intermediateState), outputBinLen); for (i = 1; i < numRounds; i += 1) { /* This weird fix-up is only for the case of SHAKE algorithms * and outputBinLen is not a multiple of 32. In this case, the * very last block of finalizedState has data that needs to be * ignored because all the finalizeFunc calls need to have * unneeded bits set to 0. */ if (((8 & SUPPORTED_ALGS) !== 0) && (isSHAKE === true) && (outputBinLen % 32 !== 0)) { finalizedState[finalizedState.length - 1] &= 0x00FFFFFF >>> 24 - (outputBinLen % 32); } finalizedState = finalizeFunc(finalizedState, outputBinLen, 0, getNewState(shaVariant), outputBinLen); } return formatFunc(finalizedState); }; /** * Returns the the HMAC in the specified format using the key given by * a previous setHMACKey call. * * @expose * @param {string} format The desired output formatting * (B64, HEX, BYTES, or ARRAYBUFFER) * @param {{outputUpper : (boolean|undefined), b64Pad : (string|undefined), * shakeLen : (number|undefined)}=} options associative array of output * formatting options * @return {string|ArrayBuffer} The string representation of the hash in the * format specified. */ this.getHMAC = function(format, options) { var formatFunc, firstHash, outputOptions, finalizedState; if (false === hmacKeySet) { throw new Error("Cannot call getHMAC without first setting HMAC key"); } outputOptions = getOutputOpts(options); /* Validate the output format selection */ switch (format) { case "HEX": formatFunc = function(binarray) {return packed2hex(binarray, outputBinLen, bigEndianMod, outputOptions);}; break; case "B64": formatFunc = function(binarray) {return packed2b64(binarray, outputBinLen, bigEndianMod, outputOptions);}; break; case "BYTES": formatFunc = function(binarray) {return packed2bytes(binarray, outputBinLen, bigEndianMod);}; break; case "ARRAYBUFFER": try { formatFunc = new ArrayBuffer(0); } catch(ignore) { throw new Error("ARRAYBUFFER not supported by this environment"); } formatFunc = function(binarray) {return packed2arraybuffer(binarray, outputBinLen, bigEndianMod);}; break; default: throw new Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER"); } firstHash = finalizeFunc(remainder.slice(), remainderLen, processedLen, stateCloneFunc(intermediateState), outputBinLen); finalizedState = roundFunc(keyWithOPad, getNewState(shaVariant)); finalizedState = finalizeFunc(firstHash, outputBinLen, variantBlockSize, finalizedState, outputBinLen); return formatFunc(finalizedState); }; }; if (("function" === typeof define) && (define["amd"])) /* AMD Support */ { define(function() { return jsSHA; }); } else if ("undefined" !== typeof exports) /* Node Support */ { if (("undefined" !== typeof module) && module["exports"]) { module["exports"] = jsSHA; exports = jsSHA; } else { exports = jsSHA; } } else { /* Browsers and Web Workers*/ global["jsSHA"] = jsSHA; } }(this));