Files
blog/blog-admin/src/lib/md5.ts
T
zqlit a74b3c7127
Deploy to Production / pre-check (push) Successful in 58s
Deploy to Production / build (push) Successful in 4m3s
Deploy to Production / deploy-edgeone (push) Successful in 3m48s
Deploy to Production / finalize (push) Successful in 26s
Deploy to Production / notify-failure (push) Skipped
归档 artalk-cf 评论后端 + rss-robot 到 blog-admin(含技术选型/模块分布 README)
2026-10-04 08:45:40 +08:00

90 lines
3.0 KiB
TypeScript

// Artalk 的 email_encrypted 字段 = MD5(email 小写),前端拿它当 Gravatar / Cravatar 的 hash。
// Workers 的 crypto.subtle 不提供 MD5,所以这里自带一份。
const K_HEX = [
'd76aa478', 'e8c7b756', '242070db', 'c1bdceee', 'f57c0faf', '4787c62a', 'a8304613', 'fd469501',
'698098d8', '8b44f7af', 'ffff5bb1', '895cd7be', '6b901122', 'fd987193', 'a679438e', '49b40821',
'f61e2562', 'c040b340', '265e5a51', 'e9b6c7aa', 'd62f105d', '02441453', 'd8a1e681', 'e7d3fbc8',
'21e1cde6', 'c33707d6', 'f4d50d87', '455a14ed', 'a9e3e905', 'fcefa3f8', '676f02d9', '8d2a4c8a',
'fffa3942', '8771f681', '6d9d6122', 'fde5380c', 'a4beea44', '4bdecfa9', 'f6bb4b60', 'bebfbc70',
'289b7ec6', 'eaa127fa', 'd4ef3085', '04881d05', 'd9d4d039', 'e6db99e5', '1fa27cf8', 'c4ac5665',
'f4292244', '432aff97', 'ab9423a7', 'fc93a039', '655b59c3', '8f0ccc92', 'ffeff47d', '85845dd1',
'6fa87e4f', 'fe2ce6e0', 'a3014314', '4e0811a1', 'f7537e82', 'bd3af235', '2ad7d2bb', 'eb86d391',
].map((h) => parseInt(h, 16) >>> 0);
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,
];
function wordToHexLE(n: number): string {
let out = '';
for (let i = 0; i < 4; i++) out += ((n >>> (i * 8)) & 0xff).toString(16).padStart(2, '0');
return out;
}
export function md5(input: string): string {
const bytes = new TextEncoder().encode(input);
const len = bytes.length;
const padded = new Uint8Array((((len + 8) >> 6) + 1) * 64);
padded.set(bytes);
padded[len] = 0x80;
const dv = new DataView(padded.buffer);
const bitLen = len * 8;
dv.setUint32(padded.length - 8, bitLen >>> 0, true);
dv.setUint32(padded.length - 4, Math.floor(bitLen / 0x100000000), true);
let a0 = 0x67452301;
let b0 = 0xefcdab89;
let c0 = 0x98badcfe;
let d0 = 0x10325476;
for (let chunk = 0; chunk < padded.length; chunk += 64) {
const M = new Uint32Array(16);
for (let i = 0; i < 16; i++) M[i] = dv.getUint32(chunk + i * 4, true);
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;
}
const sum = (F + A + K_HEX[i] + M[g]) >>> 0;
A = D;
D = C;
C = B;
B = (B + (((sum << S[i]) | (sum >>> (32 - S[i]))) >>> 0)) >>> 0;
}
a0 = (a0 + A) >>> 0;
b0 = (b0 + B) >>> 0;
c0 = (c0 + C) >>> 0;
d0 = (d0 + D) >>> 0;
}
return wordToHexLE(a0) + wordToHexLE(b0) + wordToHexLE(c0) + wordToHexLE(d0);
}
export function md5Lower(input: string): string {
return md5(input.trim().toLowerCase());
}