133 lines
4.1 KiB
JavaScript
133 lines
4.1 KiB
JavaScript
/**
|
|
* Jdenticon
|
|
* https://github.com/dmester/jdenticon
|
|
* Copyright © Daniel Mester Pirttijärvi
|
|
*/
|
|
|
|
/**
|
|
* Computes a SHA1 hash for any value and returns it as a hexadecimal string.
|
|
*
|
|
* This function is optimized for minimal code size and rather short messages.
|
|
*
|
|
* @param {string} message
|
|
*/
|
|
export function sha1(message) {
|
|
const HASH_SIZE_HALF_BYTES = 40;
|
|
const BLOCK_SIZE_WORDS = 16;
|
|
|
|
// Variables
|
|
// `var` is used to be able to minimize the number of `var` keywords.
|
|
var i = 0,
|
|
f = 0,
|
|
|
|
// Use `encodeURI` to UTF8 encode the message without any additional libraries
|
|
// We could use `unescape` + `encodeURI` to minimize the code, but that would be slightly risky
|
|
// since `unescape` is deprecated.
|
|
urlEncodedMessage = encodeURI(message) + "%80", // trailing '1' bit padding
|
|
|
|
// This can be changed to a preallocated Uint32Array array for greater performance and larger code size
|
|
data = [],
|
|
dataSize,
|
|
|
|
hashBuffer = [],
|
|
|
|
a = 0x67452301,
|
|
b = 0xefcdab89,
|
|
c = ~a,
|
|
d = ~b,
|
|
e = 0xc3d2e1f0,
|
|
hash = [a, b, c, d, e],
|
|
|
|
blockStartIndex = 0,
|
|
hexHash = "";
|
|
|
|
/**
|
|
* Rotates the value a specified number of bits to the left.
|
|
* @param {number} value Value to rotate
|
|
* @param {number} shift Bit count to shift.
|
|
*/
|
|
function rotl(value, shift) {
|
|
return (value << shift) | (value >>> (32 - shift));
|
|
}
|
|
|
|
// Message data
|
|
for ( ; i < urlEncodedMessage.length; f++) {
|
|
data[f >> 2] = data[f >> 2] |
|
|
(
|
|
(
|
|
urlEncodedMessage[i] == "%"
|
|
// Percent encoded byte
|
|
? parseInt(urlEncodedMessage.substring(i + 1, i += 3), 16)
|
|
// Unencoded byte
|
|
: urlEncodedMessage.charCodeAt(i++)
|
|
)
|
|
|
|
// Read bytes in reverse order (big endian words)
|
|
<< ((3 - (f & 3)) * 8)
|
|
);
|
|
}
|
|
|
|
// f is now the length of the utf8 encoded message
|
|
// 7 = 8 bytes (64 bit) for message size, -1 to round down
|
|
// >> 6 = integer division with block size
|
|
dataSize = (((f + 7) >> 6) + 1) * BLOCK_SIZE_WORDS;
|
|
|
|
// Message size in bits.
|
|
// SHA1 uses a 64 bit integer to represent the size, but since we only support short messages only the least
|
|
// significant 32 bits are set. -8 is for the '1' bit padding byte.
|
|
data[dataSize - 1] = f * 8 - 8;
|
|
|
|
// Compute hash
|
|
for ( ; blockStartIndex < dataSize; blockStartIndex += BLOCK_SIZE_WORDS) {
|
|
for (i = 0; i < 80; i++) {
|
|
f = rotl(a, 5) + e + (
|
|
// Ch
|
|
i < 20 ? ((b & c) ^ ((~b) & d)) + 0x5a827999 :
|
|
|
|
// Parity
|
|
i < 40 ? (b ^ c ^ d) + 0x6ed9eba1 :
|
|
|
|
// Maj
|
|
i < 60 ? ((b & c) ^ (b & d) ^ (c & d)) + 0x8f1bbcdc :
|
|
|
|
// Parity
|
|
(b ^ c ^ d) + 0xca62c1d6
|
|
) + (
|
|
hashBuffer[i] = i < BLOCK_SIZE_WORDS
|
|
// Bitwise OR is used to coerse `undefined` to 0
|
|
? (data[blockStartIndex + i] | 0)
|
|
: rotl(hashBuffer[i - 3] ^ hashBuffer[i - 8] ^ hashBuffer[i - 14] ^ hashBuffer[i - 16], 1)
|
|
);
|
|
|
|
e = d;
|
|
d = c;
|
|
c = rotl(b, 30);
|
|
b = a;
|
|
a = f;
|
|
}
|
|
|
|
hash[0] = a = ((hash[0] + a) | 0);
|
|
hash[1] = b = ((hash[1] + b) | 0);
|
|
hash[2] = c = ((hash[2] + c) | 0);
|
|
hash[3] = d = ((hash[3] + d) | 0);
|
|
hash[4] = e = ((hash[4] + e) | 0);
|
|
}
|
|
|
|
// Format hex hash
|
|
for (i = 0; i < HASH_SIZE_HALF_BYTES; i++) {
|
|
hexHash += (
|
|
(
|
|
// Get word (2^3 half-bytes per word)
|
|
hash[i >> 3] >>>
|
|
|
|
// Append half-bytes in reverse order
|
|
((7 - (i & 7)) * 4)
|
|
)
|
|
// Clamp to half-byte
|
|
& 0xf
|
|
).toString(16);
|
|
}
|
|
|
|
return hexHash;
|
|
}
|