/** * Pure-JS SHA-1 implementation (FIPS 180-4). * Replaces the `sha1` npm package to avoid pulling in a Node.js `Buffer` * polyfill (~28 KB) when bundled for the browser. */ /** Compute SHA-1 hex digest of a UTF-8 string (FIPS 180-4). */ export function sha1(message: string): string { // Encode UTF-8 const bytes: number[] = []; for (let i = 0; i < message.length; i++) { let c = message.charCodeAt(i); if (c < 0x80) { bytes.push(c); } else if (c < 0x800) { bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f)); } else if (c >= 0xd800 && c <= 0xdbff && i + 1 < message.length) { const next = message.charCodeAt(++i); c = 0x10000 + ((c & 0x3ff) << 10) + (next & 0x3ff); bytes.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f)); } else { bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f)); } } // Pre-processing: pad to 512-bit blocks const bitLen = bytes.length * 8; bytes.push(0x80); while (bytes.length % 64 !== 56) bytes.push(0); // Append length as 64-bit big-endian (high 32 bits always 0 for strings < 512 MB) bytes.push(0, 0, 0, 0); bytes.push((bitLen >>> 24) & 0xff, (bitLen >>> 16) & 0xff, (bitLen >>> 8) & 0xff, bitLen & 0xff); // Initialize hash values let h0 = 0x67452301; let h1 = 0xefcdab89; let h2 = 0x98badcfe; let h3 = 0x10325476; let h4 = 0xc3d2e1f0; // Process each 512-bit block const w = new Array(80); for (let offset = 0; offset < bytes.length; offset += 64) { for (let i = 0; i < 16; i++) { w[i] = (bytes[offset + i * 4] << 24) | (bytes[offset + i * 4 + 1] << 16) | (bytes[offset + i * 4 + 2] << 8) | bytes[offset + i * 4 + 3]; } for (let i = 16; i < 80; i++) { const n = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]; w[i] = (n << 1) | (n >>> 31); } let a = h0, b = h1, c = h2, d = h3, e = h4; for (let i = 0; i < 80; i++) { let f: number, k: number; if (i < 20) { f = (b & c) | (~b & d); k = 0x5a827999; } else if (i < 40) { f = b ^ c ^ d; k = 0x6ed9eba1; } else if (i < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8f1bbcdc; } else { f = b ^ c ^ d; k = 0xca62c1d6; } const temp = (((a << 5) | (a >>> 27)) + f + e + k + w[i]) | 0; e = d; d = c; c = (b << 30) | (b >>> 2); b = a; a = temp; } h0 = (h0 + a) | 0; h1 = (h1 + b) | 0; h2 = (h2 + c) | 0; h3 = (h3 + d) | 0; h4 = (h4 + e) | 0; } // Produce hex digest let hex = ''; for (const h of [h0, h1, h2, h3, h4]) { const part = (h >>> 0).toString(16); hex += (part.length < 8 ? '00000000'.slice(part.length) : '') + part; } return hex; }