Created
July 1, 2024 02:23
-
-
Save antimatter15/5159e781ae1318ddd0e26bf7dba469df to your computer and use it in GitHub Desktop.
Slow BLAKE-512 Implementation using BigUint64Array
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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