/** * SHA1 * Implementation of SHA1 algorithm as described in RFC 3174 * @see https://www.ietf.org/rfc/rfc3174.txt * * @param {string} string input to be hashed * @param {boolean} [raw=false] If the optional binary is set to true, then the sha1 digest is instead returned in raw binary format with a length of 20, otherwise the returned value is a 40-character hexadecimal number. * @return {string|false} Calculated the sha1 hash of a string, returning false if fail */ const sha1 = (string, raw = false) => { string = typeof string === 'number' ? string.toString() : ( typeof string === 'boolean' ? (string ? '1' : '0') : string + '' ); const strLen = string.length; const len = strLen * 8; const binLen = strLen >> 2; if (binLen <= 0) { return false; } const rotate_left = (n, s) => (n << s) | (n >>> (32 - s)); const safe_add_16b = (x, y) => ((x >> 16) + (y >> 16) + (((x & 0xFFFF) + (y & 0xFFFF)) >> 16) << 16) | (((x & 0xFFFF) + (y & 0xFFFF)) & 0xFFFF); /** * Perform the appropriate triplet combination function for the current iteration * @param {number} t * @param {number} b * @param {number} c * @param {number} d * @return {number} */ const sha1_ft = (t, b, c, d) => { if (t < 20) { return (b & c) | ((~b) & d); } if (t < 40) { return b ^ c ^ d; } if (t < 60) { return (b & c) | (b & d) | (c & d); } return b ^ c ^ d; } /** * Determine the appropriate additive constant for the current iteration * @param {number} t * @return {number} */ const sha1_kt = (t) => { if (t < 20) { return 1518500249; } if (t < 40) { return 1859775393; } if (t < 60) { return -1894007588; } return -899497514; } let i, t; let binArray = new Array(binLen); for (i = 0; i < len; i += 8) { binArray[i >> 5] |= (string.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); } /* append padding */ binArray[len >> 5] |= 0x80 << (24 - len % 32); binArray[((len + 64 >> 9) << 4) + 15] = len; let wordArray = Array(80), a = 1732584193, b = -271733879, c = -1732584194, d = 271733878, e = -1009589776; for (i = 0; i < binArray.length; i += 16) { let oldA = a, oldB = b, oldC = c, oldD = d, oldE = e; for (let j = 0; j < 80; j++) { wordArray[j] = j < 16 ? binArray[i + j] : rotate_left(wordArray[j - 3] ^ wordArray[j - 8] ^ wordArray[j - 14] ^ wordArray[j - 16], 1); t = safe_add_16b( safe_add_16b( rotate_left(a, 5), sha1_ft(j, b, c, d) ), safe_add_16b( safe_add_16b(e, wordArray[j]), sha1_kt(j) ) ); e = d; d = c; c = rotate_left(b, 30); b = a; a = t; } a = safe_add_16b(a, oldA); b = safe_add_16b(b, oldB); c = safe_add_16b(c, oldC); d = safe_add_16b(d, oldD); e = safe_add_16b(e, oldE); } binArray = [a, b, c, d, e]; // reuse binArray variable let hash = ''; // convert big-endian if (raw) { for (let i = 0; i < 160; i += 8) { hash += String.fromCharCode((binArray[i >> 5] >>> (32 - 8 - i % 32)) & 0xFF); } } else { const HEX = '0123456789abcdef'; for (let i = 0; i < 20; i++) { hash += HEX.charAt((binArray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF); hash += HEX.charAt((binArray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF); } } return hash; } /** * HMAC-SHA1 * Implementation of HMAC-SHA1 algorithm as described in RFC 2104 * @see https://www.ietf.org/rfc/rfc2104.txt * * @param {string} string input to be hashed * @param {string} key secret key * @param {boolean} [raw=false] If the optional binary is set to true, then the sha1 digest is instead returned in raw binary format with a length of 20, otherwise the returned value is a 40-character hexadecimal number. * @return {string|false} returning hashed data, otherwise false if fail */ const hmac_sha1 = (string, key, raw = false) => { key = typeof key === 'number' ? key.toString() : ( // boolean is 1 or 0 typeof key === 'boolean' ? (key ? '1' : '0') : key + '' ); string = typeof string === 'number' ? string.toString() : ( typeof string === 'boolean' ? (string ? '1' : '0') : string + '' ); if (key.length > 64) { // keys longer than block-size are shortened key = sha1(key, true); if (key === false) { return false; } } const bytes = new Array(64); let len = key.length; while (len--) { bytes[len] = key.charCodeAt(len) & 0xFF; } let oPadding = '', iPadding = ''; while (bytes.length > 0) { const byte = bytes.shift(); oPadding += String.fromCharCode(byte ^ 0x5C); iPadding += String.fromCharCode(byte ^ 0x36); } const iPadRes = sha1(iPadding + string, true); return iPadRes ? sha1(oPadding + iPadRes, raw) : false; }