/** * 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; }