summaryrefslogtreecommitdiffstats
path: root/src/sha_dev.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/sha_dev.js')
-rw-r--r--src/sha_dev.js1211
1 files changed, 683 insertions, 528 deletions
diff --git a/src/sha_dev.js b/src/sha_dev.js
index 34ab870..58e4fd8 100644
--- a/src/sha_dev.js
+++ b/src/sha_dev.js
@@ -41,17 +41,31 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
/**
* 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 str2binb 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.<number>} 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.<number>, binLen : number}} Hash list where
* "value" contains the output number array and "binLen" is the binary
* length of "value"
*/
- function str2binb(str, utfType)
+ function str2binb(str, utfType, existingBin, existingBinLen)
{
- var bin = [], codePnt, binArr = [], byteCnt = 0, i, j, offset;
+ var bin = [], codePnt, binArr = [], byteCnt = 0, i, j, existingByteLen,
+ intOffset, byteOffset;
+
+ bin = existingBin || [0];
+ existingBinLen = existingBinLen || 0;
+ existingByteLen = existingBinLen >>> 3;
if ("UTF8" === utfType)
{
@@ -90,12 +104,14 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
for (j = 0; j < binArr.length; j += 1)
{
- offset = byteCnt >>> 2;
- while (bin.length <= offset)
+ byteOffset = byteCnt + existingByteLen;
+ intOffset = byteOffset >>> 2;
+ while (bin.length <= intOffset)
{
bin.push(0);
}
- bin[offset] |= binArr[j] << (24 - (8 * (byteCnt % 4)));
+ /* Known bug kicks in here */
+ bin[intOffset] |= binArr[j] << (8 * (3 - (byteOffset % 4)));
byteCnt += 1;
}
}
@@ -109,19 +125,20 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
if ("UTF16LE" === utfType)
{
j = codePnt & 0xFF;
- codePnt = (j << 8) | (codePnt >> 8);
+ codePnt = (j << 8) | (codePnt >>> 8);
}
- offset = byteCnt >>> 2;
- while (bin.length <= offset)
+ byteOffset = byteCnt + existingByteLen;
+ intOffset = byteOffset >>> 2;
+ while (bin.length <= intOffset)
{
bin.push(0);
}
- bin[offset] |= codePnt << (16 - (8 * (byteCnt % 4)));
+ bin[intOffset] |= codePnt << (8 * (2 - (byteOffset % 4)));
byteCnt += 2;
}
}
- return {"value" : bin, "binLen" : byteCnt * 8};
+ return {"value" : bin, "binLen" : byteCnt * 8 + existingBinLen};
}
/**
@@ -129,17 +146,26 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
*
* @private
* @param {string} str String to be converted to binary representation
+ * @param {Array.<number>} 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.<number>, binLen : number}} Hash list where
* "value" contains the output number array and "binLen" is the binary
* length of "value"
*/
- function hex2binb(str)
+ function hex2binb(str, existingBin, existingBinLen)
{
- var bin = [], length = str.length, i, num, offset;
+ var bin, length = str.length, i, num, intOffset, byteOffset,
+ existingByteLen;
+
+ bin = existingBin || [0];
+ existingBinLen = existingBinLen || 0;
+ existingByteLen = existingBinLen >>> 3;
if (0 !== (length % 2))
{
- throw "String of HEX type must be in byte increments";
+ throw new Error("String of HEX type must be in byte increments");
}
for (i = 0; i < length; i += 2)
@@ -147,48 +173,59 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
num = parseInt(str.substr(i, 2), 16);
if (!isNaN(num))
{
- offset = i >>> 3;
- while (bin.length <= offset)
+ byteOffset = (i >>> 1) + existingByteLen;
+ intOffset = byteOffset >>> 2;
+ while (bin.length <= intOffset)
{
bin.push(0);
}
- bin[i >>> 3] |= num << (24 - (4 * (i % 8)));
+ bin[intOffset] |= num << 8 * (3 - (byteOffset % 4));
}
else
{
- throw "String of HEX type contains invalid characters";
+ throw new Error("String of HEX type contains invalid characters");
}
}
- return {"value" : bin, "binLen" : length * 4};
+ return {"value" : bin, "binLen" : length * 4 + existingBinLen};
}
- /**
+ /**
* 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.<number>} 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.<number>, binLen : number}} Hash list where
* "value" contains the output number array and "binLen" is the binary
* length of "value"
*/
- function bytes2binb(str)
+ function bytes2binb(str, existingBin, existingBinLen)
{
- var bin = [], codePnt, i, offset;
+ var bin = [], codePnt, i, existingByteLen, intOffset,
+ byteOffset;
+
+ bin = existingBin || [0];
+ existingBinLen = existingBinLen || 0;
+ existingByteLen = existingBinLen >>> 3;
for (i = 0; i < str.length; i += 1)
{
codePnt = str.charCodeAt(i);
- offset = i >>> 2;
- if (bin.length <= offset)
+ byteOffset = i + existingByteLen;
+ intOffset = byteOffset >>> 2;
+ if (bin.length <= intOffset)
{
bin.push(0);
}
- bin[offset] |= codePnt << (24 - (8 * (i % 4)));
+ bin[intOffset] |= codePnt << 8 * (3 - (byteOffset % 4));
}
- return {"value" : bin, "binLen" : str.length * 8};
+ return {"value" : bin, "binLen" : str.length * 8 + existingBinLen};
}
/**
@@ -196,24 +233,33 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
*
* @private
* @param {string} str String to be converted to binary representation
+ * @param {Array.<number>} 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.<number>, binLen : number}} Hash list where
* "value" contains the output number array and "binLen" is the binary
* length of "value"
*/
- function b642binb(str)
+ function b642binb(str, existingBin, existingBinLen)
{
- var retVal = [], byteCnt = 0, index, i, j, tmpInt, strPart, firstEqual, offset,
- b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var bin = [], byteCnt = 0, index, i, j, tmpInt, strPart, firstEqual,
+ b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
+ existingByteLen, intOffset, byteOffset;
+
+ bin = existingBin || [0];
+ existingBinLen = existingBinLen || 0;
+ existingByteLen = existingBinLen >>> 3;
if (-1 === str.search(/^[a-zA-Z0-9=+\/]+$/))
{
- throw "Invalid character in base-64 string";
+ throw new Error("Invalid character in base-64 string");
}
firstEqual = str.indexOf('=');
str = str.replace(/\=/g, '');
if ((-1 !== firstEqual) && (firstEqual < str.length))
{
- throw "Invalid '=' found in base-64 string";
+ throw new Error("Invalid '=' found in base-64 string");
}
for (i = 0; i < str.length; i += 4)
@@ -229,18 +275,19 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
for (j = 0; j < strPart.length - 1; j += 1)
{
- offset = byteCnt >>> 2;
- while (retVal.length <= offset)
+ byteOffset = byteCnt + existingByteLen;
+ intOffset = byteOffset >>> 2;
+ while (bin.length <= intOffset)
{
- retVal.push(0);
+ bin.push(0);
}
- retVal[offset] |= ((tmpInt >>> (16 - (j * 8))) & 0xFF) <<
- (24 - (8 * (byteCnt % 4)));
+ bin[intOffset] |= ((tmpInt >>> (16 - (j * 8))) & 0xFF) <<
+ 8 * (3 - (byteOffset % 4));
byteCnt += 1;
}
}
- return {"value" : retVal, "binLen" : byteCnt * 8};
+ return {"value" : bin, "binLen" : byteCnt * 8 + existingBinLen};
}
/**
@@ -316,11 +363,10 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
* @private
* @param {Array.<number>} binarray Array of integers to be converted to
* a raw bytes string representation
- * @param {!Object} formatOpts Unused Hash list
* @return {string} Raw bytes representation of the parameter in string
* form
*/
- function binb2bytes(binarray, formatOpts)
+ function binb2bytes(binarray)
{
var str = "", length = binarray.length * 4, i, srcByte;
@@ -338,38 +384,82 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
* presence of every option or adding the default value
*
* @private
- * @param {{outputUpper : boolean, b64Pad : string}|undefined} outputOpts
- * Hash list of output formatting options
+ * @param {{outputUpper : (boolean|undefined), b64Pad : (string|undefined)}=}
+ * options Hash list of output formatting options
* @return {{outputUpper : boolean, b64Pad : string}} Validated hash list
* containing output formatting options
*/
- function getOutputOpts(outputOpts)
+ function getOutputOpts(options)
{
- var retVal = {"outputUpper" : false, "b64Pad" : "="};
+ var retVal = {"outputUpper" : false, "b64Pad" : "="}, outputOptions;
+ outputOptions = options || {};
- try
+ retVal["outputUpper"] = outputOptions["outputUpper"] || false;
+ retVal["b64Pad"] = outputOptions["b64Pad"] || "=";
+
+ if ("boolean" !== typeof(retVal["outputUpper"]))
{
- if (outputOpts.hasOwnProperty("outputUpper"))
- {
- retVal["outputUpper"] = outputOpts["outputUpper"];
- }
+ throw new Error("Invalid outputUpper formatting option");
+ }
- if (outputOpts.hasOwnProperty("b64Pad"))
- {
- retVal["b64Pad"] = outputOpts["b64Pad"];
- }
+ if ("string" !== typeof(retVal["b64Pad"]))
+ {
+ throw new Error("Invalid b64Pad formatting option");
}
- catch(ignore)
- {}
- if ("boolean" !== typeof(retVal["outputUpper"]))
+ 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)
+ * @return {function(string, Array.<number>=, number=): {value :
+ * Array.<number>, binLen : number}} Function that will convert an input
+ * string to a packed int array
+ */
+ function getStrConverter(format, utfType)
+ {
+ var retVal;
+
+ /* Validate encoding */
+ switch (utfType)
{
- throw "Invalid outputUpper formatting option";
+ case "UTF8":
+ /* Fallthrough */
+ case "UTF16BE":
+ /* Fallthrough */
+ case "UTF16LE":
+ /* Fallthrough */
+ break;
+ default:
+ throw new Error("encoding must be UTF8, UTF16BE, or UTF16LE");
}
- if ("string" !== typeof(retVal["b64Pad"]))
+ /* Map inputFormat to the appropriate converter */
+ switch (format)
{
- throw "Invalid b64Pad formatting option";
+ case "HEX":
+ retVal = hex2binb;
+ break;
+ case "TEXT":
+ retVal = function(str, existingBin, existingBinLen)
+ {
+ return str2binb(str, utfType, existingBin, existingBinLen);
+ };
+ break;
+ case "B64":
+ retVal = b642binb;
+ break;
+ case "BYTES":
+ retVal = bytes2binb;
+ break;
+ default:
+ throw new Error("format must be HEX, TEXT, B64, or BYTES");
}
return retVal;
@@ -820,132 +910,269 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
}
/**
- * Calculates the SHA-1 hash of the string set at instantiation
+ * Gets the H values for the specified SHA variant
+ *
+ * @param {string} variant The SHA variant
+ * @return {Array.<number|Int_64>} The initial H values
+ */
+ function getH(variant)
+ {
+ var retVal, H_trunc, H_full;
+
+ if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS))
+ {
+ retVal = [
+ 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
+ ];
+ }
+ else if (6 & SUPPORTED_ALGS)
+ {
+ 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
+ {
+ throw new Error("No SHA variants supported");
+ }
+
+ return retVal;
+ }
+
+ /**
+ * Performs a round of SHA-1 hashing over a 512-byte block
*
* @private
- * @param {Array.<number>} message The binary array representation of the
- * string to hash
- * @param {number} messageLen The number of bits in the message
+ * @param {Array.<number>} block The binary array representation of the
+ * block to hash
+ * @param {Array.<number>} H The intermediate H values from a previous
+ * round
+ * @return {Array.<number>} 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.<number>} 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.<number>} H The intermediate H values from a previous
+ * round
* @return {Array.<number>} The array of integers representing the SHA-1
* hash of message
*/
- function coreSHA1(message, messageLen)
+ function finalizeSHA1(remainder, remainderBinLen, processedBinLen, 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, i, t,
- safeAdd_5 = safeAdd_32_5, appendedMessageLength, offset,
- H = [
- 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
- ];
+ var i, appendedMessageLength, offset;
- offset = (((messageLen + 65) >>> 9) << 4) + 15;
- while (message.length <= offset)
+ /* Add 72 because of the 64-bit length and the extra byte because
+ of the bit (actually a byte) append */
+ offset = (((remainderBinLen + 72) >>> 9) << 4) + 15;
+ while (remainder.length <= offset)
{
- message.push(0);
+ remainder.push(0);
}
/* Append '1' at the end of the binary string */
- message[messageLen >>> 5] |= 0x80 << (24 - (messageLen % 32));
+ 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 */
- message[offset] = messageLen;
+ remainder[offset] = remainderBinLen + processedBinLen;
- appendedMessageLength = message.length;
+ appendedMessageLength = remainder.length;
+ /* This will always be at least 1 full chunk */
for (i = 0; i < appendedMessageLength; i += 16)
{
- 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] = message[t + i];
- }
- 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]);
+ 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;
+ if (6 & SUPPORTED_ALGS)
+ {
+ 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)
+ {
+ 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)
+ ];
+ }
+ }
+
/**
- * Calculates the desired SHA-2 hash of the string set at instantiation
+ * Performs a round of SHA-2 hashing over a block
*
* @private
- * @param {Array.<number>} message The binary array representation of the
- * string to hash
- * @param {number} messageLen The number of bits in message
+ * @param {Array.<number>} block The binary array representation of the
+ * block to hash
+ * @param {Array.<number|Int_64>} H The intermediate H values from a previous
+ * round
* @param {string} variant The desired SHA-2 variant
- * @return {Array.<number>} The array of integers representing the SHA-2
- * hash of message
+ * @return {Array.<number|Int_64>} The resulting H values
*/
- function coreSHA2(message, messageLen, variant)
+ function roundSHA2(block, H, variant)
{
- var a, b, c, d, e, f, g, h, T1, T2, H, numRounds, lengthPosition, i, t,
- binaryStringInc, binaryStringMult, safeAdd_2, safeAdd_4, safeAdd_5,
- gamma0, gamma1, sigma0, sigma1, ch, maj, Int, W = [], int1, int2, offset,
- appendedMessageLength, retVal,
- K = [
- 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
- ],
- H_trunc = [
- 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939,
- 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4
- ],
- H_full = [
- 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
- 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
- ];
+ 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 */
@@ -954,8 +1181,6 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
{
/* 32-bit variant */
numRounds = 64;
- lengthPosition = (((messageLen + 65) >>> 9) << 4) + 15;
- binaryStringInc = 16;
binaryStringMult = 1;
Int = Number;
safeAdd_2 = safeAdd_32_2;
@@ -967,23 +1192,13 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
sigma1 = sigma1_32;
maj = maj_32;
ch = ch_32;
-
- if ("SHA-224" === variant)
- {
- H = H_trunc;
- }
- else /* "SHA-256" === variant */
- {
- H = H_full;
- }
+ K = K_sha2;
}
else if ((variant === "SHA-384" || variant === "SHA-512") &&
(4 & SUPPORTED_ALGS))
{
/* 64-bit variant */
numRounds = 80;
- lengthPosition = (((messageLen + 128) >>> 10) << 5) + 31;
- binaryStringInc = 32;
binaryStringMult = 2;
Int = Int_64;
safeAdd_2 = safeAdd_64_2;
@@ -995,136 +1210,122 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
sigma1 = sigma1_64;
maj = maj_64;
ch = ch_64;
+ K = K_sha512;
+ }
+ else
+ {
+ throw new Error("Unexpected error in SHA-2 implementation");
+ }
- K = [
- new Int(K[ 0], 0xd728ae22), new Int(K[ 1], 0x23ef65cd),
- new Int(K[ 2], 0xec4d3b2f), new Int(K[ 3], 0x8189dbbc),
- new Int(K[ 4], 0xf348b538), new Int(K[ 5], 0xb605d019),
- new Int(K[ 6], 0xaf194f9b), new Int(K[ 7], 0xda6d8118),
- new Int(K[ 8], 0xa3030242), new Int(K[ 9], 0x45706fbe),
- new Int(K[10], 0x4ee4b28c), new Int(K[11], 0xd5ffb4e2),
- new Int(K[12], 0xf27b896f), new Int(K[13], 0x3b1696b1),
- new Int(K[14], 0x25c71235), new Int(K[15], 0xcf692694),
- new Int(K[16], 0x9ef14ad2), new Int(K[17], 0x384f25e3),
- new Int(K[18], 0x8b8cd5b5), new Int(K[19], 0x77ac9c65),
- new Int(K[20], 0x592b0275), new Int(K[21], 0x6ea6e483),
- new Int(K[22], 0xbd41fbd4), new Int(K[23], 0x831153b5),
- new Int(K[24], 0xee66dfab), new Int(K[25], 0x2db43210),
- new Int(K[26], 0x98fb213f), new Int(K[27], 0xbeef0ee4),
- new Int(K[28], 0x3da88fc2), new Int(K[29], 0x930aa725),
- new Int(K[30], 0xe003826f), new Int(K[31], 0x0a0e6e70),
- new Int(K[32], 0x46d22ffc), new Int(K[33], 0x5c26c926),
- new Int(K[34], 0x5ac42aed), new Int(K[35], 0x9d95b3df),
- new Int(K[36], 0x8baf63de), new Int(K[37], 0x3c77b2a8),
- new Int(K[38], 0x47edaee6), new Int(K[39], 0x1482353b),
- new Int(K[40], 0x4cf10364), new Int(K[41], 0xbc423001),
- new Int(K[42], 0xd0f89791), new Int(K[43], 0x0654be30),
- new Int(K[44], 0xd6ef5218), new Int(K[45], 0x5565a910),
- new Int(K[46], 0x5771202a), new Int(K[47], 0x32bbd1b8),
- new Int(K[48], 0xb8d2d0c8), new Int(K[49], 0x5141ab53),
- new Int(K[50], 0xdf8eeb99), new Int(K[51], 0xe19b48a8),
- new Int(K[52], 0xc5c95a63), new Int(K[53], 0xe3418acb),
- new Int(K[54], 0x7763e373), new Int(K[55], 0xd6b2b8a3),
- new Int(K[56], 0x5defb2fc), new Int(K[57], 0x43172f60),
- new Int(K[58], 0xa1f0ab72), new Int(K[59], 0x1a6439ec),
- new Int(K[60], 0x23631e28), new Int(K[61], 0xde82bde9),
- new Int(K[62], 0xb2c67915), new Int(K[63], 0xe372532b),
- new Int(0xca273ece, 0xea26619c), new Int(0xd186b8c7, 0x21c0c207),
- new Int(0xeada7dd6, 0xcde0eb1e), new Int(0xf57d4f7f, 0xee6ed178),
- new Int(0x06f067aa, 0x72176fba), new Int(0x0a637dc5, 0xa2c898a6),
- new Int(0x113f9804, 0xbef90dae), new Int(0x1b710b35, 0x131c471b),
- new Int(0x28db77f5, 0x23047d84), new Int(0x32caab7b, 0x40c72493),
- new Int(0x3c9ebe0a, 0x15c9bebc), new Int(0x431d67c4, 0x9c100d4c),
- new Int(0x4cc5d4be, 0xcb3e42b6), new Int(0x597f299c, 0xfc657e2a),
- new Int(0x5fcb6fab, 0x3ad6faec), new Int(0x6c44198c, 0x4a475817)
- ];
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
- if ("SHA-384" === variant)
+ for (t = 0; t < numRounds; t += 1)
+ {
+ if (t < 16)
{
- H = [
- new Int(0xcbbb9d5d, H_trunc[0]), new Int(0x0629a292a, H_trunc[1]),
- new Int(0x9159015a, H_trunc[2]), new Int(0x0152fecd8, H_trunc[3]),
- new Int(0x67332667, H_trunc[4]), new Int(0x98eb44a87, H_trunc[5]),
- new Int(0xdb0c2e0d, H_trunc[6]), new Int(0x047b5481d, H_trunc[7])
- ];
+ 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 /* "SHA-512" === variant */
+ else
{
- H = [
- new Int(H_full[0], 0xf3bcc908), new Int(H_full[1], 0x84caa73b),
- new Int(H_full[2], 0xfe94f82b), new Int(H_full[3], 0x5f1d36f1),
- new Int(H_full[4], 0xade682d1), new Int(H_full[5], 0x2b3e6c1f),
- new Int(H_full[6], 0xfb41bd6b), new Int(H_full[7], 0x137e2179)
- ];
+ 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.<number>} 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.<number|Int_64>} H The intermediate H values from a previous
+ * round
+ * @param {string} variant The desired SHA-2 variant
+ * @return {Array.<number>} The array of integers representing the SHA-2
+ * hash of message
+ */
+ function finalizeSHA2(remainder, remainderBinLen, processedBinLen, H, variant)
+ {
+ var i, appendedMessageLength, offset, retVal, binaryStringInc;
+
+ if ((variant === "SHA-224" || variant === "SHA-256") &&
+ (2 & SUPPORTED_ALGS))
+ {
+ /* 32-bit variant */
+ /* Add 72 because of the 64-bit length and the extra byte because
+ of the bit (actually a byte) append */
+ offset = (((remainderBinLen + 72) >>> 9) << 4) + 15;
+ binaryStringInc = 16;
+ }
+ else if ((variant === "SHA-384" || variant === "SHA-512") &&
+ (4 & SUPPORTED_ALGS))
+ {
+ /* 64-bit variant */
+ /* Add 136 because of the 128-bit length and the extra byte because
+ of the bit (actually a byte) append */
+ offset = (((remainderBinLen + 136) >>> 10) << 5) + 31;
+ binaryStringInc = 32;
}
else
{
- throw "Unexpected error in SHA-2 implementation";
+ throw new Error("Unexpected error in SHA-2 implementation");
}
- while (message.length <= lengthPosition)
+ while (remainder.length <= offset)
{
- message.push(0);
+ remainder.push(0);
}
/* Append '1' at the end of the binary string */
- message[messageLen >>> 5] |= 0x80 << (24 - messageLen % 32);
+ remainder[remainderBinLen >>> 5] |= 0x80 << (24 - remainderBinLen % 32);
/* Append length of binary string in the position such that the new
* length is correct */
- message[lengthPosition] = messageLen;
+ remainder[offset] = remainderBinLen + processedBinLen;
- appendedMessageLength = message.length;
+ appendedMessageLength = remainder.length;
+ /* This will always be at least 1 full chunk */
for (i = 0; i < appendedMessageLength; i += binaryStringInc)
{
- 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 + i;
- int1 = (message.length <= offset) ? 0 : message[offset];
- int2 = (message.length <= offset + 1) ? 0 : message[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]);
+ H = roundSHA2(remainder.slice(i, i + binaryStringInc), H, variant);
}
if (("SHA-224" === variant) && (2 & SUPPORTED_ALGS))
@@ -1164,7 +1365,7 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
}
else /* This should never be reached */
{
- throw "Unexpected error in SHA-2 implementation";
+ throw new Error("Unexpected error in SHA-2 implementation");
}
return retVal;
@@ -1176,283 +1377,128 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
*
* @constructor
* @this {jsSHA}
- * @param {string} srcString The string to be hashed
- * @param {string} inputFormat The format of srcString, HEX, ASCII, TEXT,
- * B64, or BYTES
- * @param {string=} encoding The text encoding to use to encode the source
- * string
+ * @param {string} variant The desired SHA variant (SHA-1, SHA-224, SHA-256,
+ * SHA-384, or SHA-512)
+ * @param {string} inputFormat The format of srcString: HEX, TEXT, B64, or BYTES
+ * @param {{encoding: (string|undefined), numRounds: (string|undefined)}=}
+ * options Optional values
*/
- var jsSHA = function(srcString, inputFormat, encoding)
+ var jsSHA = function(variant, inputFormat, options)
{
- var strBinLen = 0, strToHash = [0], utfType = '', srcConvertRet = null;
+ var processedLen = 0, remainder = [], remainderLen = 0, utfType,
+ intermediateH, converterFunc, shaVariant = variant, outputBinLen,
+ variantBlockSize, roundFunc, finalizeFunc, finalized = false,
+ hmacKeySet = false, keyWithIPad = [], keyWithOPad = [], numRounds,
+ updatedCalled = false, inputOptions;
- utfType = encoding || "UTF8";
+ inputOptions = options || {};
+ utfType = inputOptions["encoding"] || "UTF8";
+ numRounds = inputOptions["numRounds"] || 1;
- if (!(("UTF8" === utfType) || ("UTF16BE" === utfType) || ("UTF16LE" === utfType)))
- {
- throw "encoding must be UTF8, UTF16BE, or UTF16LE";
- }
+ converterFunc = getStrConverter(inputFormat, utfType);
- /* Convert the input string into the correct type */
- if ("HEX" === inputFormat)
- {
- if (0 !== (srcString.length % 2))
- {
- throw "srcString of HEX type must be in byte increments";
- }
- srcConvertRet = hex2binb(srcString);
- strBinLen = srcConvertRet["binLen"];
- strToHash = srcConvertRet["value"];
- }
- else if (("TEXT" === inputFormat) || ("ASCII" === inputFormat))
+ if ((numRounds !== parseInt(numRounds, 10)) || (1 > numRounds))
{
- srcConvertRet = str2binb(srcString, utfType);
- strBinLen = srcConvertRet["binLen"];
- strToHash = srcConvertRet["value"];
+ throw new Error("numRounds must a integer >= 1");
}
- else if ("B64" === inputFormat)
- {
- srcConvertRet = b642binb(srcString);
- strBinLen = srcConvertRet["binLen"];
- strToHash = srcConvertRet["value"];
- }
- else if ("BYTES" === inputFormat)
+
+ if (("SHA-1" === shaVariant) && (1 & SUPPORTED_ALGS))
{
- srcConvertRet = bytes2binb(srcString);
- strBinLen = srcConvertRet["binLen"];
- strToHash = srcConvertRet["value"];
+ variantBlockSize = 512;
+ roundFunc = roundSHA1;
+ finalizeFunc = finalizeSHA1;
+ outputBinLen = 160;
}
else
{
- throw "inputFormat must be HEX, TEXT, ASCII, B64, or BYTES";
- }
-
- /**
- * Returns the desired SHA hash of the string specified at instantiation
- * using the specified parameters
- *
- * @expose
- * @param {string} variant The desired SHA variant (SHA-1, SHA-224,
- * SHA-256, SHA-384, or SHA-512)
- * @param {string} format The desired output formatting (B64, HEX, or BYTES)
- * @param {number=} numRounds The number of rounds of hashing to be
- * executed
- * @param {{outputUpper : boolean, b64Pad : string}=} outputFormatOpts
- * Hash list of output formatting options
- * @return {string} The string representation of the hash in the format
- * specified
- */
- this.getHash = function(variant, format, numRounds, outputFormatOpts)
- {
- var formatFunc = null, message = strToHash.slice(),
- messageBinLen = strBinLen, i;
-
- /* Need to do argument patching since both numRounds and
- outputFormatOpts are optional */
- if (3 === arguments.length)
+ if (6 & SUPPORTED_ALGS)
{
- if ("number" !== typeof numRounds)
+ roundFunc = function (block, H) {
+ return roundSHA2(block, H, shaVariant);
+ };
+ finalizeFunc = function (remainder, remainderBinLen, processedBinLen, H)
{
- outputFormatOpts = numRounds;
- numRounds = 1;
- }
- }
- else if (2 === arguments.length)
- {
- numRounds = 1;
- }
-
- /* Validate the numRounds argument */
- if ((numRounds !== parseInt(numRounds, 10)) || (1 > numRounds))
- {
- throw "numRounds must a integer >= 1";
- }
-
- /* Validate the output format selection */
- switch (format)
- {
- case "HEX":
- formatFunc = binb2hex;
- break;
- case "B64":
- formatFunc = binb2b64;
- break;
- case "BYTES":
- formatFunc = binb2bytes;
- break;
- default:
- throw "format must be HEX, B64, or BYTES";
+ return finalizeSHA2(remainder, remainderBinLen, processedBinLen, H, shaVariant);
+ };
}
- if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS))
+ if (("SHA-224" === shaVariant) && (2 & SUPPORTED_ALGS))
{
- for (i = 0; i < numRounds; i += 1)
- {
- message = coreSHA1(message, messageBinLen);
- messageBinLen = 160;
- }
+ variantBlockSize = 512;
+ outputBinLen = 224;
}
- else if (("SHA-224" === variant) && (2 & SUPPORTED_ALGS))
+ else if (("SHA-256" === shaVariant) && (2 & SUPPORTED_ALGS))
{
- for (i = 0; i < numRounds; i += 1)
- {
- message = coreSHA2(message, messageBinLen, variant);
- messageBinLen = 224;
- }
+ variantBlockSize = 512;
+ outputBinLen = 256;
}
- else if (("SHA-256" === variant) && (2 & SUPPORTED_ALGS))
+ else if (("SHA-384" === shaVariant) && (4 & SUPPORTED_ALGS))
{
- for (i = 0; i < numRounds; i += 1)
- {
- message = coreSHA2(message, messageBinLen, variant);
- messageBinLen = 256;
- }
+ variantBlockSize = 1024;
+ outputBinLen = 384;
}
- else if (("SHA-384" === variant) && (4 & SUPPORTED_ALGS))
+ else if (("SHA-512" === shaVariant) && (4 & SUPPORTED_ALGS))
{
- for (i = 0; i < numRounds; i += 1)
- {
- message = coreSHA2(message, messageBinLen, variant);
- messageBinLen = 384;
- }
- }
- else if (("SHA-512" === variant) && (4 & SUPPORTED_ALGS))
- {
- for (i = 0; i < numRounds; i += 1)
- {
- message = coreSHA2(message, messageBinLen, variant);
- messageBinLen = 512;
- }
+ variantBlockSize = 1024;
+ outputBinLen = 512;
}
else
{
- throw "Chosen SHA variant is not supported";
+ throw new Error("Chosen SHA variant is not supported");
}
+ }
- return formatFunc(message, getOutputOpts(outputFormatOpts));
- };
+ intermediateH = getH(shaVariant);
/**
- * Returns the desired HMAC of the string specified at instantiation
- * using the key and variant parameter
+ * Sets the HMAC key for an eventual getHMAC call. Must be called
+ * immediately after jsSHA object instantiation
*
* @expose
* @param {string} key The key used to calculate the HMAC
- * @param {string} inputFormat The format of key, HEX, TEXT, ASCII,
- * B64, or BYTES
- * @param {string} variant The desired SHA variant (SHA-1, SHA-224,
- * SHA-256, SHA-384, or SHA-512)
- * @param {string} outputFormat The desired output formatting
- * (B64, HEX, or BYTES)
- * @param {{outputUpper : boolean, b64Pad : string}=} outputFormatOpts
- * associative array of output formatting options
- * @return {string} The string representation of the hash in the format
- * specified
+ * @param {string} inputFormat The format of key, HEX, TEXT, B64, or BYTES
+ * @param {{encoding : (string|undefined)}=} options Associative array
+ * of input format options
*/
- this.getHMAC = function(key, inputFormat, variant, outputFormat,
- outputFormatOpts)
+ this.setHMACKey = function(key, inputFormat, options)
{
- var formatFunc, keyToUse, blockByteSize, blockBitSize, i,
- retVal, lastArrayIndex, keyBinLen, hashBitSize,
- keyWithIPad = [], keyWithOPad = [], keyConvertRet = null;
+ var keyConverterFunc, convertRet, keyBinLen, keyToUse, blockByteSize,
+ i, lastArrayIndex, keyOptions;
- /* Validate the output format selection */
- switch (outputFormat)
+ if (true === hmacKeySet)
{
- case "HEX":
- formatFunc = binb2hex;
- break;
- case "B64":
- formatFunc = binb2b64;
- break;
- case "BYTES":
- formatFunc = binb2bytes;
- break;
- default:
- throw "outputFormat must be HEX, B64, or BYTES";
+ throw new Error("HMAC key already set");
}
- /* Validate the hash variant selection and set needed variables */
- if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS))
- {
- blockByteSize = 64;
- hashBitSize = 160;
- }
- else if (("SHA-224" === variant) && (2 & SUPPORTED_ALGS))
- {
- blockByteSize = 64;
- hashBitSize = 224;
- }
- else if (("SHA-256" === variant) && (2 & SUPPORTED_ALGS))
- {
- blockByteSize = 64;
- hashBitSize = 256;
- }
- else if (("SHA-384" === variant) && (4 & SUPPORTED_ALGS))
+ if (true === finalized)
{
- blockByteSize = 128;
- hashBitSize = 384;
- }
- else if (("SHA-512" === variant) && (4 & SUPPORTED_ALGS))
- {
- blockByteSize = 128;
- hashBitSize = 512;
- }
- else
- {
- throw "Chosen SHA variant is not supported";
+ throw new Error("Cannot set HMAC key after finalizing hash");
}
- /* Validate input format selection */
- if ("HEX" === inputFormat)
- {
- keyConvertRet = hex2binb(key);
- keyBinLen = keyConvertRet["binLen"];
- keyToUse = keyConvertRet["value"];
- }
- else if (("TEXT" === inputFormat) || ("ASCII" === inputFormat))
- {
- keyConvertRet = str2binb(key, utfType);
- keyBinLen = keyConvertRet["binLen"];
- keyToUse = keyConvertRet["value"];
- }
- else if ("B64" === inputFormat)
- {
- keyConvertRet = b642binb(key);
- keyBinLen = keyConvertRet["binLen"];
- keyToUse = keyConvertRet["value"];
- }
- else if ("BYTES" === inputFormat)
+ if (true === updatedCalled)
{
- keyConvertRet = bytes2binb(key);
- keyBinLen = keyConvertRet["binLen"];
- keyToUse = keyConvertRet["value"];
- }
- else
- {
- throw "inputFormat must be HEX, TEXT, ASCII, B64, or BYTES";
+ throw new Error("Cannot set HMAC key after calling update");
}
+ keyOptions = options || {};
+ utfType = keyOptions["encoding"] || "UTF8";
+
+ keyConverterFunc = getStrConverter(inputFormat, utfType);
+
+ convertRet = keyConverterFunc(key);
+ keyBinLen = convertRet["binLen"];
+ keyToUse = convertRet["value"];
+
+ blockByteSize = variantBlockSize >>> 3;
+
/* These are used multiple times, calculate and store them */
- blockBitSize = blockByteSize * 8;
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))
{
- if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS))
- {
- keyToUse = coreSHA1(keyToUse, keyBinLen);
- }
- else if (6 & SUPPORTED_ALGS)
- {
- keyToUse = coreSHA2(keyToUse, keyBinLen, variant);
- }
- else
- {
- throw "Unexpected error in HMAC implementation";
- }
+ keyToUse = finalizeFunc(keyToUse, keyBinLen, 0, getH(shaVariant));
/* 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 */
@@ -1481,36 +1527,145 @@ var SUPPORTED_ALGS = 4 | 2 | 1;
keyWithOPad[i] = keyToUse[i] ^ 0x5C5C5C5C;
}
- /* Calculate the HMAC */
- if (("SHA-1" === variant) && (1 & SUPPORTED_ALGS))
+ intermediateH = roundFunc(keyWithIPad, intermediateH);
+ 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} 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)
{
- retVal = coreSHA1(
- keyWithOPad.concat(
- coreSHA1(
- keyWithIPad.concat(strToHash),
- blockBitSize + strBinLen
- )
- ),
- blockBitSize + hashBitSize);
+ if (updateProcessedLen + variantBlockSize <= chunkBinLen)
+ {
+ intermediateH = roundFunc(
+ chunk.slice(i, i + variantBlockIntInc),
+ intermediateH
+ );
+ updateProcessedLen += variantBlockSize;
+ }
}
- else if (6 & SUPPORTED_ALGS)
+ 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, or BYTES)
+ * @param {{outputUpper : (boolean|undefined), b64Pad : (string|undefined)}=}
+ * options Hash list of output formatting options
+ * @return {string} The string representation of the hash in the format
+ * specified
+ */
+ this.getHash = function(format, options)
+ {
+ var formatFunc, i, outputOptions;
+
+ if (true === hmacKeySet)
{
- retVal = coreSHA2(
- keyWithOPad.concat(
- coreSHA2(
- keyWithIPad.concat(strToHash),
- blockBitSize + strBinLen,
- variant
- )
- ),
- blockBitSize + hashBitSize, variant);
+ throw new Error("Cannot call getHash after setting HMAC key");
}
- else
+
+ outputOptions = getOutputOpts(options);
+
+ /* Validate the output format selection */
+ switch (format)
+ {
+ case "HEX":
+ formatFunc = function(binarray) {return binb2hex(binarray, outputOptions);};
+ break;
+ case "B64":
+ formatFunc = function(binarray) {return binb2b64(binarray, outputOptions);};
+ break;
+ case "BYTES":
+ formatFunc = binb2bytes;
+ break;
+ default:
+ throw new Error("format must be HEX, B64, or BYTES");
+ }
+
+ if (false === finalized)
+ {
+ intermediateH = finalizeFunc(remainder, remainderLen, processedLen, intermediateH);
+ for (i = 1; i < numRounds; i += 1)
+ {
+ intermediateH = finalizeFunc(intermediateH, outputBinLen, 0, getH(shaVariant));
+ }
+ }
+
+ finalized = true;
+ return formatFunc(intermediateH);
+ };
+
+ /**
+ * 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, or BYTES)
+ * @param {{outputUpper : (boolean|undefined), b64Pad : (string|undefined)}=}
+ * options associative array of output formatting options
+ * @return {string} The string representation of the hash in the format
+ * specified
+ */
+ this.getHMAC = function(format, options)
+ {
+ var formatFunc, firstHash, outputOptions;
+
+ 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 binb2hex(binarray, outputOptions);};
+ break;
+ case "B64":
+ formatFunc = function(binarray) {return binb2b64(binarray, outputOptions);};
+ break;
+ case "BYTES":
+ formatFunc = binb2bytes;
+ break;
+ default:
+ throw new Error("outputFormat must be HEX, B64, or BYTES");
+ }
+
+ if (false === finalized)
{
- throw "Unexpected error in HMAC implementation";
+ firstHash = finalizeFunc(remainder, remainderLen, processedLen, intermediateH);
+ intermediateH = roundFunc(keyWithOPad, getH(shaVariant));
+ intermediateH = finalizeFunc(firstHash, outputBinLen, variantBlockSize, intermediateH);
}
- return formatFunc(retVal, getOutputOpts(outputFormatOpts));
+ finalized = true;
+ return formatFunc(intermediateH);
};
};