mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
Unroll the MD5 block function (#1465)
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5775-md5-lengths.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+110
-43
@@ -7263,45 +7263,112 @@ static ScrStr *scr_digest_encode(const unsigned char *d, size_t n, int enc) {
|
||||
|
||||
/* ── MD5 (RFC 1321) ───────────────────────────────────────────────── */
|
||||
|
||||
static const uint32_t scr_md5_k[64] = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
|
||||
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
|
||||
0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
|
||||
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
|
||||
0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
|
||||
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
|
||||
static const unsigned char scr_md5_r[64] = {
|
||||
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};
|
||||
/* The 64 steps are unrolled with their message word, constant and
|
||||
* rotation inlined. a + m + K does not depend on the previous step, so
|
||||
* each step's critical path is the round function of b, an add, the
|
||||
* rotate and the add of b. G is written as (b & d) + (c & ~d) (the two
|
||||
* terms share no bits) so that its c & ~d half joins that early sum. The
|
||||
* empty asm statement keeps the compiler from moving the constant's add
|
||||
* after the round function, which puts it back on the critical path. */
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__)
|
||||
#define SCR_MD5_PIN(v) __asm__("" : "+r"(v))
|
||||
#else
|
||||
#define SCR_MD5_PIN(v) ((void)0)
|
||||
#endif
|
||||
#define SCR_MD5_F(b, c, d) ((d) ^ ((b) & ((c) ^ (d))))
|
||||
#define SCR_MD5_H(b, c, d) ((b) ^ (c) ^ (d))
|
||||
#define SCR_MD5_I(b, c, d) ((c) ^ ((b) | ~(d)))
|
||||
#define SCR_MD5_STEP(f, a, b, c, d, xk, s) \
|
||||
do { \
|
||||
(a) += (xk); \
|
||||
SCR_MD5_PIN(a); \
|
||||
(a) += f((b), (c), (d)); \
|
||||
(a) = ((a) << (s) | (a) >> (32 - (s))) + (b); \
|
||||
} while (0)
|
||||
#define SCR_MD5_GSTEP(a, b, c, d, xk, s) \
|
||||
do { \
|
||||
(a) += ((c) & ~(d)) + (xk); \
|
||||
SCR_MD5_PIN(a); \
|
||||
(a) += (b) & (d); \
|
||||
(a) = ((a) << (s) | (a) >> (32 - (s))) + (b); \
|
||||
} while (0)
|
||||
|
||||
SCR_DIGEST_ALIGNED static void scr_md5_block(uint32_t h[4], const unsigned char *p) {
|
||||
uint32_t m[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
m[i] = (uint32_t)p[i * 4] | ((uint32_t)p[i * 4 + 1] << 8) |
|
||||
((uint32_t)p[i * 4 + 2] << 16) | ((uint32_t)p[i * 4 + 3] << 24);
|
||||
}
|
||||
SCR_DIGEST_ALIGNED static void scr_md5_blocks(uint32_t h[4], const unsigned char *p, size_t blocks) {
|
||||
uint32_t a = h[0], b = h[1], c = h[2], d = h[3];
|
||||
for (int i = 0; i < 64; i++) {
|
||||
uint32_t f, g;
|
||||
if (i < 16) { f = (b & c) | (~b & d); g = (uint32_t)i; }
|
||||
else if (i < 32) { f = (d & b) | (~d & c); g = (5u * i + 1) & 15; }
|
||||
else if (i < 48) { f = b ^ c ^ d; g = (3u * i + 5) & 15; }
|
||||
else { f = c ^ (b | ~d); g = (7u * i) & 15; }
|
||||
uint32_t t = d;
|
||||
d = c;
|
||||
c = b;
|
||||
uint32_t x = a + f + scr_md5_k[i] + m[g];
|
||||
b = b + ((x << scr_md5_r[i]) | (x >> (32 - scr_md5_r[i])));
|
||||
a = t;
|
||||
for (; blocks != 0; blocks--, p += 64) {
|
||||
uint32_t m[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
m[i] = (uint32_t)p[i * 4] | ((uint32_t)p[i * 4 + 1] << 8) |
|
||||
((uint32_t)p[i * 4 + 2] << 16) | ((uint32_t)p[i * 4 + 3] << 24);
|
||||
}
|
||||
uint32_t a0 = a, b0 = b, c0 = c, d0 = d;
|
||||
SCR_MD5_STEP(SCR_MD5_F, a, b, c, d, m[0] + 0xd76aa478u, 7);
|
||||
SCR_MD5_STEP(SCR_MD5_F, d, a, b, c, m[1] + 0xe8c7b756u, 12);
|
||||
SCR_MD5_STEP(SCR_MD5_F, c, d, a, b, m[2] + 0x242070dbu, 17);
|
||||
SCR_MD5_STEP(SCR_MD5_F, b, c, d, a, m[3] + 0xc1bdceeeu, 22);
|
||||
SCR_MD5_STEP(SCR_MD5_F, a, b, c, d, m[4] + 0xf57c0fafu, 7);
|
||||
SCR_MD5_STEP(SCR_MD5_F, d, a, b, c, m[5] + 0x4787c62au, 12);
|
||||
SCR_MD5_STEP(SCR_MD5_F, c, d, a, b, m[6] + 0xa8304613u, 17);
|
||||
SCR_MD5_STEP(SCR_MD5_F, b, c, d, a, m[7] + 0xfd469501u, 22);
|
||||
SCR_MD5_STEP(SCR_MD5_F, a, b, c, d, m[8] + 0x698098d8u, 7);
|
||||
SCR_MD5_STEP(SCR_MD5_F, d, a, b, c, m[9] + 0x8b44f7afu, 12);
|
||||
SCR_MD5_STEP(SCR_MD5_F, c, d, a, b, m[10] + 0xffff5bb1u, 17);
|
||||
SCR_MD5_STEP(SCR_MD5_F, b, c, d, a, m[11] + 0x895cd7beu, 22);
|
||||
SCR_MD5_STEP(SCR_MD5_F, a, b, c, d, m[12] + 0x6b901122u, 7);
|
||||
SCR_MD5_STEP(SCR_MD5_F, d, a, b, c, m[13] + 0xfd987193u, 12);
|
||||
SCR_MD5_STEP(SCR_MD5_F, c, d, a, b, m[14] + 0xa679438eu, 17);
|
||||
SCR_MD5_STEP(SCR_MD5_F, b, c, d, a, m[15] + 0x49b40821u, 22);
|
||||
SCR_MD5_GSTEP(a, b, c, d, m[1] + 0xf61e2562u, 5);
|
||||
SCR_MD5_GSTEP(d, a, b, c, m[6] + 0xc040b340u, 9);
|
||||
SCR_MD5_GSTEP(c, d, a, b, m[11] + 0x265e5a51u, 14);
|
||||
SCR_MD5_GSTEP(b, c, d, a, m[0] + 0xe9b6c7aau, 20);
|
||||
SCR_MD5_GSTEP(a, b, c, d, m[5] + 0xd62f105du, 5);
|
||||
SCR_MD5_GSTEP(d, a, b, c, m[10] + 0x02441453u, 9);
|
||||
SCR_MD5_GSTEP(c, d, a, b, m[15] + 0xd8a1e681u, 14);
|
||||
SCR_MD5_GSTEP(b, c, d, a, m[4] + 0xe7d3fbc8u, 20);
|
||||
SCR_MD5_GSTEP(a, b, c, d, m[9] + 0x21e1cde6u, 5);
|
||||
SCR_MD5_GSTEP(d, a, b, c, m[14] + 0xc33707d6u, 9);
|
||||
SCR_MD5_GSTEP(c, d, a, b, m[3] + 0xf4d50d87u, 14);
|
||||
SCR_MD5_GSTEP(b, c, d, a, m[8] + 0x455a14edu, 20);
|
||||
SCR_MD5_GSTEP(a, b, c, d, m[13] + 0xa9e3e905u, 5);
|
||||
SCR_MD5_GSTEP(d, a, b, c, m[2] + 0xfcefa3f8u, 9);
|
||||
SCR_MD5_GSTEP(c, d, a, b, m[7] + 0x676f02d9u, 14);
|
||||
SCR_MD5_GSTEP(b, c, d, a, m[12] + 0x8d2a4c8au, 20);
|
||||
SCR_MD5_STEP(SCR_MD5_H, a, b, c, d, m[5] + 0xfffa3942u, 4);
|
||||
SCR_MD5_STEP(SCR_MD5_H, d, a, b, c, m[8] + 0x8771f681u, 11);
|
||||
SCR_MD5_STEP(SCR_MD5_H, c, d, a, b, m[11] + 0x6d9d6122u, 16);
|
||||
SCR_MD5_STEP(SCR_MD5_H, b, c, d, a, m[14] + 0xfde5380cu, 23);
|
||||
SCR_MD5_STEP(SCR_MD5_H, a, b, c, d, m[1] + 0xa4beea44u, 4);
|
||||
SCR_MD5_STEP(SCR_MD5_H, d, a, b, c, m[4] + 0x4bdecfa9u, 11);
|
||||
SCR_MD5_STEP(SCR_MD5_H, c, d, a, b, m[7] + 0xf6bb4b60u, 16);
|
||||
SCR_MD5_STEP(SCR_MD5_H, b, c, d, a, m[10] + 0xbebfbc70u, 23);
|
||||
SCR_MD5_STEP(SCR_MD5_H, a, b, c, d, m[13] + 0x289b7ec6u, 4);
|
||||
SCR_MD5_STEP(SCR_MD5_H, d, a, b, c, m[0] + 0xeaa127fau, 11);
|
||||
SCR_MD5_STEP(SCR_MD5_H, c, d, a, b, m[3] + 0xd4ef3085u, 16);
|
||||
SCR_MD5_STEP(SCR_MD5_H, b, c, d, a, m[6] + 0x04881d05u, 23);
|
||||
SCR_MD5_STEP(SCR_MD5_H, a, b, c, d, m[9] + 0xd9d4d039u, 4);
|
||||
SCR_MD5_STEP(SCR_MD5_H, d, a, b, c, m[12] + 0xe6db99e5u, 11);
|
||||
SCR_MD5_STEP(SCR_MD5_H, c, d, a, b, m[15] + 0x1fa27cf8u, 16);
|
||||
SCR_MD5_STEP(SCR_MD5_H, b, c, d, a, m[2] + 0xc4ac5665u, 23);
|
||||
SCR_MD5_STEP(SCR_MD5_I, a, b, c, d, m[0] + 0xf4292244u, 6);
|
||||
SCR_MD5_STEP(SCR_MD5_I, d, a, b, c, m[7] + 0x432aff97u, 10);
|
||||
SCR_MD5_STEP(SCR_MD5_I, c, d, a, b, m[14] + 0xab9423a7u, 15);
|
||||
SCR_MD5_STEP(SCR_MD5_I, b, c, d, a, m[5] + 0xfc93a039u, 21);
|
||||
SCR_MD5_STEP(SCR_MD5_I, a, b, c, d, m[12] + 0x655b59c3u, 6);
|
||||
SCR_MD5_STEP(SCR_MD5_I, d, a, b, c, m[3] + 0x8f0ccc92u, 10);
|
||||
SCR_MD5_STEP(SCR_MD5_I, c, d, a, b, m[10] + 0xffeff47du, 15);
|
||||
SCR_MD5_STEP(SCR_MD5_I, b, c, d, a, m[1] + 0x85845dd1u, 21);
|
||||
SCR_MD5_STEP(SCR_MD5_I, a, b, c, d, m[8] + 0x6fa87e4fu, 6);
|
||||
SCR_MD5_STEP(SCR_MD5_I, d, a, b, c, m[15] + 0xfe2ce6e0u, 10);
|
||||
SCR_MD5_STEP(SCR_MD5_I, c, d, a, b, m[6] + 0xa3014314u, 15);
|
||||
SCR_MD5_STEP(SCR_MD5_I, b, c, d, a, m[13] + 0x4e0811a1u, 21);
|
||||
SCR_MD5_STEP(SCR_MD5_I, a, b, c, d, m[4] + 0xf7537e82u, 6);
|
||||
SCR_MD5_STEP(SCR_MD5_I, d, a, b, c, m[11] + 0xbd3af235u, 10);
|
||||
SCR_MD5_STEP(SCR_MD5_I, c, d, a, b, m[2] + 0x2ad7d2bbu, 15);
|
||||
SCR_MD5_STEP(SCR_MD5_I, b, c, d, a, m[9] + 0xeb86d391u, 21);
|
||||
a += a0; b += b0; c += c0; d += d0;
|
||||
}
|
||||
h[0] += a; h[1] += b; h[2] += c; h[3] += d;
|
||||
h[0] = a; h[1] = b; h[2] = c; h[3] = d;
|
||||
}
|
||||
|
||||
typedef struct ScrMd5Ctx {
|
||||
@@ -7329,14 +7396,15 @@ static void scr_md5_update(ScrMd5Ctx *ctx, const unsigned char *data, size_t len
|
||||
data += take;
|
||||
len -= take;
|
||||
if (ctx->tail_len == 64) {
|
||||
scr_md5_block(ctx->h, ctx->tail);
|
||||
scr_md5_blocks(ctx->h, ctx->tail, 1);
|
||||
ctx->tail_len = 0;
|
||||
}
|
||||
}
|
||||
while (len >= 64) {
|
||||
scr_md5_block(ctx->h, data);
|
||||
data += 64;
|
||||
len -= 64;
|
||||
if (len >= 64) {
|
||||
size_t blocks = len / 64;
|
||||
scr_md5_blocks(ctx->h, data, blocks);
|
||||
data += blocks * 64;
|
||||
len -= blocks * 64;
|
||||
}
|
||||
if (len != 0) {
|
||||
memcpy(ctx->tail, data, len);
|
||||
@@ -7354,8 +7422,7 @@ static size_t scr_md5_final(const ScrMd5Ctx *source, unsigned char out[32]) {
|
||||
memset(tail + rem + 1, 0, pad - rem - 1 - 8);
|
||||
uint64_t bits = ctx.bytes * 8;
|
||||
for (int b = 0; b < 8; b++) tail[pad - 8 + b] = (unsigned char)(bits >> (8 * b));
|
||||
scr_md5_block(ctx.h, tail);
|
||||
if (pad == 128) scr_md5_block(ctx.h, tail + 64);
|
||||
scr_md5_blocks(ctx.h, tail, pad / 64);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int b = 0; b < 4; b++) out[j * 4 + b] = (unsigned char)(ctx.h[j] >> (8 * b));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// MD5 and md5-sha1 over every length from 0 to 520 bytes, fed whole and
|
||||
// in 1-, 7-, 64- and 100-byte updates, then over 1 MiB, and HMAC-MD5
|
||||
// over a few lengths. Each length's digests are folded into one running
|
||||
// SHA-512 so the output stays short.
|
||||
import { createHash, createHmac } from "node:crypto";
|
||||
|
||||
const data = Buffer.alloc(1 << 20);
|
||||
let x = 0x9e3779b9;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
x ^= x << 13;
|
||||
x ^= x >>> 17;
|
||||
x ^= x << 5;
|
||||
data[i] = x & 0xff;
|
||||
}
|
||||
|
||||
function digest(algorithm: string, input: Buffer, chunk: number): Buffer {
|
||||
const hash = createHash(algorithm);
|
||||
if (chunk === 0) hash.update(input);
|
||||
else for (let at = 0; at < input.length; at += chunk) hash.update(input.subarray(at, at + chunk));
|
||||
return hash.digest();
|
||||
}
|
||||
|
||||
for (const algorithm of ["md5", "md5-sha1"]) {
|
||||
for (const chunk of [0, 1, 7, 64, 100]) {
|
||||
const fold = createHash("sha512");
|
||||
for (let length = 0; length <= 520; length++) fold.update(digest(algorithm, data.subarray(0, length), chunk));
|
||||
console.log(algorithm, "chunk", chunk, fold.digest("hex").slice(0, 32));
|
||||
}
|
||||
console.log(algorithm, "1 MiB", digest(algorithm, data, 0).toString("hex").slice(0, 32), digest(algorithm, data, 4096).toString("hex").slice(0, 32));
|
||||
}
|
||||
console.log("abc", createHash("md5").update("abc").digest("hex"));
|
||||
for (const length of [0, 1, 55, 56, 63, 64, 65, 119, 120, 1000]) {
|
||||
console.log("hmac", length, createHmac("md5", "key").update(data.subarray(0, length)).digest("hex").slice(0, 32));
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"plain":2850,"san":3660}
|
||||
Reference in New Issue
Block a user