// Pure-JS MD5 (RFC 1321), used to verify .seq [SIGNATURE] sections // synchronously (WebCrypto has no MD5). Input is treated as a byte string. export function md5Hex(input: string): string { const bytes: number[] = [] for (let i = 0; i < input.length; i++) bytes.push(input.charCodeAt(i) & 0xff) const origLenBits = bytes.length * 8 bytes.push(0x80) while (bytes.length % 64 !== 56) bytes.push(0) let len = origLenBits for (let i = 0; i < 8; i++) { bytes.push(len & 0xff) len = Math.floor(len / 256) } const S = [ 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, ] const K = Array.from({ length: 64 }, (_, i) => Math.floor(Math.abs(Math.sin(i + 1)) * 4294967296) >>> 0) let a0 = 0x67452301 let b0 = 0xefcdab89 let c0 = 0x98badcfe let d0 = 0x10325476 const rotl = (x: number, c: number) => ((x << c) | (x >>> (32 - c))) >>> 0 for (let chunk = 0; chunk < bytes.length; chunk += 64) { const M = Array.from({ length: 16 }, (_, j) => { const o = chunk + j * 4 return (bytes[o] | (bytes[o + 1] << 8) | (bytes[o + 2] << 16) | (bytes[o + 3] << 24)) >>> 0 }) let A = a0 let B = b0 let C = c0 let D = d0 for (let i = 0; i < 64; i++) { let F: number let g: number if (i < 16) { F = (B & C) | (~B & D) g = i } else if (i < 32) { F = (D & B) | (~D & C) g = (5 * i + 1) % 16 } else if (i < 48) { F = B ^ C ^ D g = (3 * i + 5) % 16 } else { F = C ^ (B | ~D) g = (7 * i) % 16 } F = (F + A + K[i] + M[g]) >>> 0 A = D D = C C = B B = (B + rotl(F, S[i])) >>> 0 } a0 = (a0 + A) >>> 0 b0 = (b0 + B) >>> 0 c0 = (c0 + C) >>> 0 d0 = (d0 + D) >>> 0 } const le = (w: number) => { let s = '' for (let i = 0; i < 4; i++) s += ((w >>> (i * 8)) & 0xff).toString(16).padStart(2, '0') return s } return le(a0) + le(b0) + le(c0) + le(d0) }