Created
December 24, 2024 21:57
-
-
Save ArrayIterator/9f15024e4bf80d4319fb1453bb65b750 to your computer and use it in GitHub Desktop.
Javascript Implementation sha1 & hash_hmac<sha1>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment