Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions assembly/hash/md4.asm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
** ********************************************************************
** md4.c -- Implementation of MD4 Message Digest Algorithm **
** Updated: 2/16/90 by Ronald L. Rivest **
** (C) 1990 RSA Data Security, Inc. **
** ********************************************************************
*/

// Ported to assemblyscript by Tobias Koppers

let totalLength: u32;
let A: u32;
let B: u32;
let C: u32;
let D: u32;

function F(x: u32, y: u32, z: u32): u32 {
return z ^ (x & (y ^ z));
}
function G(x: u32, y: u32, z: u32): u32 {
return (x & (y | z)) | (y & z);
}
function H(x: u32, y: u32, z: u32): u32 {
return x ^ y ^ z;
}

function roundF(a: u32, b: u32, c: u32, d: u32, i: u32, s: u32): u32 {
return rotl<u32>(a + F(b, c, d) + load<u32>(i), s);
}
function roundG(a: u32, b: u32, c: u32, d: u32, i: u32, s: u32): u32 {
return rotl<u32>(a + G(b, c, d) + load<u32>(i) + 0x5a827999, s);
}
function roundH(a: u32, b: u32, c: u32, d: u32, i: u32, s: u32): u32 {
return rotl<u32>(a + H(b, c, d) + load<u32>(i) + 0x6ed9eba1, s);
}

export function init(): void {
A = 0x67452301;
B = 0xefcdab89;
C = 0x98badcfe;
D = 0x10325476;
totalLength = 0;
}

function body(size: u32): void {
let _A = A;
Copy link
Member

@vankop vankop Oct 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just interesting what is a reason of this variables? (you can use A,B,C,D) same for a,b,c,d
using in current block reduce module size?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A is a global and _A a local var. I tried to avoid accessing globals too often

let _B = B;
let _C = C;
let _D = D;

for (let i: u32 = 0; i < size; i += 64) {
let a = _A;
let b = _B;
let c = _C;
let d = _D;

// Round F

a = roundF(a, b, c, d, i + 4 * 0, 3);
d = roundF(d, a, b, c, i + 4 * 1, 7);
c = roundF(c, d, a, b, i + 4 * 2, 11);
b = roundF(b, c, d, a, i + 4 * 3, 19);

a = roundF(a, b, c, d, i + 4 * 4, 3);
d = roundF(d, a, b, c, i + 4 * 5, 7);
c = roundF(c, d, a, b, i + 4 * 6, 11);
b = roundF(b, c, d, a, i + 4 * 7, 19);

a = roundF(a, b, c, d, i + 4 * 8, 3);
d = roundF(d, a, b, c, i + 4 * 9, 7);
c = roundF(c, d, a, b, i + 4 * 10, 11);
b = roundF(b, c, d, a, i + 4 * 11, 19);

a = roundF(a, b, c, d, i + 4 * 12, 3);
d = roundF(d, a, b, c, i + 4 * 13, 7);
c = roundF(c, d, a, b, i + 4 * 14, 11);
b = roundF(b, c, d, a, i + 4 * 15, 19);

// Round G

a = roundG(a, b, c, d, i + 4 * 0, 3);
d = roundG(d, a, b, c, i + 4 * 4, 5);
c = roundG(c, d, a, b, i + 4 * 8, 9);
b = roundG(b, c, d, a, i + 4 * 12, 13);

a = roundG(a, b, c, d, i + 4 * 1, 3);
d = roundG(d, a, b, c, i + 4 * 5, 5);
c = roundG(c, d, a, b, i + 4 * 9, 9);
b = roundG(b, c, d, a, i + 4 * 13, 13);

a = roundG(a, b, c, d, i + 4 * 2, 3);
d = roundG(d, a, b, c, i + 4 * 6, 5);
c = roundG(c, d, a, b, i + 4 * 10, 9);
b = roundG(b, c, d, a, i + 4 * 14, 13);

a = roundG(a, b, c, d, i + 4 * 3, 3);
d = roundG(d, a, b, c, i + 4 * 7, 5);
c = roundG(c, d, a, b, i + 4 * 11, 9);
b = roundG(b, c, d, a, i + 4 * 15, 13);

// Round H

a = roundH(a, b, c, d, i + 4 * 0, 3);
d = roundH(d, a, b, c, i + 4 * 8, 9);
c = roundH(c, d, a, b, i + 4 * 4, 11);
b = roundH(b, c, d, a, i + 4 * 12, 15);

a = roundH(a, b, c, d, i + 4 * 2, 3);
d = roundH(d, a, b, c, i + 4 * 10, 9);
c = roundH(c, d, a, b, i + 4 * 6, 11);
b = roundH(b, c, d, a, i + 4 * 14, 15);

a = roundH(a, b, c, d, i + 4 * 1, 3);
d = roundH(d, a, b, c, i + 4 * 9, 9);
c = roundH(c, d, a, b, i + 4 * 5, 11);
b = roundH(b, c, d, a, i + 4 * 13, 15);

a = roundH(a, b, c, d, i + 4 * 3, 3);
d = roundH(d, a, b, c, i + 4 * 11, 9);
c = roundH(c, d, a, b, i + 4 * 7, 11);
b = roundH(b, c, d, a, i + 4 * 15, 15);

_A += a;
_B += b;
_C += c;
_D += d;
}

A = _A;
B = _B;
C = _C;
D = _D;
}

