Skip to content

Instantly share code, notes, and snippets.

@antimatter15
Created July 1, 2024 02:23
Show Gist options
  • Save antimatter15/5159e781ae1318ddd0e26bf7dba469df to your computer and use it in GitHub Desktop.
Save antimatter15/5159e781ae1318ddd0e26bf7dba469df to your computer and use it in GitHub Desktop.
Slow BLAKE-512 Implementation using BigUint64Array
// BLAKE512 JavaScript Implementation
// Uint64Blake x 4,512 ops/sec ±1.20% (98 runs sampled)
// RegularBlake x 534,660 ops/sec ±1.31% (95 runs sampled)
// This one is 100x slower than the blake-hash implementation.
const sigma = [
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
[12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
[13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
[6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
[10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
[11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
[7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
[9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
[2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]
];
const u512 = new BigUint64Array([
0x243f6a8885a308d3n, 0x13198a2e03707344n,
0xa4093822299f31d0n, 0x082efa98ec4e6c89n,
0x452821e638d01377n, 0xbe5466cf34e90c6cn,
0xc0ac29b7c97c50ddn, 0x3f84d5b5b5470917n,
0x9216d5d98979fb1bn, 0xd1310ba698dfb5acn,
0x2ffd72dbd01adfb7n, 0xb8e1afed6a267e96n,
0xba7c9045f12c7f99n, 0x24a19947b3916cf7n,
0x0801f2e2858efc16n, 0x636920d871574e69n
]);
const padding = new Uint8Array(129);
padding[0] = 0x80;
function rotr64(x, n) {
return ((x >> n) | (x << (64n - n))) & 0xffffffffffffffffn;
}
function G(i, m, v, a, b, c, d, e) {
v[a] = (v[a] + (m[sigma[i][e]] ^ u512[sigma[i][e + 1]]) + v[b]) & 0xffffffffffffffffn;
v[d] = rotr64(v[d] ^ v[a], 32n);
v[c] = (v[c] + v[d]) & 0xffffffffffffffffn;
v[b] = rotr64(v[b] ^ v[c], 25n);
v[a] = (v[a] + (m[sigma[i][e + 1]] ^ u512[sigma[i][e]]) + v[b]) & 0xffffffffffffffffn;
v[d] = rotr64(v[d] ^ v[a], 16n);
v[c] = (v[c] + v[d]) & 0xffffffffffffffffn;
v[b] = rotr64(v[b] ^ v[c], 11n);
}
class State512 {
constructor() {
this.h = new BigUint64Array([
0x6a09e667f3bcc908n, 0xbb67ae8584caa73bn,
0x3c6ef372fe94f82bn, 0xa54ff53a5f1d36f1n,
0x510e527fade682d1n, 0x9b05688c2b3e6c1fn,
0x1f83d9abfb41bd6bn, 0x5be0cd19137e2179n
]);
this.s = new BigUint64Array(4);
this.t = new BigUint64Array(2);
this.buflen = 0;
this.nullt = false;
this.buf = new Uint8Array(128);
}
compress(block) {
const v = new BigUint64Array(16);
const m = new BigUint64Array(16);
const dataView = new DataView(block.buffer, block.byteOffset, block.byteLength);
for (let i = 0; i < 8; i++) v[i] = this.h[i];
v.set(this.s.map((s, i) => s ^ u512[i]), 8);
v.set(u512.slice(4, 8), 12);
for (let i = 0; i < 16; i++) m[i] = dataView.getBigUint64(i * 8, false); // false for big-endian
if (!this.nullt) {
v[12] ^= this.t[0];
v[13] ^= this.t[0];
v[14] ^= this.t[1];
v[15] ^= this.t[1];
}
for (let i = 0; i < 16; i++) {
G(i, m, v, 0, 4, 8, 12, 0);
G(i, m, v, 1, 5, 9, 13, 2);
G(i, m, v, 2, 6, 10, 14, 4);
G(i, m, v, 3, 7, 11, 15, 6);
G(i, m, v, 0, 5, 10, 15, 8);
G(i, m, v, 1, 6, 11, 12, 10);
G(i, m, v, 2, 7, 8, 13, 12);
G(i, m, v, 3, 4, 9, 14, 14);
}
for (let i = 0; i < 16; i++) this.h[i % 8] ^= v[i];
for (let i = 0; i < 8; i++) this.h[i] ^= this.s[i % 4];
}
update(data) {
let left = this.buflen, fill = 128 - left;
if (left && data.length >= fill) {
this.buf.set(data.subarray(0, fill), left);
this.t[0] += BigInt(1024);
if (this.t[0] === 0n) this.t[1]++;
this.compress(this.buf);
data = data.subarray(fill);
left = 0;
}
while (data.length >= 128) {
this.t[0] += BigInt(1024);
if (this.t[0] === 0n) this.t[1]++;
this.compress(data.subarray(0, 128));
data = data.subarray(128);
}
if (data.length > 0) {
this.buf.set(data, left);
this.buflen = left + data.length;
} else this.buflen = 0;
}
final() {
let lo = this.t[0] + BigInt(this.buflen * 8);
let hi = this.t[1];
if (lo < BigInt(this.buflen * 8)) hi++;
const msglen = new Uint8Array(16);
for (let i = 0; i < 8; i++) {
msglen[i] = Number((hi >> BigInt(56 - i * 8)) & 0xffn);
msglen[i + 8] = Number((lo >> BigInt(56 - i * 8)) & 0xffn);
}
if (this.buflen === 111) {
this.t[0] -= 8n;
this.update(new Uint8Array([0x81]));
} else {
if (this.buflen < 111) {
if (this.buflen === 0) this.nullt = true;
this.t[0] -= BigInt(888 - this.buflen * 8);
this.update(padding.subarray(0, 111 - this.buflen));
} else {
this.t[0] -= BigInt(1024 - this.buflen * 8);
this.update(padding.subarray(0, 128 - this.buflen));
this.t[0] -= 888n;
this.update(padding.subarray(1, 112));
this.nullt = true;
}
this.update(new Uint8Array([0x01]));
this.t[0] -= 8n;
}
this.t[0] -= 128n;
this.update(msglen);
const out = new Uint8Array(64);
const dataView = new DataView(out.buffer);
for (let i = 0; i < 8; i++) dataView.setBigUint64(i * 8, this.h[i], false); // false for big-endian
return out;
}
}
function blake512(message) {
const S = new State512();
S.update(message);
return S.final();
}
// Test
const testInput = new Uint8Array(1);
const result = blake512(testInput);
const hexResult = Array.from(result).map(b => b.toString(16).padStart(2, '0')).join('');
console.assert(hexResult === '97961587f6d970faba6d2478045de6d1fabd09b61ae50932054d52bc29d31be4ff9102b9f69e2bbdb83be13d4b9c06091e5fa0b48bd081b634058be0ec49beb3', 'Test failed');
console.log("Test passed");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment