// m: message buffer, represents current state of `b` during compression\r
// t: total byte counter, incremented by `c` (BLAKE2b supports 2¹²⁸-1)\r
// v: state vector, set to bytes of `h` and `iv` then modified by `t` during compression\r
- b: Uint8Array\r
- c: u8\r
- h: Uint64Array\r
- m: Uint64Array\r
- t: v128\r
- v: Uint64Array\r
+ b: Uint8Array = new Uint8Array(128)\r
+ c: u8 = 0\r
+ h: Uint64Array = new Uint64Array(8)\r
+ m: StaticArray<u64> = new StaticArray<u64>(16)\r
+ t: v128 = v128.splat<u64>(0)\r
+ v: StaticArray<u64> = new StaticArray<u64>(16)\r
\r
// param_block: set according to BLAKE2 layout from constructor arguments\r
param_block: Uint8Array = new Uint8Array(64)\r
param_view: DataView = new DataView(this.param_block.buffer)\r
\r
- // length: bytes of hash output (1-64)\r
- constructor (length: u8) {\r
+ // input: byte-level view of refillable input buffer\r
+ input: DataView = new DataView(this.b.buffer)\r
\r
+ // output: byte-level view of 64-bit chain buffer\r
+ output: Uint8Array = Uint8Array.wrap(this.h.buffer)\r
+\r
+ init (): Blake2b {\r
// initialize buffers and counters\r
- this.b = new Uint8Array(128)\r
+ this.b.fill(0)\r
this.c = 0\r
- this.h = new Uint64Array(8)\r
- this.m = new Uint64Array(16)\r
+ this.m.fill(0)\r
this.t = v128.splat<u64>(0)\r
- this.v = new Uint64Array(16)\r
+ this.v.fill(0)\r
\r
// initialize parameter block\r
- this.param_block = new Uint8Array(64)\r
- this.param_view = new DataView(this.param_block.buffer)\r
- this.param_block[0] = length\r
+ this.param_block.fill(0)\r
+ this.param_block[0] = 64 // always 64 bytes for this implementation\r
this.param_block[1] = 0 // no key\r
this.param_block[2] = 1 // fanout\r
this.param_block[3] = 1 // depth\r
for (let i = 0; i < 8; i++) {\r
this.h[i] = blake2b_iv[i] ^ this.param_view.getUint64(i << 3, true)\r
}\r
+ return this\r
}\r
\r
// input: variable-length message data passed by user to be hashed\r
- update (input: Uint8Array): Blake2b {\r
- for (let i = 0; i < input.byteLength; i++) {\r
+ update (input: StaticArray<u8>): Blake2b {\r
+ for (let i = 0; i < input.length; i++) {\r
\r
// is buffer full?\r
if (this.c === this.b.byteLength) {\r
\r
// increment total byte counter\r
- this.t = unchecked(v128.add<u64>(this.t, i64x2(this.b.byteLength, 0)))\r
+ this.t = v128.add<u64>(this.t, i64x2(this.b.byteLength, 0))\r
\r
// reset buffer counter to zero\r
this.c = 0\r
return this\r
}\r
\r
- digest (): Uint8Array {\r
+ digest (output: StaticArray<u8>): void {\r
\r
// add final message block size to total bytes\r
- this.t = unchecked(v128.add<u64>(this.t, i64x2(this.c, 0)))\r
+ this.t = v128.add<u64>(this.t, i64x2(this.c, 0))\r
\r
// pad final block with zeros\r
this.b.fill(0, this.c)\r
// set final block flag and compress\r
this.COMPRESS(true)\r
\r
- // copy bytes\r
- const bytes = Uint8Array.wrap(this.h.buffer)\r
- const hash = new Uint8Array(this.param_block[0])\r
- hash.set(bytes.subarray(0, this.param_block[0]))\r
- return hash\r
+ // return byte array of 64-bit chain buffer\r
+ for (let i = 0; i < 64; i++) {\r
+ output[i] = this.output[i]\r
+ }\r
}\r
\r
// Defined in BLAKE2 section 2.4\r
isFinal ? this.v[14] = ~this.v[14] : this.v[14] = this.v[14]\r
\r
// copy input buffer to message block\r
- const buf = new DataView(this.b.buffer)\r
for (let i = 0; i < 16; i++) {\r
- this.m[i] = buf.getUint64(i << 3, true)\r
+ this.m[i] = this.input.getUint64(i << 3, true)\r
}\r
\r
// twelve rounds of mixing\r
const c = blake2b_state[i][2]\r
const d = blake2b_state[i][3]\r
const s = blake2b_sigma[r]\r
- this.G(a, b, c, d, s[i + i], s[i + i + 1])\r
+ this.G(a, b, c, d, s[i << 1], s[(i << 1) + 1])\r
}\r
}\r
\r
G (a: u8, b: u8, c: u8, d: u8, x: u8, y: u8): void {\r
- this.v[a] = unchecked(unchecked(this.v[a] + this.v[b]) + this.m[x])\r
+ this.v[a] = this.v[a] + this.v[b] + this.m[x]\r
this.v[d] = rotr<u64>(this.v[d] ^ this.v[a], 32)\r
- this.v[c] = unchecked(this.v[c] + this.v[d])\r
+ this.v[c] = this.v[c] + this.v[d]\r
this.v[b] = rotr<u64>(this.v[b] ^ this.v[c], 24)\r
- this.v[a] = unchecked(unchecked(this.v[a] + this.v[b]) + this.m[y])\r
+ this.v[a] = this.v[a] + this.v[b] + this.m[y]\r
this.v[d] = rotr<u64>(this.v[d] ^ this.v[a], 16)\r
- this.v[c] = unchecked(this.v[c] + this.v[d])\r
+ this.v[c] = this.v[c] + this.v[d]\r
this.v[b] = rotr<u64>(this.v[b] ^ this.v[c], 63)\r
}\r
}\r
\r
-const BLOCKHASH_BYTES: u8 = 32\r
-const PRIVATEKEY_BYTES: u8 = 32\r
-const PUBLICKEY_BYTES: u8 = 32\r
-const SIGNATURE_BYTES: u8 = 64\r
-const D: Int64Array = new Int64Array(16); D.set([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])\r
-const D2: Int64Array = new Int64Array(16); D2.set([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])\r
-const X: Int64Array = new Int64Array(16); X.set([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169])\r
-const Y: Int64Array = new Int64Array(16); Y.set([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666])\r
-const I: Int64Array = new Int64Array(16); I.set([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])\r
-const XY: Int64Array = new Int64Array(16); XY.set([0xdd90, 0xa5b7, 0x8ab3, 0x6dde, 0x52f5, 0x7751, 0x9f80, 0x20f0, 0xe37d, 0x64ab, 0x4e8e, 0x66ea, 0x7665, 0xd78b, 0x5f0f, 0xe787])\r
-\r
-function vn (x: Uint8Array, xi: u8, y: Uint8Array, yi: u8, n: i32): i64 {\r
- let d = 0\r
- for (let i = 0; i < n; i++) {\r
- d |= x[xi + i] ^ y[yi + i]\r
+const BLOCKHASH_BYTES: i32 = 32\r
+const PRIVATEKEY_BYTES: i32 = 32\r
+const PUBLICKEY_BYTES: i32 = 32\r
+const SECRETKEY_BYTES: i32 = PRIVATEKEY_BYTES + PUBLICKEY_BYTES\r
+const SIGNATURE_BYTES: i32 = 64\r
+const SIGNEDBLOCKHASH_BYTES: i32 = SIGNATURE_BYTES + BLOCKHASH_BYTES\r
+const D: StaticArray<i32> = StaticArray.fromArray<i32>([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])\r
+const D2: StaticArray<i32> = StaticArray.fromArray<i32>([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])\r
+const X: StaticArray<i32> = StaticArray.fromArray<i32>([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169])\r
+const Y: StaticArray<i32> = StaticArray.fromArray<i32>([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666])\r
+const I: StaticArray<i32> = StaticArray.fromArray<i32>([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])\r
+const XY: StaticArray<i32> = StaticArray.fromArray<i32>([0xdd90, 0xa5b7, 0x8ab3, 0x6dde, 0x52f5, 0x7751, 0x9f80, 0x20f0, 0xe37d, 0x64ab, 0x4e8e, 0x66ea, 0x7665, 0xd78b, 0x5f0f, 0xe787])\r
+\r
+// Static I/O buffers\r
+const INPUT_BUFFER = memory.data(128)\r
+const OUTPUT_BUFFER = memory.data(64)\r
+\r
+function vn (x: StaticArray<u8>, y: StaticArray<u8>): bool {\r
+ let d: u8 = 0\r
+ for (let i = 0; i < 32; i++) {\r
+ d |= x[i] ^ y[i]\r
}\r
- return (1 & ((d - 1) >>> 8)) - 1\r
+ return d !== 0\r
}\r
\r
-function pow2523 (o: Int64Array, i: Int64Array): void {\r
- const c: Int64Array = new Int64Array(16)\r
- c.set(i.subarray(0, 16), 0)\r
- for (let a = 0; a < 249; a++) {\r
+const pow_c: StaticArray<i32> = new StaticArray<i32>(16)\r
+function pow2523 (o: StaticArray<i32>, a: StaticArray<i32>): void {\r
+ const c = pow_c\r
+ for (let i = 0; i < 16; i++) {\r
+ c[i] = a[i]\r
+ }\r
+ for (let i = 0; i < 249; i++) {\r
Square(c, c)\r
- Multiply(c, c, i)\r
+ Multiply(c, c, a)\r
}\r
Square(c, c)\r
Square(c, c)\r
- Multiply(c, c, i)\r
- o.set(c, 0)\r
+ Multiply(c, c, a)\r
+ for (let i = 0; i < 16; i++) {\r
+ o[i] = c[i]\r
+ }\r
}\r
\r
-function car25519 (o: Int64Array): void {\r
- let v: i64 = 0\r
- let c: i64 = 0\r
- const s: i64 = 1 << 16\r
+function car25519 (o: StaticArray<i32>): void {\r
+ let c: i32 = 0\r
for (let i = 0; i < 16; i++) {\r
- v = o[i] + c + s\r
- o[i] = v % s\r
- c = v / s - 1\r
+ c += o[i]\r
+ o[i] = c & 0xFFFF\r
+ c >>= 16\r
}\r
o[0] += 38 * c\r
}\r
\r
-function inv25519 (o: Int64Array, i: Int64Array): void {\r
- const c: Int64Array = new Int64Array(16)\r
- c.set(i.subarray(0, 16), 0)\r
- for (let a = 253; a >= 0; a--) {\r
+const inv_c: StaticArray<i32> = new StaticArray<i32>(16)\r
+function inv25519 (o: StaticArray<i32>, a: StaticArray<i32>): void {\r
+ const c = inv_c\r
+ for (let i = 0; i < 16; i++) {\r
+ c[i] = a[i]\r
+ }\r
+ for (let i = 0; i < 249; i++) {\r
Square(c, c)\r
- if (a !== 2 && a !== 4) {\r
- Multiply(c, c, i)\r
- }\r
+ Multiply(c, c, a)\r
+ }\r
+ Square(c, c)\r
+ Square(c, c)\r
+ Multiply(c, c, a)\r
+ Square(c, c)\r
+ Square(c, c)\r
+ Multiply(c, c, a)\r
+ Square(c, c)\r
+ Multiply(c, c, a)\r
+ for (let i = 0; i < 16; i++) {\r
+ o[i] = c[i]\r
}\r
- o.set(c, 0)\r
}\r
\r
-function neq25519 (a: Int64Array, b: Int64Array): i64 {\r
- const c = new Uint8Array(32)\r
- const d = new Uint8Array(32)\r
+const neq_c = new StaticArray<u8>(32)\r
+const neq_d = new StaticArray<u8>(32)\r
+function neq25519 (a: StaticArray<i32>, b: StaticArray<i32>): bool {\r
+ const c = neq_c\r
+ const d = neq_d\r
pack25519(c, a)\r
pack25519(d, b)\r
- return vn(c, 0, d, 0, 32)\r
+ return vn(c, d)\r
}\r
\r
-function pack25519 (o: Uint8Array, n: Int64Array): void {\r
- let b: i64 = 0\r
- const m: Int64Array = new Int64Array(16)\r
- const t: Int64Array = new Int64Array(16)\r
- t.set(n.subarray(0, 16), 0)\r
+const pack_m: StaticArray<i32> = new StaticArray<i32>(16)\r
+const pack_t: StaticArray<i32> = new StaticArray<i32>(16)\r
+function pack25519 (o: StaticArray<u8>, n: StaticArray<i32>): void {\r
+ const m = pack_m\r
+ const t = pack_t\r
+ let b: i32 = 0\r
+\r
+ for (let i = 0; i < 16; i++) {\r
+ t[i] = n[i]\r
+ }\r
+\r
car25519(t)\r
car25519(t)\r
car25519(t)\r
+\r
for (let j = 0; j < 2; j++) {\r
m[0] = t[0] - 0xffed\r
for (let i = 1; i < 15; i++) {\r
m[14] &= 0xffff\r
sel25519(t, m, 1 - b)\r
}\r
+\r
for (let i = 0; i < 16; i++) {\r
- o[i + i] = u8(t[i] & 0xff)\r
- o[i + i + 1] = u8(t[i] >> 8)\r
+ o[i << 1] = u8(t[i] & 0xff)\r
+ o[(i << 1) + 1] = u8(t[i] >> 8)\r
}\r
}\r
\r
-function par25519 (a: Int64Array): u8 {\r
- const d = new Uint8Array(32)\r
+const par_d = new StaticArray<u8>(32)\r
+function par25519 (a: StaticArray<i32>): u8 {\r
+ const d = par_d\r
pack25519(d, a)\r
return d[0] & 1\r
}\r
\r
-function sel25519 (p: Int64Array, q: Int64Array, b: i64): void {\r
- let t: i64 = 0\r
- const c: i64 = ~(b - 1)\r
+function sel25519 (p: StaticArray<i32>, q: StaticArray<i32>, b: i32): void {\r
+ let t: i32 = 0\r
+ const c: i32 = ~(b - 1)\r
for (let i = 0; i < 16; i++) {\r
t = c & (p[i] ^ q[i])\r
p[i] ^= t\r
}\r
}\r
\r
-function unpack25519 (o: Int64Array, n: Uint8Array): void {\r
+function unpack25519 (o: StaticArray<i32>, n: StaticArray<u8>): void {\r
for (let i = 0; i < 16; i++) {\r
- o[i] = n[i + i] + (n[i + i + 1] << 8)\r
+ o[i] = i32(n[i << 1]) + (i32(n[(i << 1) + 1]) << 8)\r
}\r
o[15] &= (1 << 15) - 1\r
}\r
\r
-function Add (o: Int64Array, a: Int64Array, b: Int64Array): void {\r
- for (let i = 0; i < 16; i++) {\r
- o[i] = a[i] + b[i]\r
- }\r
+@inline\r
+function Add (o: StaticArray<i32>, a: StaticArray<i32>, b: StaticArray<i32>): void {\r
+ o[0] = a[0] + b[0]\r
+ o[1] = a[1] + b[1]\r
+ o[2] = a[2] + b[2]\r
+ o[3] = a[3] + b[3]\r
+ o[4] = a[4] + b[4]\r
+ o[5] = a[5] + b[5]\r
+ o[6] = a[6] + b[6]\r
+ o[7] = a[7] + b[7]\r
+ o[8] = a[8] + b[8]\r
+ o[9] = a[9] + b[9]\r
+ o[10] = a[10] + b[10]\r
+ o[11] = a[11] + b[11]\r
+ o[12] = a[12] + b[12]\r
+ o[13] = a[13] + b[13]\r
+ o[14] = a[14] + b[14]\r
+ o[15] = a[15] + b[15]\r
}\r
\r
-function Subtract (o: Int64Array, a: Int64Array, b: Int64Array): void {\r
- for (let i = 0; i < 16; i++) {\r
- o[i] = a[i] - b[i]\r
- }\r
+@inline\r
+function Subtract (o: StaticArray<i32>, a: StaticArray<i32>, b: StaticArray<i32>): void {\r
+ o[0] = a[0] - b[0]\r
+ o[1] = a[1] - b[1]\r
+ o[2] = a[2] - b[2]\r
+ o[3] = a[3] - b[3]\r
+ o[4] = a[4] - b[4]\r
+ o[5] = a[5] - b[5]\r
+ o[6] = a[6] - b[6]\r
+ o[7] = a[7] - b[7]\r
+ o[8] = a[8] - b[8]\r
+ o[9] = a[9] - b[9]\r
+ o[10] = a[10] - b[10]\r
+ o[11] = a[11] - b[11]\r
+ o[12] = a[12] - b[12]\r
+ o[13] = a[13] - b[13]\r
+ o[14] = a[14] - b[14]\r
+ o[15] = a[15] - b[15]\r
}\r
\r
-function Multiply (o: Int64Array, a: Int64Array, b: Int64Array): void {\r
- let v: i64 = 0\r
- let c: i64 = 0\r
- const s: i64 = 1 << 16\r
- const t = new Int64Array(31)\r
- t.fill(0)\r
+const multiply_t = new StaticArray<i64>(32)\r
+function Multiply (oo: StaticArray<i32>, aa: StaticArray<i32>, bb: StaticArray<i32>): void {\r
+ const a = changetype<usize>(aa)\r
+ const b = changetype<usize>(bb)\r
+ const o = changetype<usize>(oo)\r
+ const t = changetype<usize>(multiply_t.fill(0))\r
+\r
+ const b0 = v128.load(b)\r
+ const b4 = v128.load(b + 16)\r
+ const b8 = v128.load(b + 32)\r
+ const b12 = v128.load(b + 48)\r
\r
- // init t values\r
+ // init values in accumulator `t`\r
for (let i = 0; i < 16; i++) {\r
- for (let j = 0; j < 16; j++) {\r
- t[i + j] += a[i] * b[j]\r
- }\r
+ const ai = v128.splat<i32>(load<i32>(a + (i << 2)))\r
+\r
+ let ptr = t + (usize(i) << 3)\r
+ let pLo = i64x2.extmul_low_i32x4_s(ai, b0)\r
+ let pHi = i64x2.extmul_high_i32x4_s(ai, b0)\r
+ let tLo = v128.load(ptr)\r
+ let tHi = v128.load(ptr + 16)\r
+ tLo = i64x2.add(tLo, pLo)\r
+ tHi = i64x2.add(tHi, pHi)\r
+ v128.store(ptr, tLo)\r
+ v128.store(ptr + 16, tHi)\r
+\r
+ ptr += 32\r
+ pLo = i64x2.extmul_low_i32x4_s(ai, b4)\r
+ pHi = i64x2.extmul_high_i32x4_s(ai, b4)\r
+ tLo = v128.load(ptr)\r
+ tHi = v128.load(ptr + 16)\r
+ tLo = i64x2.add(tLo, pLo)\r
+ tHi = i64x2.add(tHi, pHi)\r
+ v128.store(ptr, tLo)\r
+ v128.store(ptr + 16, tHi)\r
+\r
+ ptr += 32\r
+ pLo = i64x2.extmul_low_i32x4_s(ai, b8)\r
+ pHi = i64x2.extmul_high_i32x4_s(ai, b8)\r
+ tLo = v128.load(ptr)\r
+ tHi = v128.load(ptr + 16)\r
+ tLo = i64x2.add(tLo, pLo)\r
+ tHi = i64x2.add(tHi, pHi)\r
+ v128.store(ptr, tLo)\r
+ v128.store(ptr + 16, tHi)\r
+\r
+ ptr += 32\r
+ pLo = i64x2.extmul_low_i32x4_s(ai, b12)\r
+ pHi = i64x2.extmul_high_i32x4_s(ai, b12)\r
+ tLo = v128.load(ptr)\r
+ tHi = v128.load(ptr + 16)\r
+ tLo = i64x2.add(tLo, pLo)\r
+ tHi = i64x2.add(tHi, pHi)\r
+ v128.store(ptr, tLo)\r
+ v128.store(ptr + 16, tHi)\r
}\r
+ Normalize(o, t)\r
+}\r
\r
- for (let i = 0; i < 15; i++) {\r
- t[i] += 38 * t[i + 16]\r
- }\r
- // t15 left as is\r
+function Square (o: StaticArray<i32>, a: StaticArray<i32>): void {\r
+ Multiply(o, a, a)\r
+}\r
+\r
+// Multiply and Square normalization across limbs\r
+// for (let i = 0; i < 15; i++) {\r
+// t[i] += 38 * t[i + 16]\r
+// }\r
+// for (let i = 0; i < 2; i++) {\r
+// for (let i = 0; i < 16; i++) {\r
+// c += t[i]\r
+// t[i] = c & 0xFFFF\r
+// c >>= 16\r
+// }\r
+// t[0] += 38 * c\r
+// }\r
+function Normalize (o: usize, t: usize): void {\r
+ // reduce\r
+ let x = load<i64>(t)\r
+ let y = load<i64>(t + 128)\r
+ store<i64>(t, x + (38 * y))\r
+\r
+ x = load<i64>(t + 8)\r
+ y = load<i64>(t + 136)\r
+ store<i64>(t + 8, x + (38 * y))\r
+\r
+ x = load<i64>(t + 16)\r
+ y = load<i64>(t + 144)\r
+ store<i64>(t + 16, x + (38 * y))\r
+\r
+ x = load<i64>(t + 24)\r
+ y = load<i64>(t + 152)\r
+ store<i64>(t + 24, x + (38 * y))\r
+\r
+ x = load<i64>(t + 32)\r
+ y = load<i64>(t + 160)\r
+ store<i64>(t + 32, x + (38 * y))\r
+\r
+ x = load<i64>(t + 40)\r
+ y = load<i64>(t + 168)\r
+ store<i64>(t + 40, x + (38 * y))\r
+\r
+ x = load<i64>(t + 48)\r
+ y = load<i64>(t + 176)\r
+ store<i64>(t + 48, x + (38 * y))\r
+\r
+ x = load<i64>(t + 56)\r
+ y = load<i64>(t + 184)\r
+ store<i64>(t + 56, x + (38 * y))\r
+\r
+ x = load<i64>(t + 64)\r
+ y = load<i64>(t + 192)\r
+ store<i64>(t + 64, x + (38 * y))\r
+\r
+ x = load<i64>(t + 72)\r
+ y = load<i64>(t + 200)\r
+ store<i64>(t + 72, x + (38 * y))\r
+\r
+ x = load<i64>(t + 80)\r
+ y = load<i64>(t + 208)\r
+ store<i64>(t + 80, x + (38 * y))\r
+\r
+ x = load<i64>(t + 88)\r
+ y = load<i64>(t + 216)\r
+ store<i64>(t + 88, x + (38 * y))\r
+\r
+ x = load<i64>(t + 96)\r
+ y = load<i64>(t + 224)\r
+ store<i64>(t + 96, x + (38 * y))\r
+\r
+ x = load<i64>(t + 104)\r
+ y = load<i64>(t + 232)\r
+ store<i64>(t + 104, x + (38 * y))\r
+\r
+ x = load<i64>(t + 112)\r
+ y = load<i64>(t + 240)\r
+ store<i64>(t + 112, x + (38 * y))\r
\r
// first carry\r
- c = 0\r
- for (let i = 0; i < 16; i++) {\r
- v = t[i] + c\r
- t[i] = v % s\r
- c = v / s\r
- }\r
- t[0] += 38 * c\r
+ let c: i64 = load<i64>(t)\r
+ store<i64>(t, c & 0xFFFF)\r
+ c >>= 16\r
\r
- // second carry\r
- c = 0\r
- for (let i = 0; i < 16; i++) {\r
- v = t[i] + c\r
- t[i] = v % s\r
- c = v / s\r
- }\r
- t[0] += 38 * c\r
+ c += load<i64>(t + 8)\r
+ store<i64>(t + 8, c & 0xFFFF)\r
+ c >>= 16\r
\r
- // assign result to output\r
- o.set(t.slice(0, 16), 0)\r
-}\r
+ c += load<i64>(t + 16)\r
+ store<i64>(t + 16, c & 0xFFFF)\r
+ c >>= 16\r
\r
-function Square (o: Int64Array, a: Int64Array): void {\r
- let v: i64 = 0\r
- let c: i64 = 0\r
- const s = 1 << 16\r
- const t = new Int64Array(31)\r
- t.fill(0)\r
+ c += load<i64>(t + 24)\r
+ store<i64>(t + 24, c & 0xFFFF)\r
+ c >>= 16\r
\r
- // init t values, same as Multiply except we can skip some iterations of\r
- // the inner loop since a[x]*a[y] + a[y]*a[x] = 2*a[x]*a[y]\r
- for (let i = 0; i < 16; i++) {\r
- for (let j = i; j < 16; j++) {\r
- t[i + j] += a[i] * a[j] * (u8(i < j) + 1)\r
- }\r
- }\r
+ c += load<i64>(t + 32)\r
+ store<i64>(t + 32, c & 0xFFFF)\r
+ c >>= 16\r
\r
- for (let i = 0; i < 15; i++) {\r
- t[i] += 38 * t[i + 16]\r
- }\r
- // t15 left as is\r
+ c += load<i64>(t + 40)\r
+ store<i64>(t + 40, c & 0xFFFF)\r
+ c >>= 16\r
\r
- // first carry\r
- c = 0\r
- for (let i = 0; i < 16; i++) {\r
- v = t[i] + c\r
- t[i] = v % s\r
- c = v / s\r
- }\r
- t[0] += 38 * c\r
+ c += load<i64>(t + 48)\r
+ store<i64>(t + 48, c & 0xFFFF)\r
+ c >>= 16\r
\r
- // second carry\r
- c = 0\r
- for (let i = 0; i < 16; i++) {\r
- v = t[i] + c\r
- t[i] = v % s\r
- c = v / s\r
- }\r
- t[0] += 38 * c\r
+ c += load<i64>(t + 56)\r
+ store<i64>(t + 56, c & 0xFFFF)\r
+ c >>= 16\r
\r
- // assign result to output\r
- o.set(t.slice(0, 16), 0)\r
-}\r
+ c += load<i64>(t + 64)\r
+ store<i64>(t + 64, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 72)\r
+ store<i64>(t + 72, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 80)\r
+ store<i64>(t + 80, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 88)\r
+ store<i64>(t + 88, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 96)\r
+ store<i64>(t + 96, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 104)\r
+ store<i64>(t + 104, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 112)\r
+ store<i64>(t + 112, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 120)\r
+ store<i64>(t + 120, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ store<i64>(t, load<i64>(t) + (38 * c))\r
+\r
+ // second carry and assign result to output\r
+ c = load<i64>(t)\r
+ store<i32>(o, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 8)\r
+ store<i32>(o + 4, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 16)\r
+ store<i32>(o + 8, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 24)\r
+ store<i32>(o + 12, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 32)\r
+ store<i32>(o + 16, c & 0xFFFF)\r
+ c >>= 16\r
\r
-function add (p: Int64Array[], q: Int64Array[]): void {\r
- const a: Int64Array = new Int64Array(16)\r
- const b: Int64Array = new Int64Array(16)\r
- const c: Int64Array = new Int64Array(16)\r
- const d: Int64Array = new Int64Array(16)\r
- const e: Int64Array = new Int64Array(16)\r
- const f: Int64Array = new Int64Array(16)\r
- const g: Int64Array = new Int64Array(16)\r
- const h: Int64Array = new Int64Array(16)\r
- const t: Int64Array = new Int64Array(16)\r
+ c += load<i64>(t + 40)\r
+ store<i32>(o + 20, c & 0xFFFF)\r
+ c >>= 16\r
\r
+ c += load<i64>(t + 48)\r
+ store<i32>(o + 24, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 56)\r
+ store<i32>(o + 28, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 64)\r
+ store<i32>(o + 32, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 72)\r
+ store<i32>(o + 36, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 80)\r
+ store<i32>(o + 40, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 88)\r
+ store<i32>(o + 44, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 96)\r
+ store<i32>(o + 48, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 104)\r
+ store<i32>(o + 52, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 112)\r
+ store<i32>(o + 56, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ c += load<i64>(t + 120)\r
+ store<i32>(o + 60, c & 0xFFFF)\r
+ c >>= 16\r
+\r
+ store<i64>(o, load<i64>(o) + (38 * c))\r
+}\r
+\r
+const a: StaticArray<i32> = new StaticArray<i32>(16)\r
+const b: StaticArray<i32> = new StaticArray<i32>(16)\r
+const c: StaticArray<i32> = new StaticArray<i32>(16)\r
+const d: StaticArray<i32> = new StaticArray<i32>(16)\r
+const e: StaticArray<i32> = new StaticArray<i32>(16)\r
+const f: StaticArray<i32> = new StaticArray<i32>(16)\r
+const g: StaticArray<i32> = new StaticArray<i32>(16)\r
+const h: StaticArray<i32> = new StaticArray<i32>(16)\r
+const t: StaticArray<i32> = new StaticArray<i32>(16)\r
+function add (p: StaticArray<i32>[], q: StaticArray<i32>[]): void {\r
Subtract(a, p[1], p[0])\r
Subtract(t, q[1], q[0])\r
Multiply(a, a, t)\r
Multiply(p[3], e, h)\r
}\r
\r
-function cswap (p: Int64Array[], q: Int64Array[], b: i64): void {\r
+function cswap (p: StaticArray<i32>[], q: StaticArray<i32>[], b: i32): void {\r
for (let i = 0; i < 4; i++) {\r
sel25519(p[i], q[i], b)\r
}\r
}\r
\r
-function pack (r: Uint8Array, p: Int64Array[]): void {\r
- const tx: Int64Array = new Int64Array(16)\r
- const ty: Int64Array = new Int64Array(16)\r
- const zi: Int64Array = new Int64Array(16)\r
+const tx: StaticArray<i32> = new StaticArray<i32>(16)\r
+const ty: StaticArray<i32> = new StaticArray<i32>(16)\r
+const zi: StaticArray<i32> = new StaticArray<i32>(16)\r
+function pack (r: StaticArray<u8>, p: StaticArray<i32>[]): void {\r
inv25519(zi, p[2])\r
Multiply(tx, p[0], zi)\r
Multiply(ty, p[1], zi)\r
r[31] ^= par25519(tx) << 7\r
}\r
\r
-function scalarmult (p: Int64Array[], q: Int64Array[], s: Uint8Array): void {\r
- p[0].fill(0)\r
- p[1].fill(0).set([1], 0)\r
- p[2].fill(0).set([1], 0)\r
- p[3].fill(0)\r
- for (let i: i32 = 255; i >= 0; i--) {\r
- const b = (s[(i / 8) | 0] >> u8(i & 7)) & 1\r
+function scalarmult (p: StaticArray<i32>[], q: StaticArray<i32>[], s: StaticArray<u8>): void {\r
+ for (let i = 255; i >= 0; i--) {\r
+ const b: i32 = (s[i >> 3] >> u8(i & 7)) & 1\r
cswap(p, q, b)\r
add(q, p)\r
add(p, p)\r
}\r
}\r
\r
-function scalarbase (p: Int64Array[], s: Uint8Array): void {\r
- const q: Int64Array[] = [new Int64Array(16), new Int64Array(16), new Int64Array(16), new Int64Array(16)]\r
- q[0].set(X, 0)\r
- q[1].set(Y, 0)\r
- q[2].set([1], 0)\r
- q[3].set(XY, 0)\r
+const scalarbase_q: StaticArray<i32>[] = [new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16)]\r
+function scalarbase (p: StaticArray<i32>[], s: StaticArray<u8>): void {\r
+ const q = scalarbase_q\r
+ for (let i = 0; i < 16; i++) {\r
+ q[0][i] = X[i]\r
+ q[1][i] = Y[i]\r
+ q[2][i] = 0\r
+ q[3][i] = XY[i]\r
+ }\r
+ q[2][0] = 1\r
scalarmult(p, q, s)\r
}\r
\r
-const L: StaticArray<i64> = StaticArray.fromArray<i64>([\r
+const L: StaticArray<i32> = StaticArray.fromArray<i32>([\r
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,\r
0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,\r
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\r
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10\r
])\r
\r
-function modL (r: Uint8Array, x: Int64Array): void {\r
- let carry: i64\r
- let i: i32\r
- let j: i32\r
- let k: i32\r
- for (i = 63; i >= 32; --i) {\r
- carry = 0\r
- for (j = i - 32, k = i - 12; j < k; ++j) {\r
- x[j] += carry - 16 * x[i] * L[j - (i - 32)]\r
- carry = (x[j] + 128) >> 8\r
- x[j] -= carry << 8\r
+function modL (r: StaticArray<u8>, x: StaticArray<i32>): void {\r
+ let c: i32\r
+ let t: i32\r
+ let v: i32\r
+ for (let i = 63; i >= 32; --i) {\r
+ c = 0\r
+ const xi = x[i]\r
+ for (let j = i - 32, k = i - 12; j < k; j++) {\r
+ t = (xi * L[j - (i - 32)]) << 4\r
+ v = x[j] + c - t\r
+ c = (v + 128) >> 8\r
+ x[j] = v - (c << 8)\r
}\r
- x[j] += carry\r
+ x[i - 12] += c\r
x[i] = 0\r
}\r
- carry = 0\r
- for (j = 0; j < 32; j++) {\r
- x[j] += carry - (x[31] >> 4) * L[j]\r
- carry = x[j] >> 8\r
+ c = 0\r
+ const x31 = x[31]\r
+ for (let j = 0; j < 32; j++) {\r
+ x[j] += c - (x31 >> 4) * L[j]\r
+ c = x[j] >> 8\r
x[j] &= 255\r
}\r
- for (j = 0; j < 32; j++) {\r
- x[j] -= carry * L[j]\r
+ for (let j = 0; j < 32; j++) {\r
+ x[j] -= c * L[j]\r
}\r
- for (i = 0; i < 32; i++) {\r
+ for (let i = 0; i < 32; i++) {\r
x[i + 1] += x[i] >> 8\r
r[i] = u8(x[i] & 255)\r
}\r
}\r
\r
-function reduce (r: Uint8Array): void {\r
- let x = new Int64Array(64)\r
- x.set(r.subarray(0, 64), 0)\r
- r.fill(0, 0, 64)\r
+const x = new StaticArray<i32>(64)\r
+function reduce (r: StaticArray<u8>): void {\r
+ for (let i = 0; i < 64; i++) {\r
+ x[i] = i32(r[i])\r
+ r[i] = 0\r
+ }\r
modL(r, x)\r
}\r
\r
-function unpackneg (r: Array<Int64Array>, p: Uint8Array): i8 {\r
- const t: Int64Array = new Int64Array(16)\r
- const chk: Int64Array = new Int64Array(16)\r
- const num: Int64Array = new Int64Array(16)\r
- const den: Int64Array = new Int64Array(16)\r
- const den2: Int64Array = new Int64Array(16)\r
- const den4: Int64Array = new Int64Array(16)\r
- const den6: Int64Array = new Int64Array(16)\r
+const unpack_t: StaticArray<i32> = new StaticArray<i32>(16)\r
+const chk: StaticArray<i32> = new StaticArray<i32>(16)\r
+const num: StaticArray<i32> = new StaticArray<i32>(16)\r
+const den: StaticArray<i32> = new StaticArray<i32>(16)\r
+const den2: StaticArray<i32> = new StaticArray<i32>(16)\r
+const den4: StaticArray<i32> = new StaticArray<i32>(16)\r
+const den6: StaticArray<i32> = new StaticArray<i32>(16)\r
+const z: StaticArray<i32> = new StaticArray<i32>(16)\r
+function unpackneg (r: Array<StaticArray<i32>>, p: StaticArray<u8>): i8 {\r
+ const t = unpack_t\r
\r
- r[2].fill(0).set([1], 0)\r
unpack25519(r[1], p)\r
Square(num, r[1])\r
Multiply(den, num, D)\r
}\r
\r
if (par25519(r[0]) === (p[31] >> 7)) {\r
- Subtract(r[0], new Int64Array(16), r[0])\r
+ Subtract(r[0], z, r[0])\r
}\r
Multiply(r[3], r[0], r[1])\r
return 0\r
}\r
\r
-function crypto_sign (sm: Uint8Array, m: Uint8Array, n: i32, sk: Uint8Array, pk: Uint8Array): void {\r
- const p: Int64Array[] = [new Int64Array(16), new Int64Array(16), new Int64Array(16), new Int64Array(16)]\r
+const blake2b = new Blake2b()\r
+function crypto_hash (o: StaticArray<u8>, i: StaticArray<u8>): void {\r
+ blake2b.init().update(i).digest(o)\r
+}\r
\r
- const d: Uint8Array = new Blake2b(64).update(sk).digest()\r
+const crypto_convert_h = new StaticArray<u8>(64)\r
+const crypto_convert_p = [new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16)]\r
+function crypto_convert (pk: StaticArray<u8>, sk: StaticArray<u8>): void {\r
+ const h = crypto_convert_h\r
+ const p = crypto_convert_p\r
+ p[0].fill(0)\r
+ p[1].fill(0); p[1][0] = 1\r
+ p[2].fill(0); p[2][0] = 1\r
+ p[3].fill(0)\r
+ crypto_hash(h, sk)\r
+ h[0] &= 248\r
+ h[31] &= 127\r
+ h[31] |= 64\r
+ scalarbase(p, h)\r
+ pack(pk, p)\r
+}\r
+\r
+const crypto_sign_d = new StaticArray<u8>(64)\r
+const crypto_sign_h = new StaticArray<u8>(64)\r
+const crypto_sign_p = [new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16)]\r
+const crypto_sign_r = new StaticArray<u8>(64)\r
+const crypto_sign_x = new StaticArray<i32>(64)\r
+const crypto_sign_s = new StaticArray<u8>(SIGNATURE_BYTES)\r
+const crypto_sign_prv = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
+function crypto_sign (sm: StaticArray<u8>, m: StaticArray<u8>, sk: StaticArray<u8>): void {\r
+ const d = crypto_sign_d\r
+ const h = crypto_sign_h\r
+ const p = crypto_sign_p\r
+ const r = crypto_sign_r\r
+ const x = crypto_sign_x\r
+ const s = crypto_sign_s\r
+ const prv = crypto_sign_prv\r
+ p[0].fill(0)\r
+ p[1].fill(0); p[1][0] = 1\r
+ p[2].fill(0); p[2][0] = 1\r
+ p[3].fill(0)\r
+\r
+ for (let i = 0; i < PRIVATEKEY_BYTES; i++) {\r
+ prv[i] = sk[i]\r
+ }\r
+ crypto_hash(d, prv)\r
d[0] &= 248\r
d[31] &= 127\r
d[31] |= 64\r
\r
- sm.set(m.subarray(0, n), 64)\r
- sm.set(d.subarray(32, 64), 32)\r
+ for (let i = 0; i < 32; i++) {\r
+ s[i] = sm[i + 32] = d[i + 32]\r
+ s[i + 32] = sm[i + 64] = m[i]\r
+ }\r
\r
- const r: Uint8Array = new Blake2b(64).update(sm.subarray(32)).digest()\r
+ crypto_hash(r, s)\r
reduce(r)\r
scalarbase(p, r)\r
pack(sm, p)\r
\r
- sm.set(pk, 32)\r
- const h: Uint8Array = new Blake2b(64).update(sm).digest()\r
+ for (let i = 32; i < 64; i++) {\r
+ sm[i] = sk[i]\r
+ }\r
+ crypto_hash(h, sm)\r
reduce(h)\r
\r
- const x = new Int64Array(64)\r
- x.set(r.subarray(0, 32))\r
+ x.fill(0)\r
+ for (let i = 0; i < 32; i++) {\r
+ x[i] = r[i]\r
+ }\r
for (let i = 0; i < 32; i++) {\r
for (let j = 0; j < 32; j++) {\r
- x[i + j] += i64(h[i]) * i64(d[j])\r
+ x[i + j] += i32(h[i]) * i32(d[j])\r
}\r
}\r
\r
- modL(sm.subarray(32), x)\r
+ for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
+ s[i] = sm[i + 32]\r
+ }\r
+ modL(s, x)\r
+ for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
+ sm[i + 32] = s[i]\r
+ }\r
}\r
\r
+const crypto_verify_open_d = new StaticArray<u8>(64)\r
+const crypto_verify_open_p = [new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16)]\r
+const crypto_verify_open_q = [new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16), new StaticArray<i32>(16)]\r
+const crypto_verify_open_t = new StaticArray<u8>(32)\r
+const crypto_verify_open_sm = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
+const crypto_verify_open_S = new StaticArray<u8>(32)\r
/**\r
-* Verifies that the signed message `sm` was signed using the public key `pk`.\r
-* If so, the original message is copied into `m` and the original message\r
-* bytelength is returned. Otherwise, `m` is zeroed and -1 is returned.\r
+* Verifies block hash `h` was signed with signature `s` against public key `k`.\r
*/\r
-function crypto_sign_open (m: Uint8Array, sm: Uint8Array, n: i32, pk: Uint8Array): i32 {\r
- const t = new Uint8Array(32)\r
- const p: Int64Array[] = [new Int64Array(16), new Int64Array(16), new Int64Array(16), new Int64Array(16)]\r
- const q: Int64Array[] = [new Int64Array(16), new Int64Array(16), new Int64Array(16), new Int64Array(16)]\r
+function crypto_verify (h: StaticArray<u8>, s: StaticArray<u8>, k: StaticArray<u8>): bool {\r
+ const d = crypto_verify_open_d\r
+ const p = crypto_verify_open_p\r
+ const q = crypto_verify_open_q\r
+ const t = crypto_verify_open_t\r
+ const sm = crypto_verify_open_sm\r
+ const S = crypto_verify_open_S\r
+ p[0].fill(0)\r
+ p[1].fill(0); p[1][0] = 1\r
+ p[2].fill(0); p[2][0] = 1\r
+ p[3].fill(0)\r
+ q[0].fill(0)\r
+ q[1].fill(0)\r
+ q[2].fill(0); q[2][0] = 1\r
+ q[3].fill(0)\r
\r
// fail\r
- if (unpackneg(q, pk)) {\r
- return -1\r
+ if (unpackneg(q, k)) {\r
+ return false\r
}\r
\r
- m.set(sm.subarray(0, n), 0)\r
- m.set(pk.subarray(0, 32), 32)\r
- const h: Uint8Array = new Blake2b(64).update(m).digest()\r
- reduce(h)\r
- scalarmult(p, q, h)\r
- scalarbase(q, sm.subarray(32))\r
+ // signature is nonce point R and scalar S (R || S)\r
+ // data to hash is nonce point R, public key A, and message M (R || A || M)\r
+ // R, S, A, and M are all 32-byte values in this implementation\r
+ for (let i = 0; i < 32; i++) {\r
+ S[i] = s[i + 32]\r
+ sm[i] = s[i]\r
+ sm[i + 32] = k[i]\r
+ sm[i + 64] = h[i]\r
+ }\r
+ crypto_hash(d, sm)\r
+ reduce(d)\r
+ scalarmult(p, q, d)\r
+\r
+ q[0].fill(0)\r
+ q[1].fill(0); q[1][0] = 1\r
+ q[2].fill(0); q[2][0] = 1\r
+ q[3].fill(0)\r
+ scalarbase(q, S)\r
add(p, q)\r
pack(t, p)\r
\r
- // check failure\r
- if (vn(sm, 0, t, 0, 32)) {\r
- m.fill(0)\r
- return -1\r
- }\r
+ return !vn(s, t)\r
+}\r
+\r
+// Returns the pointer to the static input buffer (128 bytes).\r
+export function getInputPointer (): usize {\r
+ return INPUT_BUFFER\r
+}\r
\r
- // success\r
- n -= 64\r
- m.set(sm.subarray(64, n + 64), 0)\r
- return n\r
+// Returns the pointer to the static output buffer (64 bytes).\r
+export function getOutputPointer (): usize {\r
+ return OUTPUT_BUFFER\r
}\r
\r
+const derive_sk = new StaticArray<u8>(PRIVATEKEY_BYTES)\r
+const derive_pk = new StaticArray<u8>(PUBLICKEY_BYTES)\r
/**\r
-* Derives a Nano public key from a private key. This mirrors the functionality\r
-* of `nacl.sign.keyPair.fromSeed()`, returning just the `publicKey` property.\r
-*\r
-* @param {Uint8Array} privateKey - 32-byte private key\r
-* @returns {Uint8Array} 32-byte public key\r
+* Derives a Nano public key from a private key and writes it to the static\r
+* output buffer. This mirrors the functionality of\r
+* `nacl.sign.keyPair.fromSeed()`, returning just the `publicKey` property.\r
*/\r
-export function derive (k0: u64, k1: u64, k2: u64, k3: u64): Uint8Array {\r
- const k = new Uint64Array(4); k.set([k0, k1, k2, k3])\r
- const privateKey = Uint8Array.wrap(k.buffer)\r
- if (privateKey.byteLength !== PRIVATEKEY_BYTES) {\r
- const err = `Invalid private key byte length to convert to public key, expected ${PRIVATEKEY_BYTES}, actual ${privateKey.byteLength}`\r
- trace(err, 1, privateKey.byteLength)\r
- throw new Error(err)\r
- }\r
- const publicKey = new Uint8Array(PUBLICKEY_BYTES)\r
- const p: Int64Array[] = [new Int64Array(16), new Int64Array(16), new Int64Array(16), new Int64Array(16)]\r
-\r
- const h: Uint8Array = new Blake2b(64).update(privateKey).digest()\r
- h[0] &= 248\r
- h[31] &= 127\r
- h[31] |= 64\r
+export function derive (): void {\r
+ const sk = derive_sk\r
+ const pk = derive_pk\r
+ for (let i = 0; i < PRIVATEKEY_BYTES; i++) {\r
+ sk[i] = load<u8>(INPUT_BUFFER + i)\r
+ }\r
\r
- scalarbase(p, h)\r
- pack(publicKey, p)\r
+ const start = performance.now()\r
+ crypto_convert(pk, sk)\r
+ const end = performance.now()\r
+ trace('derive time', 1, end - start)\r
\r
- return publicKey\r
+ for (let i = 0; i < PUBLICKEY_BYTES; i++) {\r
+ store<u8>(OUTPUT_BUFFER + i, pk[i])\r
+ }\r
}\r
\r
+const sign_h = new StaticArray<u8>(BLOCKHASH_BYTES)\r
+const sign_sk = new StaticArray<u8>(SECRETKEY_BYTES)\r
+const sign_s = new StaticArray<u8>(SIGNEDBLOCKHASH_BYTES)\r
/**\r
-* Signs the message using the private key and returns a signature. This mirrors\r
-* the functionality of `nacl.sign.detached()`.\r
+* Signs the message using the private key and writes the signature to the static\r
+* output buffer. This mirrors the functionality of `nacl.sign.detached()`.\r
*\r
-* @param {Uint8Array} message - Message to sign\r
-* @param {Uint8Array} privateKey - 32-byte key to use for signing\r
-* @returns {Uint8Array} 64-byte signature\r
+* @param {u64} h0-h3 - Message to sign (32 bytes as 4 × u64)\r
+* @param {u64} k0-k7 - Secret key (32 bytes private key + 32 bytes public key as 8 × u64)\r
*/\r
-export function sign (h0: u64, h1: u64, h2: u64, h3: u64, k0: u64, k1: u64, k2: u64, k3: u64): Uint8Array {\r
- const h = new Uint64Array(4); h.set([h0, h1, h2, h3])\r
- const k = new Uint64Array(4); k.set([k0, k1, k2, k3])\r
- const message = Uint8Array.wrap(h.buffer)\r
- const privateKey = Uint8Array.wrap(k.buffer)\r
- if (privateKey.byteLength !== PRIVATEKEY_BYTES) {\r
- const err = `Invalid key byte length to sign message, expected ${PRIVATEKEY_BYTES}, actual ${privateKey.byteLength}`\r
- trace(err, 1, privateKey.byteLength)\r
- throw new Error(err)\r
- }\r
- const signed = new Uint8Array(SIGNATURE_BYTES + message.length)\r
- const publicKey = derive(k0, k1, k2, k3)\r
- crypto_sign(signed, message, message.length, privateKey, publicKey)\r
- const sig = new Uint8Array(SIGNATURE_BYTES)\r
- for (let i = 0; i < sig.length; i++) {\r
- sig[i] = signed[i]\r
- }\r
- return sig\r
+export function sign (): void {\r
+ const h = sign_h\r
+ const sk = sign_sk\r
+ const s = sign_s\r
+ let ptr = INPUT_BUFFER\r
+ for (let i = 0; i < BLOCKHASH_BYTES; i++) {\r
+ h[i] = load<u8>(usize(i) + ptr)\r
+ }\r
+ ptr += BLOCKHASH_BYTES\r
+ for (let i = 0; i < SECRETKEY_BYTES; i++) {\r
+ sk[i] = load<u8>(usize(i) + ptr)\r
+ }\r
+\r
+ const start = performance.now()\r
+ crypto_sign(s, h, sk)\r
+ const end = performance.now()\r
+ trace('sign time', 1, end - start)\r
+\r
+ for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
+ store<u8>(OUTPUT_BUFFER + i, s[i])\r
+ }\r
}\r
\r
+const verify_h = new StaticArray<u8>(BLOCKHASH_BYTES)\r
+const verify_s = new StaticArray<u8>(SIGNATURE_BYTES)\r
+const verify_k = new StaticArray<u8>(PUBLICKEY_BYTES)\r
/**\r
-* Verifies a signature on a blockhash against a public key. This mirrors the\r
+* Verifies a signature on a block hash against a public key. This mirrors the\r
* functionality of `nacl.sign.detached.verify()`.\r
*\r
-* @param {Uint8Array} blockhash - 32-byte hash of signed Nano block\r
-* @param {Uint8Array} signature - 64-byte signature\r
-* @param {Uint8Array} publicKey - 32-byte key used for signing\r
-* @returns {boolean} - True if `publicKey` was used to sign `blockhash` and generate `signature`, else false\r
+* @param {u64} h0-h3 - Blockhash (32 bytes as 4 × u64)\r
+* @param {u64} s0-s7 - Signature (64 bytes as 8 × u64)\r
+* @param {u64} k0-k3 - Public key (32 bytes as 4 × u64)\r
+* @returns {i32} - 1 if valid, 0 if invalid\r
*/\r
-export function verify (h0: u64, h1: u64, h2: u64, h3: u64, s0: u64, s1: u64, s2: u64, s3: u64, s4: u64, s5: u64, s6: u64, s7: u64, k0: u64, k1: u64, k2: u64, k3: u64): i32 {\r
- const h = new Uint64Array(4); h.set([h0, h1, h2, h3])\r
- const s = new Uint64Array(8); s.set([s0, s1, s2, s3, s4, s5, s6, s7])\r
- const k = new Uint64Array(4); k.set([k0, k1, k2, k3])\r
- const blockhash = Uint8Array.wrap(h.buffer)\r
- const signature = Uint8Array.wrap(s.buffer)\r
- const publicKey = Uint8Array.wrap(k.buffer)\r
- if (blockhash.byteLength !== BLOCKHASH_BYTES) {\r
- const err = `Invalid blockhash size to verify signature, expected ${BLOCKHASH_BYTES}, actual ${blockhash.byteLength}`\r
- trace(err, 1, blockhash.byteLength)\r
- throw new Error(err)\r
- }\r
- if (signature.byteLength !== SIGNATURE_BYTES) {\r
- const err = `Invalid signature size to verify signature, expected ${SIGNATURE_BYTES}, actual ${signature.byteLength}`\r
- trace(err, 1, signature.byteLength)\r
- throw new Error(err)\r
- }\r
- if (publicKey.byteLength !== PUBLICKEY_BYTES) {\r
- const err = `Invalid public key size to verify signature, expected ${PUBLICKEY_BYTES}, actual ${publicKey.byteLength}`\r
- trace(err, 1, signature.byteLength)\r
- throw new Error(err)\r
- }\r
- const sm = new Uint8Array(SIGNATURE_BYTES + blockhash.length)\r
- const m = new Uint8Array(SIGNATURE_BYTES + blockhash.length)\r
- sm.set(signature, 0)\r
- sm.set(blockhash, SIGNATURE_BYTES)\r
- return (crypto_sign_open(m, sm, sm.length, publicKey) >= 0) ? 1 : 0\r
+export function verify (): i32 {\r
+ const h = verify_h\r
+ const s = verify_s\r
+ const k = verify_k\r
+\r
+ let ptr = INPUT_BUFFER\r
+ for (let i = 0; i < BLOCKHASH_BYTES; i++) {\r
+ h[i] = load<u8>(usize(i) + ptr)\r
+ }\r
+ ptr += BLOCKHASH_BYTES\r
+ for (let i = 0; i < SIGNATURE_BYTES; i++) {\r
+ s[i] = load<u8>(usize(i) + ptr)\r
+ }\r
+ ptr += SIGNATURE_BYTES\r
+ for (let i = 0; i < PUBLICKEY_BYTES; i++) {\r
+ k[i] = load<u8>(usize(i) + ptr)\r
+ }\r
+\r
+ const start = performance.now()\r
+ const result = crypto_verify(h, s, k)\r
+ const end = performance.now()\r
+ trace('verify time', 1, end - start)\r
+\r
+ return result ? 1 : 0\r
}\r
<!DOCTYPE html>
<head>
+ <meta charset="utf-8">
<link rel="icon" href="data:,">
+ <script src="https://unpkg.com/nanocurrency-web@1.4.3/dist/index.min.js"></script>
+ <script src="https://unpkg.com/tweetnacl@1.0.3/nacl-fast.js"></script>
<script type="module">
-
-import * as NanoNaCl from './dist/browser.js'
-
-const blockHash = 'BB569136FA05F8CBF65CEF2EDE368475B289C4477342976556BA4C0DDF216E45'
-const privateKey = '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3'
-
-try {
- const { derive, sign, verify } = NanoNaCl
-
- const publicKey = await derive(privateKey)
- console.log('publicKey', publicKey)
- console.log(publicKey.toUpperCase() === '3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039')
-
- const signature = await sign(blockHash, privateKey)
- console.log('signature', signature)
- console.log(signature.toUpperCase() === '74BCC59DBA39A1E34A5F75F96D6DE9154E3477AAD7DE30EA563DFCFE501A804228008F98DDF4A15FD35705102785C50EF76732C3A74B0FEC5B0DD67B574A5900')
-
- const isValid = await verify(blockHash, signature, publicKey)
- console.log('isValid', isValid)
- console.log(isValid === true)
-} catch (err) {
- console.error(err)
-}
-/*
- let NanoNaCl, stats
+ let NanoNaCl, TEST_VECTORS
try {
- ({ NanoNaCl, stats } = await import('./index.js'))
+ NanoNaCl = await import('./dist/browser.js')
} catch (err) {
console.warn(err)
try {
- ({ NanoNaCl, stats } = await import('https://unpkg.com/nano-nacl@5.1/dist/main.min.js'))
+ NanoNaCl = await import('https://unpkg.com/nano-nacl')
} catch (err) {
console.warn(err)
try {
- ({ NanoNaCl, stats } = await import('https://cdn.jsdelivr.net/npm/nano-nacl@5.1/dist/main.min.js'))
+ NanoNaCl = await import('https://cdn.jsdelivr.net/npm/nano-nacl@latest')
} catch (err) {
throw new Error(`Failed to load NanoNaCl ${err}`)
}
}
}
+ try {
+ ({ TEST_VECTORS } = await import('./env.mjs'))
+ } catch (err) {
+ console.error(err)
+ }
- const glSize = (canvas => {
- const gl = canvas.getContext('webgl2')
- canvas.height = gl.getParameter(gl.MAX_VIEWPORT_DIMS)[0]
- canvas.width = gl.getParameter(gl.MAX_VIEWPORT_DIMS)[1]
- return Math.min(gl.drawingBufferHeight, gl.drawingBufferWidth)
- })(new OffscreenCanvas(0, 0))
-
- function random (size = 64) {
- let hex = ''
- while (hex.length < size) {
- hex += crypto.randomUUID().replace(/-.*-/g, '')
+ /**
+ * Computes various types of averages for a set of numbers.
+ *
+ * @param {number[]} times - List of numbers, often timing durations.
+ * @returns Object with averaged values for the specified list.
+ */
+ function stats (times) {
+ if (times == null || times.length === 0) return null
+
+ const count = times.length
+ const truncatedStart = Math.floor(count * 0.1)
+ const truncatedEnd = count - truncatedStart
+ const truncatedCount = truncatedEnd - truncatedStart
+ let min = Number.MAX_SAFE_INTEGER
+ let logarithms, max, median, reciprocals, total
+ logarithms = max = median = reciprocals = total = 0
+
+ let truncatedMin = Number.MAX_SAFE_INTEGER
+ let truncatedLogarithms, truncatedMax, truncatedReciprocals, truncatedTotal
+ truncatedLogarithms = truncatedMax = truncatedReciprocals = truncatedTotal = 0
+
+ times.sort((a, b) => a - b)
+ for (let i = 0; i < count; i++) {
+ const time = times[i]
+ total += time
+ logarithms += Math.log(time)
+ reciprocals += 1 / time
+ min = Math.min(min, time)
+ max = Math.max(max, time)
+ if (i === Math.floor((count - 1) / 2)) median = time
+ if (i === Math.floor(count / 2) && count % 2 === 0) median = (median + time) / 2
+ }
+ for (let i = truncatedStart; i < truncatedEnd; i++) {
+ const time = times[i]
+ truncatedTotal += time
+ truncatedLogarithms += Math.log(time)
+ truncatedReciprocals += 1 / time
+ truncatedMin = Math.min(truncatedMin, time)
+ truncatedMax = Math.max(truncatedMax, time)
+ }
+ return {
+ count,
+ total,
+ rate: 1000 * count / total,
+ min,
+ max,
+ median,
+ arithmetic: total / count,
+ geometric: Math.exp(logarithms / count),
+ harmonic: count / reciprocals,
+ truncatedCount,
+ truncatedTotal,
+ truncatedRate: 1000 * truncatedCount / truncatedTotal,
+ truncatedMin,
+ truncatedMax,
+ truncatedArithmetic: truncatedTotal / truncatedCount,
+ truncatedGeometric: Math.exp(truncatedLogarithms / truncatedCount),
+ truncatedHarmonic: truncatedCount / truncatedReciprocals,
}
- return hex.slice(0, size)
}
- function average (times, type, effort) {
+ function average (times, type) {
const averages = stats(times)
- const title = `NanoNaCl (${type})`
+ const title = `${type} derive-sign-verify`
return {
[title]: averages
}
}
- export async function run (size, difficulty, effort, api, isOutputShown, isDebug, isSelfCheck) {
- // Generate once on load to compile shaders and initialize buffers
- await NanoNaCl.sign(random(), { api, difficulty: 0 })
- const type = api
- api = type.toLowerCase()
+ function random (length = 32) {
+ const bytes = crypto.getRandomValues(new Uint8Array(length))
+ return [...bytes].map(b => b.toString(16).padStart(2, '0')).join('')
+ }
+
+ export async function test (api, size, runs, isDebug, isSelfCheck) {
+ // Execute once on load to initialize worker and WASM
+ const h = random(), k = random(64)
+ await NanoNaCl.sign(h, k)
+
+ const derive = sk => {
+ switch (api) {
+ case 'NanoNaCl': {
+ return NanoNaCl.derive(sk)
+ }
+ case 'NanocurrencyWeb': {
+ return NanocurrencyWeb.wallet.legacyAccounts(sk)[0].publicKey
+ }
+ case 'TweetNaCl.js': {
+ const sk8 = sk.match(/.{2}/g).map(b => parseInt(b, 16))
+ const pk8 = nacl.sign.keyPair.fromSeed(new Uint8Array(sk8)).publicKey
+ return [...pk8].map(b => b.toString(16).padStart(2, '0')).join('')
+ }
+ default: {
+ return null
+ }
+ }
+ }
+ const sign = (h, sk, pk) => {
+ switch (api) {
+ case 'NanoNaCl': {
+ return NanoNaCl.sign(h, sk + pk)
+ }
+ case 'NanocurrencyWeb': {
+ const { privateKey } = NanocurrencyWeb.wallet.legacyAccounts(sk)[0]
+ return NanocurrencyWeb.tools.sign(privateKey, h)
+ }
+ case 'TweetNaCl.js': {
+ sk = sk + pk
+ const h8 = h.match(/.{2}/g).map(b => parseInt(b, 16))
+ const sk8 = sk.match(/.{2}/g).map(b => parseInt(b, 16))
+ const s8 = nacl.sign.detached(new Uint8Array(h8), new Uint8Array(sk8))
+ return [...s8].map(b => b.toString(16).padStart(2, '0')).join('')
+ }
+ default: {
+ return null
+ }
+ }
+ }
+ const verify = (h, s, pk) => {
+ switch (api) {
+ case 'NanoNaCl': {
+ return NanoNaCl.verify(h, s, pk)
+ }
+ case 'NanocurrencyWeb': {
+ return NanocurrencyWeb.tools.verify(pk, s, h)
+ }
+ case 'TweetNaCl.js': {
+ const h8 = h.match(/.{2}/g).map(b => parseInt(b, 16))
+ const s8 = s.match(/.{2}/g).map(b => parseInt(b, 16))
+ const pk8 = pk.match(/.{2}/g).map(b => parseInt(b, 16))
+ return nacl.sign.detached.verify(new Uint8Array(h8), new Uint8Array(s8), new Uint8Array(pk8))
+ }
+ default: {
+ return null
+ }
+ }
+ }
+
if (isSelfCheck) {
document.getElementById('status').innerHTML = `RUNNING SELF-CHECK`
console.log(`%cNanoNaCl`, 'color:green', 'Checking validation against known values')
-
+ TEST_VECTORS ??= {
+ blockHash: 'BB569136FA05F8CBF65CEF2EDE368475B289C4477342976556BA4C0DDF216E45',
+ privateKey: '781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3',
+ publicKey: '3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039',
+ signature: '74BCC59DBA39A1E34A5F75F96D6DE9154E3477AAD7DE30EA563DFCFE501A804228008F98DDF4A15FD35705102785C50EF76732C3A74B0FEC5B0DD67B574A5900'
+ }
+ const zeroes = '0000000000000000000000000000000000000000000000000000000000000000'
const expect = []
let result
// PASS
- result = await NanoNaCl.verify('47c83266398728cf', '92BA74A7D6DC7557F3EDA95ADC6341D51AC777A0A6FF0688A5C492AB2B2CB40D', { api, debug: isDebug })
- console.log(result)
- result = result.valid_all === '1'
- console.log(`verify() output for good nonce 1 is ${result === true ? 'correct' : 'incorrect'}`)
- expect.push(result)
-
- result = await NanoNaCl.verify('4a8fb104eebbd336', '8797585D56B8AEA3A62899C31FC088F9BE849BA8298A88E94F6E3112D4E55D01', { api, debug: isDebug })
+ result = await NanoNaCl.derive(TEST_VECTORS.privateKey)
console.log(result)
- result = result.valid_all === '1'
- console.log(`verify() output for good nonce 2 is ${result === true ? 'correct' : 'incorrect'}`)
+ result = result.toUpperCase() === TEST_VECTORS.publicKey.toUpperCase()
+ console.log(`derive() output for good private key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify('c5d5d6f7c5d6ccd1', '281E89AC73B1082B464B9C3C1168384F846D39F6DF25105F8B4A22915E999117', { api, debug: isDebug })
+ result = await NanoNaCl.sign(TEST_VECTORS.blockHash, TEST_VECTORS.privateKey + TEST_VECTORS.publicKey)
console.log(result)
- result = result.valid_all === '1'
- console.log(`verify() output for colliding nonce is ${result === true ? 'correct' : 'incorrect'}`)
+ result = result.toUpperCase() === TEST_VECTORS.signature.toUpperCase()
+ console.log(`sign() output for good block hash and secret key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify('6866c1ac3831a891', '7069D9CD1E85D6204301D254B0927F06ACC794C9EA5DF70EA5578458FB597090', { api, difficulty: 0xfffffe0000000000n, debug: isDebug })
+ result = await NanoNaCl.verify(TEST_VECTORS.blockHash, TEST_VECTORS.signature, TEST_VECTORS.publicKey)
console.log(result)
- result = result.valid === '1' && result.valid_all === '0' && result.valid_receive === '1'
- console.log(`verify() output for good receive difficulty nonce is ${result === true ? 'correct' : 'incorrect'}`)
+ result = result === true
+ console.log(`verify() output for good block hash, signature, and private key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
// XFAIL
- result = await NanoNaCl.verify('0000000000000000', '0000000000000000000000000000000000000000000000000000000000000000', { api, debug: isDebug })
- console.log(result)
- result = result.valid_all === '0'
- console.log(`verify() output for bad nonce 1 is ${result === true ? 'correct' : 'incorrect'}`)
- expect.push(result)
-
- result = await NanoNaCl.verify('c5d5d6f7c5d6ccd1', 'BA1E946BA3D778C2F30A83D44D2132CC6EEF010D8D06FF10A8ABD0100D8FB47E', { api, debug: isDebug })
- console.log(result)
- result = result.valid_all === '0'
- console.log(`verify() output for bad nonce 2 is ${result === true ? 'correct' : 'incorrect'}`)
- expect.push(result)
-
- result = await NanoNaCl.verify('ae238556213c3624', 'BF41D87DA3057FDC6050D2B00C06531F89F4AA6195D7C6C2EAAF15B6E703F8F6', { api, difficulty: 0xfffffff800000001n, debug: isDebug })
+ result = await NanoNaCl.verify(zeroes, TEST_VECTORS.signature, TEST_VECTORS.publicKey)
console.log(result)
- result = result.error === 'Invalid difficulty fffffff800000001'
- console.log(`verify() output for bad difficulty beyond max is ${result === true ? 'correct' : 'incorrect'}`)
+ result = result === false
+ console.log(`verify() output for bad block hash is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify('29a9ae0236990e2e', '32721F4BD2AFB6F6A08D41CD0DF3C0D9C0B5294F68D0D12422F52B28F0800B5F', { api, debug: isDebug })
+ result = await NanoNaCl.verify(TEST_VECTORS.blockHash, `${zeroes}${zeroes}`, TEST_VECTORS.publicKey)
console.log(result)
- result = result.valid_all === '0'
- console.log(`verify() output for slightly wrong nonce is ${result === true ? 'correct' : 'incorrect'}`)
+ result = result === false
+ console.log(`verify() output for bad signature is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- result = await NanoNaCl.verify('7d903b18d03f9820', '39C57C28F904DFE4012288FFF64CE80C0F42601023A9C82108E8F7B2D186C150', { api, difficulty: 0xfffffe0000000000n, debug: isDebug })
+ result = await NanoNaCl.verify(TEST_VECTORS.blockHash, TEST_VECTORS.signature, zeroes)
console.log(result)
- result = result.valid === '0' && result.valid_all === '0' && result.valid_receive === '0'
- console.log(`verify() output for bad receive difficulty nonce is ${result === true ? 'correct' : 'incorrect'}`)
+ result = result === false
+ console.log(`verify() output for bad public key is ${result === true ? 'correct' : 'incorrect'}`)
expect.push(result)
- const prefixes = [
- '0B1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111',
- '0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111',
- '0O17777777777777777777777777777777777777777777777777777777777777777777777777777777777777',
- '0o17777777777777777777777777777777777777777777777777777777777777777777777777777777777777',
- '0Xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
- '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
- ' 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn ',
- ' ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn ',
- ]
- for (let i = 0; i < prefixes.length; i++) {
- const hash = prefixes[i]
- let result = null
- const start = performance.now()
- try {
- result = await NanoNaCl.sign(hash, { api, difficulty, effort, debug: isDebug })
- console.log(result)
- } catch (err) {
- document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
- console.error(err)
- return
- }
- const end = performance.now()
- const check = await NanoNaCl.verify(result.work, result.hash, { difficulty, debug: isDebug })
- const isValid = (check.valid === '1' && BigInt(`0x${result.hash}`) === BigInt(hash.replace('n', '').replace(' f', '0xf')))
- console.log(`sign() output for max value block hash is ${isValid === true ? 'correct' : 'incorrect'}`)
- expect.push(isValid)
- }
-
try {
if (!expect.every(result => result)) throw new Error(`Validation is not working`)
} catch (err) {
}
}
- document.getElementById('status').innerHTML = `TESTING IN PROGRESS 0/${size}`
- console.log(`%cNanoNaCl (${type})`, 'color:green', `Sign ${size} unique send block hashes`)
- const times = []
- for (let i = 0; i < size; i++) {
- document.getElementById('status').innerHTML = `TESTING IN PROGRESS ${i}/${size}<br/>`
- const hash = random()
- let result = null
- const start = performance.now()
- try {
- result = await NanoNaCl.sign(hash, { api, difficulty, effort, debug: isDebug })
- } catch (err) {
- document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
- console.error(err)
- return
- }
- const end = performance.now()
- const check = await NanoNaCl.verify(result.work, result.hash, { difficulty, debug: isDebug })
- const isValid = (result.hash === hash && check.valid === '1') ? 'VALID' : 'INVALID'
- times.push(end - start)
- const msg = `${isValid} (${end - start} ms)\n${JSON.stringify(result, ' ', 2)}`
- if (isOutputShown) document.getElementById('output').innerHTML += `${msg}<br/>`
- }
- document.getElementById('output').innerHTML += `<hr/>`
- document.getElementById('summary').innerHTML += `${JSON.stringify(average(times, type, effort), null, '\t')}<br/>`
- document.getElementById('status').innerHTML = `TESTING COMPLETE<br/>`
- console.log('%cTESTING COMPLETE', 'color:orange;font-weight:bold')
- }
-
- export async function score (runs, size, difficulty, effort, api) {
- console.log(`%cNanoNaCl`, 'color:green', `Calculate truncated harmonic mean of the truncated arithmetic rate across ${runs} runs of ${size} samples.`)
+ console.log(`%c${api}`, 'color:green', `Calculate truncated harmonic mean of the truncated arithmetic rate of signing random block hashes across ${runs} runs of ${size} samples.`)
const rates = []
+ let start = 0, end = 0, publicKey = null, signature = null, isValid = false
for (let i = 0; i < runs; i++) {
const times = []
for (let j = 0; j < size; j++) {
- document.getElementById('status').innerHTML = `SCORING IN PROGRESS. THIS WILL TAKE A LONG TIME. ${i}/${size} ${j}/${runs}<br/>`
- const hash = random()
- let result = null
- const start = performance.now()
+ document.getElementById('status').innerHTML = `TESTING IN PROGRESS. THIS MAY TAKE A LONG TIME. ${i}/${runs} ${j}/${size}<br/>`
+ const blockHash = random()
+ const privateKey = blockHash
try {
- result = await NanoNaCl.sign(hash, { difficulty, effort, api })
+ start = performance.now()
+ publicKey = await derive(privateKey)
+ signature = await sign(blockHash, privateKey, publicKey)
+ isValid = await verify(blockHash, signature, publicKey)
+ end = performance.now()
} catch (err) {
document.getElementById('output').innerHTML += `Error: ${err.message}<br/>`
console.error(err)
return
}
- times.push(performance.now() - start)
+ if (!isValid) {
+ document.getElementById('status').innerHTML = `ERROR: invalid result\nblock hash: ${blockHash}\nsignature: ${signature}\npublic key: ${publicKey}`
+ return
+ }
+ if (!start || !end || start > end) {
+ document.getElementById('status').innerHTML = `ERROR: invalid timing\nstart: ${start}\nend: ${end}`
+ return
+ }
+ times.push(end - start)
}
- const results = Object.values(average(times))[0]
- const { truncatedRate } = results
+ const avg = average(times, api)
+ const result = Object.values(avg)[0]
+ const { truncatedRate } = result
rates.push(truncatedRate)
- document.getElementById('output').innerHTML += `Benchmark ${i + 1} score: ${truncatedRate} wps<br/>`
+ if (isDebug) document.getElementById('output').innerHTML += `Benchmark ${i + 1} score: ${truncatedRate} ops<br/>`
+ if (isDebug) document.getElementById('summary').innerHTML += `${JSON.stringify(average(times, api), null, '\t')}<br/>`
}
- const results = Object.values(average(rates))[0]
- console.log(rates)
- console.log(results)
+ const results = Object.values(average(rates, api))[0]
const { truncatedHarmonic } = results
document.getElementById('output').innerHTML += `<hr/>`
- document.getElementById('summary').innerHTML += `work-per-second: ${truncatedHarmonic}<br/>`
- document.getElementById('status').innerHTML = `SCORING COMPLETE<br/>`
- console.log('%cSCORING COMPLETE', 'color:orange;font-weight:bold')
+ document.getElementById('summary').innerHTML += `${api} derive-sign-verify: ${truncatedHarmonic} per second<br/>`
+ document.getElementById('status').innerHTML = `TESTING COMPLETE<br/>`
+ console.log('%cTESTING COMPLETE', 'color:orange;font-weight:bold')
}
function startValidation (event) {
- const difficulty = document.getElementById('difficulty')?.value
- const work = document.getElementById('work')?.value
- const hash = document.getElementById('hash')?.value
- const validation = document.getElementById('validation')
const api = document.getElementById('api')?.value
- const apiContainer = document.getElementById('api')?.parentElement
- if (api === 'CPU') {
- apiContainer.classList.add('warning')
- apiContainer.title = 'Use only for testing with very low difficulty.'
- } else {
- apiContainer.classList.remove('warning')
- apiContainer.title = ''
- }
+ const signature = document.getElementById('signature')?.value
+ const blockHash = document.getElementById('blockHash')?.value
+ const publicKey = document.getElementById('publicKey')?.value
+ const validation = document.getElementById('validation')
validation.innerText = '⏳'
- if (work.length === 16 && hash.length === 64) {
- NanoNaCl.verify(work, hash, { difficulty })
+ if (signature.length === 128 && blockHash.length === 64 && publicKey.length === 64) {
+ NanoNaCl.verify(blockHash, signature, publicKey)
.then(result => {
validation.innerText = result
? '✔️'
})
}
}
- document.getElementById('difficulty').addEventListener('input', startValidation)
- document.getElementById('work').addEventListener('input', startValidation)
- document.getElementById('hash').addEventListener('input', startValidation)
document.getElementById('api').addEventListener('input', startValidation)
+ document.getElementById('blockHash').addEventListener('input', startValidation)
+ document.getElementById('signature').addEventListener('input', startValidation)
+ document.getElementById('publicKey').addEventListener('input', startValidation)
function startTest (event) {
event.target.disabled = true
- const size = document.getElementById('size')
- const difficulty = document.getElementById('difficulty')
- const effort = document.getElementById('effort')
const api = document.getElementById('api')
- const isOutputShown = document.getElementById('isOutputShown')
+ const size = document.getElementById('size')
+ const runs = document.getElementById('runs')
const isDebug = document.getElementById('isDebug')
const isSelfCheck = document.getElementById('isSelfCheck')
- run(+size.value, difficulty.value, +effort.value, api.value, isOutputShown.checked, isDebug.checked, isSelfCheck.checked)
+ test(api.value, +size.value, +runs.value, isDebug.checked, isSelfCheck.checked)
.then(() => {
event.target.disabled = false
isSelfCheck.checked = false
})
}
- function startScore (event) {
- event.target.disabled = true
- const runs = document.getElementById('runs')
- const size = document.getElementById('size')
- const difficulty = document.getElementById('difficulty')
- const effort = document.getElementById('effort')
- const api = document.getElementById('api')
- const isOutputShown = document.getElementById('isOutputShown')
- score(+runs.value, +size.value, difficulty.value, +effort.value, api.value)
- .then(() => event.target.disabled = false)
- }
-
document.getElementById('btnStartTest').addEventListener('click', startTest)
- document.getElementById('btnStartScore').addEventListener('click', startScore)
- document.getElementById('effort').value = Math.max(1, Math.floor(navigator.hardwareConcurrency) / 2)
- */
</script>
<style>
body{background:black;color:white;}a{color:darkcyan;}input[type=number]{width:5em;}input[type=checkbox]{margin-right:2em;}
- div.hex{display:inline-block;}
- div.hex::before{color:grey;content:'0x';display:inline-block;font-size:90%;left:0.5em;position:relative;width:0;}
- div.hex>input{padding-left:1.5em;}
+ div.hex{display:inline-block;font-family:monospace;}
+ div.hex::before{color:grey;content:'0x';display:inline-block;font-family:monospace;font-size:90%;left:0.5em;position:relative;width:0;}
+ div.hex>input{font-family:monospace;padding-left:1.5em;}
.warning::before{content:'⚠️';}
</style>
</head>
<h2>Speed test for NanoNaCl tool.</h2>
<p>Times below are in milliseconds and are summarized by various averaging methods.</p>
<hr />
- <label for="work">Validate Work</label>
- <div class="hex"><input id="work" type="text" /></div>
- <label for="hash" class="hex">Hash</label>
- <div class="hex"><input id="hash" type="text" /></div>
+ <label for="signature">Verify Signature</label>
+ <div class="hex"><input id="signature" type="text" /></div>
+ <label for="blockHash" class="hex">Block Hash</label>
+ <div class="hex"><input id="blockHash" type="text" /></div>
+ <label for="publicKey" class="hex">Public Key</label>
+ <div class="hex"><input id="publicKey" type="text" /></div>
<span id="validation"></span>
<hr />
- <h3>Options</h3>
- <label for="difficulty" class="hex">Difficulty</label>
- <div class="hex"><input id="difficulty" type="text" value="FFFFFFF800000000" /></div>
- <label for="size">Test Size</label>
- <input id="size" type="number" value="1" autofocus />
- <label for="effort">Effort (1-32)</label>
- <input id="effort" type="number" min="1" max="32" />
+ <h3>Benchmarking</h3>
<span>
<label for="api">API</label>
<select id="api">
- <option>WebGPU</option>
- <option>WebGL</option>
- <option>WASM</option>
- <option>CPU</option>
+ <option>NanoNaCl</option>
+ <option>NanocurrencyWeb</option>
+ <option>TweetNaCl.js</option>
</select>
</span>
- <hr />
- <h3>Benchmarking</h3>
- <span>
- <label for="isOutputShown">Show output?</label>
- <input id="isOutputShown" type="checkbox" checked />
- </span>
+ <label for="size">Test Size</label>
+ <input id="size" type="number" value="100" autofocus />
+ <label for="runs">Test Runs</label>
+ <input id="runs" type="number" value="10" autofocus />
<span>
<label for="isDebug">Debug?</label>
<input id="isDebug" type="checkbox" />
</span>
<button id="btnStartTest">Go</button>
<hr />
- <h3>Scoring</h3>
- <label for="runs">Score Runs</label>
- <input id="runs" type="number" value="1" autofocus />
- <button id="btnStartScore">Score ❗</button>
- <hr />
<h3 id="status">WAITING</h3>
<hr />
<pre id="summary"></pre>
//! SPDX-License-Identifier: GPL-3.0-or-later
import nacl from './build/nano-nacl.wasm'
-type Derive = (k0: bigint, k1: bigint, k2: bigint, k3: bigint) => number
-type Sign = (h0: bigint, h1: bigint, h2: bigint, h3: bigint, k0: bigint, k1: bigint, k2: bigint, k3: bigint) => number
-type Verify = (h0: bigint, h1: bigint, h2: bigint, h3: bigint, s0: bigint, s1: bigint, s2: bigint, s3: bigint, s4: bigint, s5: bigint, s6: bigint, s7: bigint, k0: bigint, k1: bigint, k2: bigint, k3: bigint) => number
+type Derive = () => void
+type Sign = () => void
+type Verify = () => number
+type GetInputPointer = () => number
+type GetOutputPointer = () => number
type Data = {
action: string
- hash?: string
+ blockHash?: string
privateKey?: string
publicKey?: string
+ secretKey?: string
signature?: string
}
let wasm
let module
let instance
- let derive: (k: BigUint64Array) => Uint8Array<ArrayBuffer>
- let sign: (h: BigUint64Array, k: BigUint64Array) => Uint8Array<ArrayBuffer>
- let verify: (h: BigUint64Array, s: BigUint64Array, k: BigUint64Array) => boolean
let memory: WebAssembly.Memory
+ let derive: (k: Uint8Array) => Uint8Array<ArrayBuffer>
+ let sign: (h: Uint8Array, k: Uint8Array) => Uint8Array<ArrayBuffer>
+ let verify: (h: Uint8Array, s: Uint8Array, k: Uint8Array) => boolean
async function setup (): Promise<void> {
try {
console.error('Wasm abort:', `msg ${msg}`, `file ${file}`, `row ${row}`, `col ${col}`)
throw new Error(`Wasm abort: ${msg}`)
},
+ "performance.now" () {
+ // ~lib/bindings/dom/performance.now() => f64
+ return performance.now()
+ },
trace: (message: any, n?: number, a0?: number, a1?: number, a2?: number, a3?: number, a4?: number) => {
// ~lib/builtins/trace(~lib/string/String, i32?, f64?, f64?, f64?, f64?, f64?) => void
message = __liftString(message >>> 0);
console.log(message, ...[a0, a1, a2, a3, a4].slice(0, n))
})()
},
- memory: new WebAssembly.Memory({ initial: 256, maximum: 1024 })
+ memory: new WebAssembly.Memory({ initial: 1, maximum: 8 })
}
})
- const { exports } = instance as { exports: { derive: Derive, sign: Sign, verify: Verify, memory: WebAssembly.Memory } }
+ const { exports } = instance as { exports: { derive: Derive, sign: Sign, verify: Verify, getInputPointer: GetInputPointer, getOutputPointer: GetOutputPointer, memory: WebAssembly.Memory } }
memory = exports.memory
+
function __liftString (pointer: number) {
if (!pointer) return null
const
return string + String.fromCharCode(...memoryU16.subarray(start, end))
}
- function __liftTypedArray (pointer: number) {
- if (!pointer) throw new Error('bad pointer')
- const membuf = new DataView(memory.buffer)
- const offset = membuf.getUint32(pointer + 4, true)
- const length = membuf.getUint32(pointer + 8, true)
- return new Uint8Array(memory.buffer, offset, length).slice()
- }
-
- derive = function (k: BigUint64Array): Uint8Array<ArrayBuffer> {
- // assembly/nano-nacl/derive(u64, u64, u64, u64) => ~lib/typedarray/Uint8Array
- const k0 = k[0] ?? 0n
- const k1 = k[1] ?? 0n
- const k2 = k[2] ?? 0n
- const k3 = k[3] ?? 0n
- return __liftTypedArray(exports.derive(k0, k1, k2, k3) >>> 0)
+ derive = function (k: Uint8Array): Uint8Array<ArrayBuffer> {
+ // assembly/nano-nacl/derive() => void
+ let buffer: DataView | undefined = new DataView(memory.buffer)
+ let inPtr = exports.getInputPointer()
+ for (let i = 0; i < 32; i++) {
+ buffer.setUint8(inPtr + i, k[i])
+ }
+ exports.derive()
+ const outPtr = exports.getOutputPointer()
+ const pk = new Uint8Array(32)
+ for (let i = 0; i < 32; i++) {
+ pk[i] = buffer.getUint8(outPtr + i)
+ }
+ buffer = undefined
+ return pk
}
- sign = function (h: BigUint64Array, k: BigUint64Array): Uint8Array<ArrayBuffer> {
- // assembly/nano-nacl/sign(u64, u64, u64, u64, u64, u64, u64, u64) => ~lib/typedarray/Uint8Array
- const h0 = h[0] ?? 0n
- const h1 = h[1] ?? 0n
- const h2 = h[2] ?? 0n
- const h3 = h[3] ?? 0n
- const k0 = k[0] ?? 0n
- const k1 = k[1] ?? 0n
- const k2 = k[2] ?? 0n
- const k3 = k[3] ?? 0n
- return __liftTypedArray(exports.sign(h0, h1, h2, h3, k0, k1, k2, k3) >>> 0)
+ sign = function (h: Uint8Array, k: Uint8Array): Uint8Array<ArrayBuffer> {
+ // assembly/nano-nacl/sign() => void
+ let buffer: DataView | undefined = new DataView(memory.buffer)
+ let inPtr = exports.getInputPointer()
+ for (let i = 0; i < 32; i++) {
+ buffer.setUint8(inPtr + i, h[i])
+ }
+ inPtr += 32
+ for (let i = 0; i < 64; i++) {
+ buffer.setUint8(inPtr + i, k[i])
+ }
+ exports.sign()
+ const outPtr = exports.getOutputPointer()
+ const s = new Uint8Array(64)
+ for (let i = 0; i < 64; i++) {
+ s[i] = buffer.getUint8(outPtr + i)
+ }
+ buffer = undefined
+ return s
}
- verify = function (h: BigUint64Array, s: BigUint64Array, k: BigUint64Array): boolean {
- // assembly/nano-nacl/sign(u64, u64, u64, u64, u64, u64, u64, u64) => ~lib/typedarray/Uint8Array
- const h0 = h[0] || 0n
- const h1 = h[1] || 0n
- const h2 = h[2] || 0n
- const h3 = h[3] || 0n
- const s0 = s[0] || 0n
- const s1 = s[1] || 0n
- const s2 = s[2] || 0n
- const s3 = s[3] || 0n
- const s4 = s[4] || 0n
- const s5 = s[5] || 0n
- const s6 = s[6] || 0n
- const s7 = s[7] || 0n
- const k0 = k[0] || 0n
- const k1 = k[1] || 0n
- const k2 = k[2] || 0n
- const k3 = k[3] || 0n
- const result = exports.verify(h0, h1, h2, h3, s0, s1, s2, s3, s4, s5, s6, s7, k0, k1, k2, k3)
- return result === 1
+ verify = function (h: Uint8Array, s: Uint8Array, k: Uint8Array): boolean {
+ // assembly/nano-nacl/verify() => bool
+ let buffer: DataView | undefined = new DataView(memory.buffer)
+ let inPtr = exports.getInputPointer()
+ for (let i = 0; i < 32; i++) {
+ buffer.setUint8(inPtr + i, h[i])
+ }
+ inPtr += 32
+ for (let i = 0; i < 64; i++) {
+ buffer.setUint8(inPtr + i, s[i])
+ }
+ inPtr += 64
+ for (let i = 0; i < 32; i++) {
+ buffer.setUint8(inPtr + i, k[i])
+ }
+ const v = exports.verify()
+ buffer = undefined
+ return v === 1
}
isReady = true
}
}
+ function hex2bytes (type: string, byteLength: number, hex?: unknown): Uint8Array<ArrayBuffer> {
+ if (typeof type !== 'string') {
+ throw new TypeError(`hex2bytes(): Invalid type ${type}`)
+ }
+ if (typeof byteLength !== 'number' && (byteLength & 1) !== 0) {
+ throw new TypeError(`hex2bytes(): Invalid byte length ${byteLength}`)
+ }
+ const regex = RegExp(`[a-z0-9]{${byteLength << 1}}`, 'i')
+ if (typeof hex !== 'string' || !regex.test(hex)) {
+ throw new TypeError(`hex2bytes(): Invalid ${type} ${hex}`)
+ }
+ const bytes = new Uint8Array(hex.match(/.{2}/g)?.map(b => parseInt(b, 16)) || [])
+ if (bytes.byteLength !== byteLength) {
+ throw new TypeError(`hex2bytes(): Error parsing ${type} ${bytes}`)
+ }
+ return bytes
+ }
+
async function handleMessage (msg: any): Promise<void> {
let result: any = null
try {
if (!isReady) await setup()
- const hashArray = new BigUint64Array(4)
- const hashView = new DataView(hashArray.buffer)
- const privateKeyArray = new BigUint64Array(4)
- const privateKeyView = new DataView(privateKeyArray.buffer)
- const publicKeyArray = new BigUint64Array(4)
- const publicKeyView = new DataView(publicKeyArray.buffer)
- const signatureArray = new BigUint64Array(8)
- const signatureView = new DataView(signatureArray.buffer)
if (msg.data === 'start') {
result = 'started'
const { action } = data
if (action === 'derive') {
const { privateKey } = data
- if (privateKey == null) {
- throw new TypeError('Private key required to derive public key')
- }
- for (let i = 0; i < privateKey.length; i += 16) {
- const u64 = privateKey.slice(i, i + 16)
- privateKeyView.setBigUint64(i / 2, BigInt(`0x${u64}`))
+ const privateKeyBytes = hex2bytes('private key', 32, privateKey)
+ const publicKeyBytes = derive(privateKeyBytes)
+ if (publicKeyBytes == null || !(publicKeyBytes instanceof Uint8Array) || publicKeyBytes.byteLength != 32) {
+ throw new TypeError('Invalid public key from WASM derive()')
}
- const publicKey = derive(privateKeyArray)
- if (publicKey == null) {
- throw new TypeError('Invalid verification response from WASM')
- }
- result = publicKey
+ result = publicKeyBytes
} else if (action === 'sign') {
- const { hash, privateKey } = data
- if (hash == null) {
- throw new TypeError('Block hash required for signing')
- }
- if (privateKey == null) {
- throw new TypeError('Private key required to sign block hash')
- }
- for (let i = 0; i < hash.length; i += 16) {
- const u64 = hash.slice(i, i + 16)
- hashView.setBigUint64(i / 2, BigInt(`0x${u64}`))
- }
- for (let i = 0; i < privateKey.length; i += 16) {
- const u64 = privateKey.slice(i, i + 16)
- privateKeyView.setBigUint64(i / 2, BigInt(`0x${u64}`))
- }
- const signature = sign(hashArray, privateKeyArray)
+ const { blockHash, secretKey } = data
+ const blockHashBytes = hex2bytes('block hash', 32, blockHash)
+ const secretKeyBytes = hex2bytes('secret key', 64, secretKey)
+ const signature = sign(blockHashBytes, secretKeyBytes)
if (signature == null) {
- throw new TypeError('Invalid signature from WASM')
+ throw new TypeError('Invalid signature from WASM sign()')
}
- console.log('signature', signature)
result = signature
} else if (action === 'verify') {
- const { hash, publicKey, signature } = data
- if (hash == null) {
- throw new TypeError('Hash required to verify signature')
- }
- if (publicKey == null) {
- throw new TypeError('Public key required to verify signature')
- }
- if (signature == null) {
- throw new TypeError('Signature required to verify against public key')
- }
- for (let i = 0; i < hash.length; i += 16) {
- const u64 = hash.slice(i, i + 16)
- hashView.setBigUint64(i / 2, BigInt(`0x${u64}`))
- }
- for (let i = 0; i < signature.length; i += 16) {
- const u64 = signature.slice(i, i + 16)
- signatureView.setBigUint64(i / 2, BigInt(`0x${u64}`))
- }
- for (let i = 0; i < publicKey.length; i += 16) {
- const u64 = publicKey.slice(i, i + 16)
- publicKeyView.setBigUint64(i / 2, BigInt(`0x${u64}`))
- }
- const isVerified = verify(hashArray, signatureArray, publicKeyArray)
+ const { blockHash, publicKey, signature } = data
+ const blockHashBytes = hex2bytes('block hash', 32, blockHash)
+ const signatureBytes = hex2bytes('signature', 64, signature)
+ const publicKeyBytes = hex2bytes('public key', 32, publicKey)
+ const isVerified = verify(blockHashBytes, signatureBytes, publicKeyBytes)
if (isVerified == null) {
- throw new TypeError('Invalid verification response from WASM')
+ throw new TypeError('Invalid verification from WASM verify()')
}
- result = new Uint8Array([isVerified ? 0 : 1])
+ result = new Uint8Array([isVerified ? 1 : 0])
}
}
} catch (err: unknown) {
}
async function init (): Promise<void> {
+ if (!isReady) setup()
return new Promise(async (ready, fail): Promise<void> => {
worker.onmessage = msg => msg.data === 'started' ? ready(msg.data) : fail(msg.data)
worker.onerror = err => fail(err.message)
worker.onerror = reject
worker.onmessage = (msg): void => {
const result = msg.data
- console.log(`received result from worker`, result)
+ LOG: console.log(`received result from worker`)
resolve(result)
}
- console.log(`sending data to worker`)
+ LOG: console.log(`sending data to worker`)
worker.postMessage(JSON.stringify(data))
})
}
worker.onmessage = (msg): void => {
const result = msg.data
if (result === 'stopped') {
- console.log('workers stopped successfully')
+ console.log('worker stopped successfully')
resolve()
} else {
reject(result)
})
}
-/**
-* Nano public key derivation using WebAssembly.
-*/
-export async function derive (privateKey: string): Promise<string> {
- if (isReady === false) setup()
+async function run (data: Record<string, string>): Promise<string> {
try {
await init()
- console.log('Workers ready')
} catch (err) {
console.error(err)
- throw new Error('Error initializing workers')
+ throw new Error('Error initializing worker')
}
- let publicKey = new Uint8Array(0)
try {
- publicKey = await dispatch({ action: 'derive', privateKey })
- } catch (err) {
- console.error(err)
- } finally {
+ const result = await dispatch(data)
+ if (!(result instanceof Uint8Array)) {
+ throw new Error(result)
+ }
+ return [...result].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase()
+ } catch (err: any) {
try {
+ console.error(err)
await stop()
- } catch (err: any) {
- console.error('failed to stop workers', err?.message ?? err ?? 'unknown reason')
+ } catch (e: any) {
+ console.error('failed to stop worker', err?.message ?? err ?? 'unknown reason')
reset()
+ } finally {
+ return ''
}
}
+}
- return [...publicKey].map(b => b.toString(16).padStart(2, '0')).join('')
+/**
+* Nano public key derivation using WebAssembly.
+*/
+export async function derive (privateKey: string): Promise<string> {
+ return await run({ action: 'derive', privateKey })
}
/**
* Nano block signature using WebAssembly.
*/
-export async function sign (hash: string, privateKey: string): Promise<string> {
- if (isReady === false) setup()
- try {
- await init()
- console.log('Workers ready')
- } catch (err) {
- console.error(err)
- throw new Error('Error initializing workers')
- }
-
- let signature = new Uint8Array(0)
- try {
- signature = await dispatch({ action: 'sign', hash, privateKey })
- } catch (err) {
- console.error(err)
- } finally {
- try {
- await stop()
- } catch (err: any) {
- console.error('failed to stop workers', err?.message ?? err ?? 'unknown reason')
- reset()
- }
- }
-
- return [...signature].map(b => b.toString(16).padStart(2, '0')).join('')
+export async function sign (blockHash: string, secretKey: string): Promise<string> {
+ return await run({ action: 'sign', blockHash, secretKey })
}
/**
* Nano block signature verification using WebAssembly.
*/
-export async function verify (hash: string, signature: string, publicKey: string): Promise<boolean> {
- if (isReady === false) setup()
- try {
- await init()
- console.log('Workers ready')
- } catch (err) {
- console.error(err)
- throw new Error('Error initializing workers')
- }
-
- let isValid = new Uint8Array(0)
- try {
- isValid = await dispatch({ action: 'verify', hash, signature, publicKey })
- } catch (err) {
- console.error(err)
- } finally {
- try {
- await stop()
- } catch (err: any) {
- console.error('failed to stop workers', err?.message ?? err ?? 'unknown reason')
- reset()
- }
- }
-
- return isValid[0] === 1
+export async function verify (blockHash: string, signature: string, publicKey: string): Promise<boolean> {
+ const result = await run({ action: 'verify', blockHash, signature, publicKey })
+ return result === '01'
}