export function update(length: u32): void {
body(length);
totalLength += length;
}

export function final(length: u32): void {
const bits: u64 = u64(totalLength + length) << 3;
const finalLength: u32 = (length + 9 + 63) & ~63;
const bitsPosition = finalLength - 8;

// end
store<u8>(length++, 0x80);

// padding
for (; length & 7 && length < finalLength; length++) store<u8>(length, 0);
for (; length < finalLength; length += 8) store<u64>(length, 0);

// bits
store<u64>(bitsPosition, bits);

body(finalLength);

store<u64>(0, u32ToHex(A));
store<u64>(8, u32ToHex(B));
store<u64>(16, u32ToHex(C));
store<u64>(24, u32ToHex(D));
}

function u32ToHex(x: u64): u64 {
// from https://johnnylee-sde.github.io/Fast-unsigned-integer-to-hex-string/

x = ((x & 0xffff0000) << 16) | (x & 0xffff);
x = ((x & 0x0000ff000000ff00) << 8) | (x & 0x000000ff000000ff);
x = ((x & 0x00f000f000f000f0) >> 4) | ((x & 0x000f000f000f000f) << 8);

const mask = ((x + 0x0606060606060606) >> 4) & 0x0101010101010101;

x |= 0x3030303030303030;

x += 0x27 * mask;

return x;
}
2 changes: 0 additions & 2 deletions assembly/hash/xxhash64.asm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ export function final(length: u32): void {
result *= Prime3;
result ^= result >> 32;

store<u64>(0, result);

store<u64>(0, u32ToHex(result >> 32));
store<u64>(8, u32ToHex(result & 0xffffffff));
}
Expand Down
39 changes: 39 additions & 0 deletions benchmark/md4-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const createHash = require("../lib/util/createHash");

const compare = require("./micro-compare");

const size = 50;

const strings = [];
for (let count = 1; ; count *= 10) {
while (strings.length < count) {
const s = require("crypto").randomBytes(size).toString("hex");
strings.push(s);
const hash = createHash("native-md4");
hash.update(s);
hash.update(s);
hash.digest("hex");
}
let i = 0;
console.log(
`${count} different 200 char strings: ` +
compare(
"native md4",
() => {
const hash = createHash("native-md4");
const s = strings[(i = (i + 1) % strings.length)];
hash.update(s);
hash.update(s);
return hash.digest("hex");
},
"wasm md4",
() => {
const hash = createHash("md4");
const s = strings[(i = (i + 1) % strings.length)];
hash.update(s);
hash.update(s);
return hash.digest("hex");
}
)
);
}
49 changes: 49 additions & 0 deletions benchmark/md4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
const createHash = require("../lib/util/createHash");

