From 8d06781c19667604f4dc4b9ba4657191d5931152 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Thu, 28 Nov 2024 01:06:03 -0800 Subject: [PATCH] Rewrite blake2b as class and update references. Add typings and assertions to satisfy TypeScript compiler. --- src/lib/account.ts | 14 +- src/lib/bip39-mnemonic.ts | 2 +- src/lib/blake2b.ts | 289 +++++++++++++++++------------------ src/lib/tools.ts | 17 ++- src/lib/wallet.ts | 10 +- src/lib/workers/nacl-nano.ts | 4 +- test.html | 2 +- 7 files changed, 169 insertions(+), 169 deletions(-) diff --git a/src/lib/account.ts b/src/lib/account.ts index bfe16ec..eefeb20 100644 --- a/src/lib/account.ts +++ b/src/lib/account.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2024 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -import { blake2b } from './blake2b.js' +import { Blake2b } from './blake2b.js' import { ACCOUNT_KEY_LENGTH, ALPHABET, PREFIX, PREFIX_LEGACY } from './constants.js' import { base32, bytes, hex } from './convert.js' import { Rpc } from './rpc.js' @@ -143,8 +143,8 @@ export class Account { const expectedChecksum = address.slice(-8) const keyBase32 = address.slice(address.indexOf('_') + 1, -8) const keyBuf = base32.toBytes(keyBase32) - const actualChecksumBuf = blake2b(5, undefined, undefined, undefined, true) - .update(keyBuf).digest().reverse() + const actualChecksumBuf = new Blake2b(5).update(keyBuf).digest() as Uint8Array + actualChecksumBuf.reverse() const actualChecksum = bytes.toBase32(actualChecksumBuf) if (expectedChecksum !== actualChecksum) { @@ -187,8 +187,8 @@ export class Account { static #addressToKey (v: string): string { const keyBytes = base32.toBytes(v.substring(0, 52)) const checksumBytes = base32.toBytes(v.substring(52, 60)) - const blakeHash = blake2b(5, undefined, undefined, undefined, true) - .update(keyBytes).digest().reverse() + const blakeHash = new Blake2b(5).update(keyBytes).digest() as Uint8Array + blakeHash.reverse() if (bytes.toHex(checksumBytes) !== bytes.toHex(blakeHash)) { throw new Error('Checksum mismatch in address') } @@ -197,8 +197,8 @@ export class Account { static async #keyToAddress (key: string): Promise { const publicKeyBytes = hex.toBytes(key) - const checksum = blake2b(5, undefined, undefined, undefined, true) - .update(publicKeyBytes).digest().reverse() + const checksum = new Blake2b(5).update(publicKeyBytes).digest() as Uint8Array + checksum.reverse() const encoded = bytes.toBase32(publicKeyBytes) const encodedChecksum = bytes.toBase32(checksum) return `${PREFIX}${encoded}${encodedChecksum}` diff --git a/src/lib/bip39-mnemonic.ts b/src/lib/bip39-mnemonic.ts index 70d3996..4ae0b6d 100644 --- a/src/lib/bip39-mnemonic.ts +++ b/src/lib/bip39-mnemonic.ts @@ -59,7 +59,7 @@ export class Bip39Mnemonic { const e = new Entropy(entropy) const checksum = await this.checksum(e) let concatenation = `${e.bits}${checksum}` - const words = [] + const words: string[] = [] while (concatenation.length > 0) { const wordBits = concatenation.substring(0, 11) const wordIndex = parseInt(wordBits, 2) diff --git a/src/lib/blake2b.ts b/src/lib/blake2b.ts index 6b8e634..4bc4032 100644 --- a/src/lib/blake2b.ts +++ b/src/lib/blake2b.ts @@ -1,54 +1,33 @@ // SPDX-FileCopyrightText: 2024 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later -'use strict' - /** * Implementation derived from blake2b@2.1.4. Copyright 2017 Emil Bay * (https://github.com/emilbayes/blake2b). See LICENSES/ISC.txt * -* Modified to eliminate dependencies and port to TypeScript. +* Modified to eliminate dependencies, port to TypeScript, and embed in web +* workers. * * Original source commit: https://github.com/emilbayes/blake2b/blob/1f63e02e3f226642959506cdaa67c8819ff145cd/index.js */ -function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal?: Uint8Array, noAssert?: boolean) { - const BYTES_MIN = 16 - const BYTES_MAX = 64 - const KEYBYTES_MIN = 16 - const KEYBYTES_MAX = 64 - const SALTBYTES = 16 - const PERSONALBYTES = 16 - - // reusable parameter_block - const parameter_block = new Uint8Array([ - 0, 0, 0, 0, // 0: outlen, keylen, fanout, depth - 0, 0, 0, 0, // 4: leaf length, sequential mode - 0, 0, 0, 0, // 8: node offset - 0, 0, 0, 0, // 12: node offset - 0, 0, 0, 0, // 16: node depth, inner length, rfu - 0, 0, 0, 0, // 20: rfu - 0, 0, 0, 0, // 24: rfu - 0, 0, 0, 0, // 28: rfu - 0, 0, 0, 0, // 32: salt - 0, 0, 0, 0, // 36: salt - 0, 0, 0, 0, // 40: salt - 0, 0, 0, 0, // 44: salt - 0, 0, 0, 0, // 48: personal - 0, 0, 0, 0, // 52: personal - 0, 0, 0, 0, // 56: personal - 0, 0, 0, 0 // 60: personal - ]) +export class Blake2b { + static BYTES_MIN = 1 + static BYTES_MAX = 64 + static KEYBYTES_MIN = 16 + static KEYBYTES_MAX = 64 + static SALTBYTES = 16 + static PERSONALBYTES = 16 // Initialization Vector - const BLAKE2B_IV32 = new Uint32Array([ + static BLAKE2B_IV32 = new Uint32Array([ 0xF3BCC908, 0x6A09E667, 0x84CAA73B, 0xBB67AE85, 0xFE94F82B, 0x3C6EF372, 0x5F1D36F1, 0xA54FF53A, 0xADE682D1, 0x510E527F, 0x2B3E6C1F, 0x9B05688C, 0xFB41BD6B, 0x1F83D9AB, 0x137E2179, 0x5BE0CD19 ]) - const SIGMA8 = [ + static SIGMA8 = [ 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, @@ -68,17 +47,38 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? * Multiply them all by 2 to make them offsets into a uint32 buffer, * because this is Javascript and we don't have uint64s */ - const SIGMA82 = new Uint8Array(SIGMA8.map(x => x * 2)) + static SIGMA82 = new Uint8Array(Blake2b.SIGMA8.map(x => x * 2)) + + // reusable parameter_block + static parameter_block = new Uint8Array([ + 0, 0, 0, 0, // 0: outlen, keylen, fanout, depth + 0, 0, 0, 0, // 4: leaf length, sequential mode + 0, 0, 0, 0, // 8: node offset + 0, 0, 0, 0, // 12: node offset + 0, 0, 0, 0, // 16: node depth, inner length, rfu + 0, 0, 0, 0, // 20: rfu + 0, 0, 0, 0, // 24: rfu + 0, 0, 0, 0, // 28: rfu + 0, 0, 0, 0, // 32: salt + 0, 0, 0, 0, // 36: salt + 0, 0, 0, 0, // 40: salt + 0, 0, 0, 0, // 44: salt + 0, 0, 0, 0, // 48: personal + 0, 0, 0, 0, // 52: personal + 0, 0, 0, 0, // 56: personal + 0, 0, 0, 0 // 60: personal + ]) + - let v = new Uint32Array(32) - let m = new Uint32Array(32) + static v = new Uint32Array(32) + static m = new Uint32Array(32) /** * 64-bit unsigned addition * Sets v[a,a+1] += v[b,b+1] * v should be a Uint32Array */ - const ADD64AA = function (v: Uint32Array, a, b) { + static ADD64AA (v: Uint32Array, a: number, b: number) { var o0 = v[a] + v[b] var o1 = v[a + 1] + v[b + 1] if (o0 >= 0x100000000) { @@ -93,7 +93,7 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? * Sets v[a,a+1] += b * b0 is the low 32 bits of b, b1 represents the high 32 bits */ - const ADD64AC = function (v, a, b0, b1) { + static ADD64AC (v: Uint32Array, a: number, b0: number, b1: number) { var o0 = v[a] + b0 if (b0 < 0) { o0 += 0x100000000 @@ -107,7 +107,7 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? } // Little-endian byte access - const B2B_GET32 = function (arr, i) { + static B2B_GET32 (arr: Uint8Array, i: number) { return (arr[i] ^ (arr[i + 1] << 8) ^ (arr[i + 2] << 16) ^ @@ -118,120 +118,102 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? * G Mixing function * The ROTRs are inlined for speed */ - const B2B_G = function (a, b, c, d, ix, iy) { - var x0 = m[ix] - var x1 = m[ix + 1] - var y0 = m[iy] - var y1 = m[iy + 1] + static B2B_G (a: number, b: number, c: number, d: number, ix: number, iy: number) { + var x0 = Blake2b.m[ix] + var x1 = Blake2b.m[ix + 1] + var y0 = Blake2b.m[iy] + var y1 = Blake2b.m[iy + 1] - ADD64AA(v, a, b) // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s - ADD64AC(v, a, x0, x1) // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits + Blake2b.ADD64AA(Blake2b.v, a, b) // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s + Blake2b.ADD64AC(Blake2b.v, a, x0, x1) // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits - var xor0 = v[d] ^ v[a] - var xor1 = v[d + 1] ^ v[a + 1] - v[d] = xor1 - v[d + 1] = xor0 + var xor0 = Blake2b.v[d] ^ Blake2b.v[a] + var xor1 = Blake2b.v[d + 1] ^ Blake2b.v[a + 1] + Blake2b.v[d] = xor1 + Blake2b.v[d + 1] = xor0 - ADD64AA(v, c, d) + Blake2b.ADD64AA(Blake2b.v, c, d) // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits - xor0 = v[b] ^ v[c] - xor1 = v[b + 1] ^ v[c + 1] - v[b] = (xor0 >>> 24) ^ (xor1 << 8) - v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8) + xor0 = Blake2b.v[b] ^ Blake2b.v[c] + xor1 = Blake2b.v[b + 1] ^ Blake2b.v[c + 1] + Blake2b.v[b] = (xor0 >>> 24) ^ (xor1 << 8) + Blake2b.v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8) - ADD64AA(v, a, b) - ADD64AC(v, a, y0, y1) + Blake2b.ADD64AA(Blake2b.v, a, b) + Blake2b.ADD64AC(Blake2b.v, a, y0, y1) // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits - xor0 = v[d] ^ v[a] - xor1 = v[d + 1] ^ v[a + 1] - v[d] = (xor0 >>> 16) ^ (xor1 << 16) - v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16) + xor0 = Blake2b.v[d] ^ Blake2b.v[a] + xor1 = Blake2b.v[d + 1] ^ Blake2b.v[a + 1] + Blake2b.v[d] = (xor0 >>> 16) ^ (xor1 << 16) + Blake2b.v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16) - ADD64AA(v, c, d) + Blake2b.ADD64AA(Blake2b.v, c, d) // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits - xor0 = v[b] ^ v[c] - xor1 = v[b + 1] ^ v[c + 1] - v[b] = (xor1 >>> 31) ^ (xor0 << 1) - v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1) + xor0 = Blake2b.v[b] ^ Blake2b.v[c] + xor1 = Blake2b.v[b + 1] ^ Blake2b.v[c + 1] + Blake2b.v[b] = (xor1 >>> 31) ^ (xor0 << 1) + Blake2b.v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1) } /** * Compression function. 'last' flag indicates last block. * Note we're representing 16 uint64s as 32 uint32s */ - const blake2bCompress = function (ctx, last) { + static blake2bCompress (ctx: any, last: boolean) { var i = 0 // init work variables for (i = 0; i < 16; i++) { - v[i] = ctx.h[i] - v[i + 16] = BLAKE2B_IV32[i] + Blake2b.v[i] = ctx.h[i] + Blake2b.v[i + 16] = Blake2b.BLAKE2B_IV32[i] } // low 64 bits of offset - v[24] = v[24] ^ ctx.t - v[25] = v[25] ^ (ctx.t / 0x100000000) + Blake2b.v[24] = Blake2b.v[24] ^ ctx.t + Blake2b.v[25] = Blake2b.v[25] ^ (ctx.t / 0x100000000) // high 64 bits not supported, offset may not be higher than 2**53-1 // last block flag set ? if (last) { - v[28] = ~v[28] - v[29] = ~v[29] + Blake2b.v[28] = ~Blake2b.v[28] + Blake2b.v[29] = ~Blake2b.v[29] } // get little-endian words for (i = 0; i < 32; i++) { - m[i] = B2B_GET32(ctx.b, 4 * i) + Blake2b.m[i] = Blake2b.B2B_GET32(ctx.b, 4 * i) } // twelve rounds of mixing for (i = 0; i < 12; i++) { - B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1]) - B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3]) - B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5]) - B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7]) - B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9]) - B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11]) - B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13]) - B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15]) + Blake2b.B2B_G(0, 8, 16, 24, Blake2b.SIGMA82[i * 16 + 0], Blake2b.SIGMA82[i * 16 + 1]) + Blake2b.B2B_G(2, 10, 18, 26, Blake2b.SIGMA82[i * 16 + 2], Blake2b.SIGMA82[i * 16 + 3]) + Blake2b.B2B_G(4, 12, 20, 28, Blake2b.SIGMA82[i * 16 + 4], Blake2b.SIGMA82[i * 16 + 5]) + Blake2b.B2B_G(6, 14, 22, 30, Blake2b.SIGMA82[i * 16 + 6], Blake2b.SIGMA82[i * 16 + 7]) + Blake2b.B2B_G(0, 10, 20, 30, Blake2b.SIGMA82[i * 16 + 8], Blake2b.SIGMA82[i * 16 + 9]) + Blake2b.B2B_G(2, 12, 22, 24, Blake2b.SIGMA82[i * 16 + 10], Blake2b.SIGMA82[i * 16 + 11]) + Blake2b.B2B_G(4, 14, 16, 26, Blake2b.SIGMA82[i * 16 + 12], Blake2b.SIGMA82[i * 16 + 13]) + Blake2b.B2B_G(6, 8, 18, 28, Blake2b.SIGMA82[i * 16 + 14], Blake2b.SIGMA82[i * 16 + 15]) } for (i = 0; i < 16; i++) { - ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16] + ctx.h[i] = ctx.h[i] ^ Blake2b.v[i] ^ Blake2b.v[i + 16] } } - const update = function (input: Uint8Array) { - if (!(input instanceof Uint8Array)) - throw new TypeError(`input must be Uint8Array or Buffer`) - blake2bUpdate(this, input) - return { "update": update.bind(this), "digest": digest.bind(this) } - } - - function digest (out: 'hex'): string - function digest (out?: 'binary' | Uint8Array): Uint8Array - function digest (out?: 'binary' | 'hex' | Uint8Array): string | Uint8Array { - var buf = (!out || out === 'binary' || out === 'hex') ? new Uint8Array(outlen) : out - if (!(buf instanceof Uint8Array)) throw new TypeError(`out must be "binary", "hex", Uint8Array, or Buffer`) - if (buf.length < outlen) throw new RangeError(`out must have at least outlen bytes of space`) - blake2bFinal(this, buf) - if (out === 'hex') return hexSlice(buf) - return buf - } - /** * Updates a BLAKE2b streaming hash * Requires hash context and Uint8Array (byte array) */ - const blake2bUpdate = function (ctx, input: Uint8Array) { + static blake2bUpdate (ctx: any, input: Uint8Array) { for (var i = 0; i < input.length; i++) { if (ctx.c === 128) { // buffer full ? ctx.t += ctx.c // add counters - blake2bCompress(ctx, false) // compress (not last) + Blake2b.blake2bCompress(ctx, false) // compress (not last) ctx.c = 0 // counter to zero } ctx.b[ctx.c++] = input[i] @@ -242,13 +224,13 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? * Completes a BLAKE2b streaming hash * Returns a Uint8Array containing the message digest */ - const blake2bFinal = function (ctx, out: Uint8Array) { + static blake2bFinal (ctx: any, out: Uint8Array) { ctx.t += ctx.c // mark last block offset while (ctx.c < 128) { // fill up with zeros ctx.b[ctx.c++] = 0 } - blake2bCompress(ctx, true) // final block flag = 1 + Blake2b.blake2bCompress(ctx, true) // final block flag = 1 for (var i = 0; i < ctx.outlen; i++) { out[i] = ctx.h[i >> 2] >> (8 * (i & 3)) @@ -256,13 +238,13 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? return out } - const hexSlice = function (buf: Uint8Array) { + static hexSlice (buf: Uint8Array) { var str = '' - for (var i = 0; i < buf.length; i++) str += toHex(buf[i]) + for (var i = 0; i < buf.length; i++) str += Blake2b.toHex(buf[i]) return str } - const toHex = function (n: number) { + static toHex (n: number) { if (typeof n !== 'number') throw new TypeError(`expected number to convert to hex; received ${typeof n}`) if (n < 0 || n > 255) @@ -270,65 +252,82 @@ function blake2b (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal? return n.toString(16).padStart(2, '0') } + b: Uint8Array + h: Uint32Array + t: number + c: number + outlen: number + /** * Creates a BLAKE2b hashing context * Requires an output length between 1 and 64 bytes * Takes an optional Uint8Array key */ - const Blake2b = function (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal?: Uint8Array) { + constructor (outlen: number, key?: Uint8Array, salt?: Uint8Array, personal?: Uint8Array, noAssert?: boolean) { + if (noAssert !== true) { + if (outlen < Blake2b.BYTES_MIN) throw new RangeError(`expected outlen >= ${Blake2b.BYTES_MIN}; actual ${outlen}`) + if (outlen > Blake2b.BYTES_MAX) throw new RangeError(`expectd outlen <= ${Blake2b.BYTES_MAX}; actual ${outlen}`) + if (key != null) { + if (!(key instanceof Uint8Array)) throw new TypeError(`key must be Uint8Array or Buffer`) + if (key.length < Blake2b.KEYBYTES_MIN) throw new RangeError(`expected key >= ${Blake2b.KEYBYTES_MIN}; actual ${key.length}`) + if (key.length > Blake2b.KEYBYTES_MAX) throw new RangeError(`expected key <= ${Blake2b.KEYBYTES_MAX}; actual ${key.length}`) + } + if (salt != null) { + if (!(salt instanceof Uint8Array)) throw new TypeError(`salt must be Uint8Array or Buffer`) + if (salt.length !== Blake2b.SALTBYTES) throw new RangeError(`expected salt ${Blake2b.SALTBYTES} bytes; actual ${salt.length} bytes`) + } + if (personal != null) { + if (!(personal instanceof Uint8Array)) throw new TypeError(`personal must be Uint8Array or Buffer`) + if (personal.length !== Blake2b.PERSONALBYTES) throw new RangeError(`expected personal ${Blake2b.PERSONALBYTES} bytes; actual ${personal.length} bytes`) + } + } + + this.b = new Uint8Array(128) + this.h = new Uint32Array(16) + this.t = 0 // input count + this.c = 0 // pointer within buffer + this.outlen = outlen // output length in bytes + // zero out parameter_block before usage - parameter_block.fill(0) + Blake2b.parameter_block.fill(0) // state, 'param block' - let b = new Uint8Array(128) - let h = new Uint32Array(16) - let t = 0 // input count - let c = 0 // pointer within buffer - // let outlen = outlen // output length in bytes - - parameter_block[0] = outlen - if (key) parameter_block[1] = key.length - parameter_block[2] = 1 // fanout - parameter_block[3] = 1 // depth + Blake2b.parameter_block[0] = outlen + if (key) Blake2b.parameter_block[1] = key.length + Blake2b.parameter_block[2] = 1 // fanout + Blake2b.parameter_block[3] = 1 // depth - if (salt) parameter_block.set(salt, 32) - if (personal) parameter_block.set(personal, 48) + if (salt) Blake2b.parameter_block.set(salt, 32) + if (personal) Blake2b.parameter_block.set(personal, 48) // initialize hash state for (var i = 0; i < 16; i++) { - h[i] = BLAKE2B_IV32[i] ^ B2B_GET32(parameter_block, i * 4) + this.h[i] = Blake2b.BLAKE2B_IV32[i] ^ Blake2b.B2B_GET32(Blake2b.parameter_block, i * 4) } // key the hash, if applicable if (key) { - blake2bUpdate({ b, h, t, c, outlen }, key) + Blake2b.blake2bUpdate(this, key) // at the end - c = 128 + this.c = 128 } - return { "update": update.bind({ b, h, t, c, outlen }), "digest": digest.bind({ b, h, t, c, outlen }) } } - // finally execute the original function call - if (noAssert !== true) { - if (outlen < BYTES_MIN) throw new RangeError(`expected outlen >= ${BYTES_MIN}; actual ${outlen}`) - if (outlen > BYTES_MAX) throw new RangeError(`expectd outlen <= ${BYTES_MAX}; actual ${outlen}`) - if (key != null) { - if (!(key instanceof Uint8Array)) throw new TypeError(`key must be Uint8Array or Buffer`) - if (key.length < KEYBYTES_MIN) throw new RangeError(`expected key >= ${KEYBYTES_MIN}; actual ${key.length}`) - if (key.length > KEYBYTES_MAX) throw new RangeError(`expected key <= ${KEYBYTES_MAX}; actual ${key.length}`) - } - if (salt != null) { - if (!(salt instanceof Uint8Array)) throw new TypeError(`salt must be Uint8Array or Buffer`) - if (salt.length !== SALTBYTES) throw new RangeError(`expected salt ${SALTBYTES} bytes; actual ${salt.length} bytes`) - } - if (personal != null) { - if (!(personal instanceof Uint8Array)) throw new TypeError(`personal must be Uint8Array or Buffer`) - if (personal.length !== PERSONALBYTES) throw new RangeError(`expected personal ${PERSONALBYTES} bytes; actual ${personal.length} bytes`) - } + update (input: Uint8Array) { + if (!(input instanceof Uint8Array)) + throw new TypeError(`input must be Uint8Array or Buffer`) + Blake2b.blake2bUpdate(this, input) + return this } - return Blake2b(outlen, key, salt, personal) + digest (out?: 'binary' | 'hex' | Uint8Array): string | Uint8Array { + var buf = (!out || out === 'binary' || out === 'hex') ? new Uint8Array(this.outlen) : out + if (!(buf instanceof Uint8Array)) throw new TypeError(`out must be "binary", "hex", Uint8Array, or Buffer`) + if (buf.length < this.outlen) throw new RangeError(`out must have at least outlen bytes of space`) + Blake2b.blake2bFinal(this, buf) + if (out === 'hex') return Blake2b.hexSlice(buf) as string + return buf + } } -export { blake2b } -export default blake2b.toString() +export default Blake2b.toString() diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 8ad449e..fbd1be8 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import { Account } from './account.js' -import { blake2b } from './blake2b.js' +import { Blake2b } from './blake2b.js' import { UNITS } from './constants.js' import { bytes, hex } from './convert.js' import { Rpc } from './rpc.js' @@ -68,14 +68,15 @@ export async function convert (amount: bigint | string, inputUnit: string, outpu */ export async function hash (data: string | string[], encoding?: 'hex'): Promise { if (!Array.isArray(data)) data = [data] - const hash = blake2b(32) + const stream = new Blake2b(32) if (encoding === 'hex') { - data.forEach(str => hash.update(hex.toBytes(str))) + data.forEach(str => stream.update(hex.toBytes(str))) } else { const enc = new TextEncoder() - data.forEach(str => hash.update(enc.encode(str))) + data.forEach(str => stream.update(enc.encode(str))) } - return hash.digest('hex').toUpperCase() + const hash = stream.digest('hex') as string + return hash.toUpperCase() } /** @@ -115,7 +116,7 @@ export async function sweep (rpc: Rpc | string | URL, wallet: Blake2bWallet | Bi if (rpc.constructor !== Rpc) { throw new TypeError('RPC must be a valid node') } - const blockQueue = [] + const blockQueue: Promise[] = [] const results: { status: 'success' | 'error', address: string, message: string }[] = [] const recipientAccount = new Account(recipient) @@ -130,7 +131,7 @@ export async function sweep (rpc: Rpc | string | URL, wallet: Blake2bWallet | Bi account.representative.address, account.frontier ) - const blockRequest = new Promise(async (resolve) => { + const blockRequest: Promise = new Promise(async (resolve) => { try { await block.pow(rpc) await block.sign(account.index) @@ -139,7 +140,7 @@ export async function sweep (rpc: Rpc | string | URL, wallet: Blake2bWallet | Bi } catch (err: any) { results.push({ status: 'error', address: block.account.address, message: err.message }) } finally { - resolve(null) + resolve() } }) blockQueue.push(blockRequest) diff --git a/src/lib/wallet.ts b/src/lib/wallet.ts index e39277d..71d393f 100644 --- a/src/lib/wallet.ts +++ b/src/lib/wallet.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import { Account } from './account.js' -import { blake2b } from './blake2b.js' +import { Blake2b } from './blake2b.js' import { Bip39Mnemonic } from './bip39-mnemonic.js' import { ADDRESS_GAP, SEED_LENGTH_BIP44, SEED_LENGTH_BLAKE2B } from './constants.js' import { Entropy } from './entropy.js' @@ -80,7 +80,7 @@ abstract class Wallet { from = to to = swap } - const indexes = [] + const indexes: number[] = [] for (let i = from; i <= to; i++) { if (this.#accounts[i] == null) { indexes.push(i) @@ -422,7 +422,7 @@ export class Bip44Wallet extends Wallet { let now = performance.now() const results: [{ index: number, key: string }] = await this.#pool.work(data) console.log(`ckd: ${-now + (now = performance.now())} ms`) - const accounts = [] + const accounts: Account[] = [] for (const result of results) { const { index, key } = result if (typeof key !== 'string') { @@ -580,11 +580,11 @@ export class Blake2bWallet extends Wallet { const inputHex = `${this.seed}${indexHex}`.padStart(72, '0') const inputArray = (inputHex.match(/.{1,2}/g) ?? []).map(h => parseInt(h, 16)) const inputBytes = Uint8Array.from(inputArray) - const key = blake2b(32).update(inputBytes).digest('hex') + const key: string = new Blake2b(32).update(inputBytes).digest('hex') as string return { key, index } }) console.log(`ckd: ${-now + (now = performance.now())} ms`) - const accounts = [] + const accounts: Account[] = [] for (const result of results) { const { key, index } = result if (typeof key !== 'string') { diff --git a/src/lib/workers/nacl-nano.ts b/src/lib/workers/nacl-nano.ts index a2c190f..41b850e 100644 --- a/src/lib/workers/nacl-nano.ts +++ b/src/lib/workers/nacl-nano.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2024 Chris Duncan // SPDX-License-Identifier: GPL-3.0-or-later //@ts-nocheck -import { blake2b } from '../blake2b.js' +import { Blake2b } from '../blake2b.js' 'use strict'; @@ -522,7 +522,7 @@ function crypto_hash(out, m, n) { for (let i = 0; i < n; ++i) { input[i] = m[i] } - const hash = blake2b(64).update(input).digest() + const hash = new Blake2b(64).update(input).digest() for (let i = 0; i < 64; ++i) { out[i] = hash[i] } diff --git a/test.html b/test.html index 8a29fdc..1f13a6e 100644 --- a/test.html +++ b/test.html @@ -17,7 +17,7 @@ const blakeWallet = await libnemo.Blake2bWallet.create('test') await blakeWallet.unlock('test') console.log(blakeWallet.mnemonic) - const blakeAccounts = await blakeWallet.accounts(0, 0xffff) + const blakeAccounts = await blakeWallet.accounts(0, 0x2000) const blakeAccount = blakeAccounts[0] console.log(blakeAccount.privateKey) console.log(blakeAccount.publicKey) -- 2.34.1