|
| 1 | +// Copied from https://github.com/paulmillr/noble-hashes/blob/main/test/misc/md5.ts |
| 2 | + |
| 3 | +import { HashMD } from '@noble/hashes/_md'; |
| 4 | +import { rotl, wrapConstructor } from '@noble/hashes/utils'; |
| 5 | + |
| 6 | +// Per-round constants |
| 7 | +const K = Array.from({ length: 64 }, (_, i) => Math.floor(2 ** 32 * Math.abs(Math.sin(i + 1)))); |
| 8 | +// Choice: a ? b : c |
| 9 | +const Chi = (a: number, b: number, c: number) => (a & b) ^ (~a & c); |
| 10 | +// Initial state (same as sha1, but 4 u32 instead of 5) |
| 11 | +const IV = /* @__PURE__ */ new Uint32Array([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]); |
| 12 | +// Temporary buffer, not used to store anything between runs |
| 13 | +// Named this way for SHA1 compat |
| 14 | +const MD5_W = /* @__PURE__ */ new Uint32Array(16); |
| 15 | +class MD5 extends HashMD<MD5> { |
| 16 | + private A = IV[0] | 0; |
| 17 | + private B = IV[1] | 0; |
| 18 | + private C = IV[2] | 0; |
| 19 | + private D = IV[3] | 0; |
| 20 | + constructor() { |
| 21 | + super(64, 16, 8, true); |
| 22 | + } |
| 23 | + protected get(): [number, number, number, number] { |
| 24 | + const { A, B, C, D } = this; |
| 25 | + return [A, B, C, D]; |
| 26 | + } |
| 27 | + protected set(A: number, B: number, C: number, D: number) { |
| 28 | + this.A = A | 0; |
| 29 | + this.B = B | 0; |
| 30 | + this.C = C | 0; |
| 31 | + this.D = D | 0; |
| 32 | + } |
| 33 | + protected process(view: DataView, offset: number): void { |
| 34 | + for (let i = 0; i < 16; i++, offset += 4) MD5_W[i] = view.getUint32(offset, true); |
| 35 | + // Compression function main loop, 64 rounds |
| 36 | + let { A, B, C, D } = this; |
| 37 | + for (let i = 0; i < 64; i++) { |
| 38 | + // eslint-disable-next-line one-var, one-var-declaration-per-line |
| 39 | + let F, g, s; |
| 40 | + if (i < 16) { |
| 41 | + // eslint-disable-next-line new-cap |
| 42 | + F = Chi(B, C, D); |
| 43 | + g = i; |
| 44 | + s = [7, 12, 17, 22]; |
| 45 | + } else if (i < 32) { |
| 46 | + // eslint-disable-next-line new-cap |
| 47 | + F = Chi(D, B, C); |
| 48 | + g = (5 * i + 1) % 16; |
| 49 | + s = [5, 9, 14, 20]; |
| 50 | + } else if (i < 48) { |
| 51 | + F = B ^ C ^ D; |
| 52 | + g = (3 * i + 5) % 16; |
| 53 | + s = [4, 11, 16, 23]; |
| 54 | + } else { |
| 55 | + F = C ^ (B | ~D); |
| 56 | + g = (7 * i) % 16; |
| 57 | + s = [6, 10, 15, 21]; |
| 58 | + } |
| 59 | + F = F + A + K[i] + MD5_W[g]; |
| 60 | + A = D; |
| 61 | + D = C; |
| 62 | + C = B; |
| 63 | + B = B + rotl(F, s[i % 4]); |
| 64 | + } |
| 65 | + // Add the compressed chunk to the current hash value |
| 66 | + A = (A + this.A) | 0; |
| 67 | + B = (B + this.B) | 0; |
| 68 | + C = (C + this.C) | 0; |
| 69 | + D = (D + this.D) | 0; |
| 70 | + this.set(A, B, C, D); |
| 71 | + } |
| 72 | + protected roundClean() { |
| 73 | + MD5_W.fill(0); |
| 74 | + } |
| 75 | + destroy() { |
| 76 | + this.set(0, 0, 0, 0); |
| 77 | + this.buffer.fill(0); |
| 78 | + } |
| 79 | +} |
| 80 | +export const md5 = /* @__PURE__ */ wrapConstructor(() => new MD5()); |
0 commit comments