const compare = require("./micro-compare");

for (const size of [
1, 10, 20, 40, 60, 80, 100, 200, 400, 1000, 1001, 5000, 8183, 8184, 8185,
10000, 20000, 32768, 32769, 50000, 100000, 200000
]) {
const longString = require("crypto").randomBytes(size).toString("hex");
const buffer = require("crypto").randomBytes(size * 2);
console.log(
`string ${longString.length} chars: ` +
compare(
"native md4",
() => {
const hash = createHash("native-md4");
hash.update(longString);
hash.update(longString);
return hash.digest("hex");
},
"wasm md4",
() => {
const hash = createHash("md4");
hash.update(longString);
hash.update(longString);
return hash.digest("hex");
}
)
);
console.log(
`buffer ${buffer.length} bytes: ` +
compare(
"native md4",
() => {
const hash = createHash("native-md4");
hash.update(buffer);
hash.update(buffer);
return hash.digest("hex");
},
"wasm md4",
() => {
const hash = createHash("md4");
hash.update(buffer);
hash.update(buffer);
return hash.digest("hex");
}
)
);
}
44 changes: 44 additions & 0 deletions benchmark/micro-compare.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
let result;

const measure = (fn, count) => {
const start = process.hrtime.bigint();
for (let i = 0; i < count; i++) result = fn();
return Number(process.hrtime.bigint() - start);
};

const NS_PER_MS = 1000000; // 1ms
const MIN_DURATION = 100 * NS_PER_MS; // 100ms
const MAX_DURATION = 1000 * NS_PER_MS; // 1000ms
const MAX_WARMUP_DURATION = 1 * NS_PER_MS; // 1ms

const format = (fast, slow, fastName, slowName, count) => {
return `${fastName} is ${
Math.round(((slow - fast) * 1000) / slow) / 10
}% faster than ${slowName} (${Math.round(fast / 100 / count) / 10} µs vs ${
Math.round(slow / 100 / count) / 10
} µs, ${count}x)`;
};

const compare = (n1, f1, n2, f2) => {
let count = 1;
while (true) {
const timings = [f1, f2, f1, f2, f1, f2].map(f => measure(f, count));
const t1 = Math.min(timings[0], timings[2], timings[4]);
const t2 = Math.min(timings[1], timings[3], timings[5]);
if (count === 1 && (t1 > MAX_WARMUP_DURATION || t2 > MAX_WARMUP_DURATION)) {
continue;
}
if (
(t1 > MIN_DURATION && t2 > MIN_DURATION) ||
t1 > MAX_DURATION ||
t2 > MAX_DURATION
) {
return t1 > t2
? format(t2, t1, n2, n1, count)
: format(t1, t2, n1, n2, count);
}
count *= 2;
}
};

module.exports = compare;
45 changes: 45 additions & 0 deletions benchmark/xxhash64-vs-md4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const createHash = require("../lib/util/createHash");

const compare = require("./micro-compare");

for (const size of [
1, 10, 20, 40, 60, 80, 100, 200, 400, 1000, 1001, 5000, 8183, 8184, 8185,
10000, 20000, 32768, 32769, 50000, 100000, 200000
]) {
const longString = require("crypto").randomBytes(size).toString("hex");
const buffer = require("crypto").randomBytes(size * 2);
console.log(
`string ${longString.length} chars: ` +
compare(
"wasm xxhash64",
() => {
const hash = createHash("xxhash64");
hash.update(longString);
return hash.digest("hex");
},
"wasm md4",
() => {
const hash = createHash("md4");
hash.update(longString);
return hash.digest("hex");
}
)
);
console.log(
`buffer ${buffer.length} bytes: ` +
compare(
"wasm xxhash64",
() => {
const hash = createHash("xxhash64");
hash.update(buffer);
return hash.digest("hex");
},
"wasm md4",
() => {
const hash = createHash("md4");
hash.update(buffer);
return hash.digest("hex");
}
)
);
}
Loading