Skip to content

Commit

Permalink
md5: use noble-hashes, and import code dynamically
Browse files Browse the repository at this point in the history
To fetch the code only when the function is called.
  • Loading branch information
larabr committed Feb 5, 2025
1 parent 5ecff61 commit 55d8f64
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 204 deletions.
201 changes: 0 additions & 201 deletions lib/crypto/_md5.js

This file was deleted.

89 changes: 89 additions & 0 deletions lib/crypto/_md5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* eslint-disable @typescript-eslint/lines-between-class-members */
// Copied from https://github.com/paulmillr/noble-hashes/blob/main/test/misc/md5.ts

import { HashMD } from '@noble/hashes/_md';
import { rotl, wrapConstructor } from '@noble/hashes/utils';

// Per-round constants
const K = Array.from({ length: 64 }, (_, i) => Math.floor(2 ** 32 * Math.abs(Math.sin(i + 1))));
// Choice: a ? b : c
const Chi = (a: number, b: number, c: number) => (a & b) ^ (~a & c);
// Initial state (same as sha1, but 4 u32 instead of 5)
const IV = /* @__PURE__ */ new Uint32Array([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]);
// Temporary buffer, not used to store anything between runs
// Named this way for SHA1 compat
const MD5_W = /* @__PURE__ */ new Uint32Array(16);
class MD5 extends HashMD<MD5> {
private A = IV[0] | 0;
private B = IV[1] | 0;
private C = IV[2] | 0;
private D = IV[3] | 0;

constructor() {
super(64, 16, 8, true);
}

protected get(): [number, number, number, number] {
const { A, B, C, D } = this;
return [A, B, C, D];
}

protected set(A: number, B: number, C: number, D: number) {
this.A = A | 0;
this.B = B | 0;
this.C = C | 0;
this.D = D | 0;
}

protected process(view: DataView, offset: number): void {
for (let i = 0; i < 16; i++, offset += 4) MD5_W[i] = view.getUint32(offset, true);
// Compression function main loop, 64 rounds
let { A, B, C, D } = this;
for (let i = 0; i < 64; i++) {
// eslint-disable-next-line one-var, one-var-declaration-per-line
let F, g, s;
if (i < 16) {
// eslint-disable-next-line new-cap
F = Chi(B, C, D);
g = i;
s = [7, 12, 17, 22];
} else if (i < 32) {
// eslint-disable-next-line new-cap
F = Chi(D, B, C);
g = (5 * i + 1) % 16;
s = [5, 9, 14, 20];
} else if (i < 48) {
F = B ^ C ^ D;
g = (3 * i + 5) % 16;
s = [4, 11, 16, 23];
} else {
F = C ^ (B | ~D);
g = (7 * i) % 16;
s = [6, 10, 15, 21];
}
F = F + A + K[i] + MD5_W[g];
A = D;
D = C;
C = B;
B += rotl(F, s[i % 4]);
}

// Add the compressed chunk to the current hash value
A = (A + this.A) | 0;
B = (B + this.B) | 0;
C = (C + this.C) | 0;
D = (D + this.D) | 0;
this.set(A, B, C, D);
}

// eslint-disable-next-line class-methods-use-this
protected roundClean() {
MD5_W.fill(0);
}

destroy() {
this.set(0, 0, 0, 0);
this.buffer.fill(0);
}
}
export const md5 = /* @__PURE__ */ wrapConstructor(() => new MD5());
4 changes: 1 addition & 3 deletions lib/crypto/hash.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { MaybeWebStream } from '../pmcrypto';
import md5 from './_md5';

export const SHA256 = async (data: Uint8Array) => {
const digest = await crypto.subtle.digest('SHA-256', data);
Expand All @@ -14,9 +13,8 @@ export const SHA512 = async (data: Uint8Array) => {
/**
* MD5 is an unsafe hash function. It should normally not be used.
* It's exposed because it's required for old auth versions.
* @see openpgp.crypto.hash.md5
*/
export const unsafeMD5 = (data: Uint8Array) => md5(data);
export const unsafeMD5 = async (data: Uint8Array) => import('./_md5').then(({ md5 }) => md5(data));

/**
* SHA1 is an unsafe hash function. It should not be used for cryptographic purposes.
Expand Down

0 comments on commit 55d8f64

Please sign in to comment.