]> zoso.dev Git - libnemo.git/commitdiff
Integrate blake2b package, replacing blakejs and blake2b-wasm. Restore arrow notation...
authorChris Duncan <chris@zoso.dev>
Tue, 19 Nov 2024 06:55:55 +0000 (22:55 -0800)
committerChris Duncan <chris@zoso.dev>
Tue, 19 Nov 2024 06:55:55 +0000 (22:55 -0800)
package-lock.json
package.json
src/lib/account.ts
src/lib/ckd.ts [deleted file]
src/lib/curve25519.ts
src/lib/ed25519.ts
src/lib/safe.ts
src/lib/thread.ts [deleted file]
src/lib/tools.ts
src/lib/wallet.ts
test/derive-accounts.test.mjs

index 2b136a8fc47df93e2725ac87086b1a9ff167e742..592337e6eef6281758de859afc5b86dc96b4d62a 100644 (file)
@@ -9,12 +9,10 @@
                        "version": "0.0.19",
                        "license": "(GPL-3.0-or-later AND MIT)",
                        "dependencies": {
-                               "blake2b": "^2.1.4",
-                               "blake2b-wasm": "^2.4.0",
-                               "blakejs": "^1.2.1"
+                               "blake2b": "^2.1.4"
                        },
                        "devDependencies": {
-                               "@types/blake2b-wasm": "^2.4.3",
+                               "@types/blake2b": "^2.1.3",
                                "@types/node": "^22.8.6",
                                "@types/w3c-web-hid": "^1.0.6",
                                "@types/w3c-web-usb": "^1.0.10",
                        "license": "Apache-2.0",
                        "optional": true
                },
-               "node_modules/@types/blake2b-wasm": {
-                       "version": "2.4.3",
-                       "resolved": "https://registry.npmjs.org/@types/blake2b-wasm/-/blake2b-wasm-2.4.3.tgz",
-                       "integrity": "sha512-emsOJOuF5shxg5zhN3CHOy4BO/a26O++yk0ncFW9fePquKSGs1g6PIps8u8zFmApJjIkMQr7neVUqvoic4BRFw==",
+               "node_modules/@types/blake2b": {
+                       "version": "2.1.3",
+                       "resolved": "https://registry.npmjs.org/@types/blake2b/-/blake2b-2.1.3.tgz",
+                       "integrity": "sha512-MFCdX0MNxFBP/xEILO5Td0kv6nI7+Q2iRWZbTL/yzH2/eDVZS5Wd1LHdsmXClvsCyzqaZfHFzZaN6BUeUCfSDA==",
                        "dev": true,
                        "license": "MIT"
                },
                "node_modules/@types/node": {
-                       "version": "22.8.6",
-                       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz",
-                       "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==",
+                       "version": "22.9.0",
+                       "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
+                       "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
                        "dev": true,
                        "license": "MIT",
                        "dependencies": {
                                "nanoassert": "^2.0.0"
                        }
                },
-               "node_modules/blakejs": {
-                       "version": "1.2.1",
-                       "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
-                       "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==",
-                       "license": "MIT"
-               },
                "node_modules/esbuild": {
                        "version": "0.24.0",
                        "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz",
index ce5973cfee6f349742ae72857047e38fbc4fae1e..4990a99f3ddc1d5a4451e4ee5ccbcb10d5b061e1 100644 (file)
@@ -45,9 +45,7 @@
                "test:coverage:report": "npm run test:coverage -- --test-reporter=lcov --test-reporter-destination=coverage.info && genhtml coverage.info --output-directory test/coverage && rm coverage.info && xdg-open test/coverage/index.html"
        },
        "dependencies": {
-               "blake2b": "^2.1.4",
-               "blake2b-wasm": "^2.4.0",
-               "blakejs": "^1.2.1"
+               "blake2b": "^2.1.4"
        },
        "optionalDependencies": {
                "@ledgerhq/hw-transport-web-ble": "^6.29.4",
@@ -55,7 +53,7 @@
                "@ledgerhq/hw-transport-webusb": "^6.29.4"
        },
        "devDependencies": {
-               "@types/blake2b-wasm": "^2.4.3",
+               "@types/blake2b": "^2.1.3",
                "@types/node": "^22.8.6",
                "@types/w3c-web-hid": "^1.0.6",
                "@types/w3c-web-usb": "^1.0.10",
index 7a111d3df7f0f5ae7c7bc4bd86ec8daea50626dc..d01a988e88b9ce9464e9e061cc348207f7259f8b 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>\r
 // SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
-import { blake2b } from 'blakejs'\r
+import blake2b from 'blake2b'\r
 import { ACCOUNT_KEY_LENGTH, ALPHABET, PREFIX, PREFIX_LEGACY } from './constants.js'\r
 import { base32, bytes, hex } from './convert.js'\r
 import Ed25519 from './ed25519.js'\r
@@ -143,7 +143,7 @@ export class Account {
                const expectedChecksum = address.slice(-8)\r
                const keyBase32 = address.slice(address.indexOf('_') + 1, -8)\r
                const keyBuf = base32.toBytes(keyBase32)\r
-               const actualChecksumBuf = blake2b(keyBuf, undefined, 5).reverse()\r
+               const actualChecksumBuf = blake2b(5, undefined, undefined, undefined, true).update(keyBuf).digest().reverse()\r
                const actualChecksum = bytes.toBase32(actualChecksumBuf)\r
 \r
                if (expectedChecksum !== actualChecksum) {\r
@@ -186,7 +186,7 @@ export class Account {
        static #addressToKey (v: string): string {\r
                const keyBytes = base32.toBytes(v.substring(0, 52))\r
                const checksumBytes = base32.toBytes(v.substring(52, 60))\r
-               const blakeHash = blake2b(keyBytes, undefined, 5).reverse()\r
+               const blakeHash = blake2b(5, undefined, undefined, undefined, true).update(keyBytes).digest().reverse()\r
                if (bytes.toHex(checksumBytes) !== bytes.toHex(blakeHash)) {\r
                        throw new Error('Checksum mismatch in address')\r
                }\r
@@ -195,7 +195,7 @@ export class Account {
 \r
        static async #keyToAddress (key: string): Promise<string> {\r
                const publicKeyBytes = hex.toBytes(key)\r
-               const checksum = blake2b(publicKeyBytes, undefined, 5).reverse()\r
+               const checksum = blake2b(5, undefined, undefined, undefined, true).update(publicKeyBytes).digest().reverse()\r
                const encoded = bytes.toBase32(publicKeyBytes)\r
                const encodedChecksum = bytes.toBase32(checksum)\r
                return `${PREFIX}${encoded}${encodedChecksum}`\r
diff --git a/src/lib/ckd.ts b/src/lib/ckd.ts
deleted file mode 100644 (file)
index 78cb8cc..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-import blake2b from 'blake2b-wasm'
-import { nanoCKD } from './bip32-key-derivation.js'
-
-let addEventListener = globalThis.addEventListener
-let postMessage = globalThis.postMessage
-if (addEventListener == null || postMessage == null) {
-       const { isMainThread, parentPort } = await import('node:worker_threads')
-       if (!isMainThread && parentPort) {
-               addEventListener = Object.getPrototypeOf(parentPort).addListener.bind(parentPort)
-               postMessage = Object.getPrototypeOf(parentPort).postMessage.bind(parentPort)
-       }
-}
-
-/**
-* Derives BIP-44 Nano account private keys.
-*
-* @param {number} index - Index of the account
-* @returns {Promise<Account>}
-*/
-addEventListener('message', (message) => {
-       const { type, seed, index } = message.data ?? message
-       switch (type) {
-               case 'bip44': {
-                       ckdBip44(seed, index).then(postMessage)
-                       break
-               }
-               case 'blake2b': {
-                       ckdBlake2b(seed, index).then(postMessage)
-                       break
-               }
-       }
-})
-
-/**
-* Derives BIP-44 Nano account private keys.
-*
-* @param {number} index - Index of the account
-* @returns {Promise<Account>}
-*/
-async function ckdBip44 (seed: string, index: number): Promise<string> {
-       return nanoCKD(seed, index)
-}
-
-/**
-* Derives BLAKE2b account private keys.
-*
-* @param {number} index - Index of the account
-* @returns {Promise<Account>}
-*/
-async function ckdBlake2b (seed: string, index: number): Promise<string> {
-       const indexHex = index.toString(16).padStart(8, '0').toUpperCase()
-       const inputHex = `${seed}${indexHex}`.padStart(72, '0')
-       const inputArray = (inputHex.match(/.{1,2}/g) ?? []).map(h => parseInt(h, 16))
-       const inputBytes = Uint8Array.from(inputArray)
-       const hash = blake2b().update(inputBytes).digest('hex')
-       return hash
-}
index 14ba60f5162da283ff77fae3f22605993a3c9ff7..1386d88708fc82078d6cc8fa9e4eeb359e497483 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
 // SPDX-License-Identifier: GPL-3.0-or-later
 
-import { blake2b } from 'blakejs'
+import blake2b from 'blake2b'
 
 /**
 * Derived from:
@@ -686,7 +686,7 @@ export default class Curve25519 {
                for (let i = 0; i < n; ++i) {
                        input[i] = m[i]
                }
-               const hash = blake2b(input)
+               const hash = blake2b(64).update(input).digest()
                for (let i = 0; i < 64; ++i) {
                        out[i] = hash[i]
                }
index 395a409ac27031077c65a50713c6939263b393fe..60631cd28804e661916b80fdfca0bf3b22c7352f 100644 (file)
@@ -1,8 +1,7 @@
 // SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>\r
 // SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
-import blakejs from 'blakejs'\r
-const { blake2b, blake2bInit, blake2bUpdate, blake2bFinal } = blakejs\r
+import blake2b from 'blake2b'\r
 import { bytes, hex } from './convert.js'\r
 import Curve25519 from './curve25519.js'\r
 \r
@@ -26,7 +25,7 @@ const L: Uint8Array = new Uint8Array([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12,
 function getPublicKey (privateKey: string): string {\r
        const pk = new Uint8Array(32)\r
        const p = [curve.gf(), curve.gf(), curve.gf(), curve.gf()]\r
-       const h = blake2b(hex.toBytes(privateKey), undefined, 64).slice(0, 32)\r
+       const h = blake2b(64).update(hex.toBytes(privateKey)).digest().slice(0, 32)\r
 \r
        h[0] &= 0xf8\r
        h[31] &= 0x7f\r
@@ -94,11 +93,11 @@ function verify (msg: Uint8Array, publicKey: Uint8Array, signature: Uint8Array):
                return false\r
        }\r
 \r
-       const ctx = blake2bInit(64, undefined)\r
-       blake2bUpdate(ctx, signature.subarray(0, 32))\r
-       blake2bUpdate(ctx, publicKey)\r
-       blake2bUpdate(ctx, msg)\r
-       let k = blake2bFinal(ctx)\r
+       const k = blake2b(64)\r
+               .update(signature.subarray(0, 32))\r
+               .update(publicKey)\r
+               .update(msg)\r
+               .digest()\r
        reduce(k)\r
        scalarmult(p, q, k)\r
 \r
index 3a417c0fc383694a8bf4e028bc3dd4d2d0071d77..8654756877403ba8931cf492a198465dec1d3144 100644 (file)
@@ -3,13 +3,11 @@
 
 import { buffer, hex, utf8 } from './convert.js'
 import { Entropy } from './entropy.js'
-// import { Thread } from './thread.js'
 const { subtle } = globalThis.crypto
 const ERR_MSG = 'Failed to store item in Safe'
 
 export class Safe {
        #storage = globalThis.sessionStorage
-       // #thread = new Thread(new URL('./ckd.js', import.meta.url))
 
        /**
        * Encrypts data with a password and stores it in the Safe.
diff --git a/src/lib/thread.ts b/src/lib/thread.ts
deleted file mode 100644 (file)
index 79addc4..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-if (globalThis.Worker == null) {
-  const { Worker } = await import('node:worker_threads')
-  //@ts-expect-error
-  Worker.prototype.addEventListener = Worker.prototype.addListener
-  //@ts-expect-error
-  globalThis.Worker = Worker
-}
-
-type Task = {
-  data: object,
-  resolve: Function
-}
-
-/**
-* Processes tasks from a queue using a Web Worker.
-*/
-export class Thread {
-  #isAvailable: boolean = true
-  #queue: Task[] = []
-  #task?: Task
-  #worker: Worker
-
-  #post (next: Task) {
-    this.#isAvailable = false
-    this.#task = next
-    this.#worker.postMessage(next.data)
-  }
-
-  constructor (url: string | URL) {
-    this.#worker = new Worker(new URL(url, import.meta.url), { type: 'module' })
-    this.#worker.addEventListener('message', (event) => {
-      const result = event.data ?? event
-      if (this.#task == null) {
-        throw new ReferenceError(`Error resolving Worker result: ${result}`)
-      }
-      const resolve = this.#task.resolve
-      const next = this.#queue.shift()
-      if (next == null) {
-        this.#isAvailable = true
-      } else {
-        this.#post(next)
-      }
-      resolve(result)
-    })
-  }
-
-  async work (data: object): Promise<any> {
-    return new Promise(resolve => {
-      if (this.#isAvailable) {
-        this.#post({ data, resolve })
-      } else {
-        this.#queue.push({ data, resolve })
-      }
-    })
-  }
-}
index d5c24728c52137571f83143f864e049d118c6732..796df72173a3f5037e839f2221a5bd5b736d6705 100644 (file)
@@ -1,8 +1,7 @@
 // SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>
 // SPDX-License-Identifier: GPL-3.0-or-later
 
-import blakejs from 'blakejs'
-const { blake2bInit, blake2bUpdate, blake2bFinal } = blakejs
+import blake from 'blake2b'
 import { Account } from './account.js'
 import { UNITS } from './constants.js'
 import { bytes, hex } from './convert.js'
@@ -19,9 +18,9 @@ import { SendBlock } from './block.js'
 */
 export async function blake2b (data: string | string[]): Promise<Uint8Array> {
        if (!Array.isArray(data)) data = [data]
-       const ctx = blake2bInit(32)
-       data.forEach(str => blake2bUpdate(ctx, hex.toBytes(str)))
-       return blake2bFinal(ctx)
+       const hash = blake(32)
+       data.forEach(str => hash.update(hex.toBytes(str)))
+       return hash.digest()
 }
 
 /**
index b180edbba3e9929db4a07b385b788b54243bb0f2..ae3bea24ef1f229e7c654cb55225fe7b33d66c5b 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: 2024 Chris Duncan <chris@zoso.dev>\r
 // SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
-import blake2b from 'blake2b-wasm'\r
+import blake2b from 'blake2b'\r
 import { Account } from './account.js'\r
 import { nanoCKD } from './bip32-key-derivation.js'\r
 import { Bip39Mnemonic } from './bip39-mnemonic.js'\r
@@ -536,7 +536,7 @@ export class Blake2bWallet extends Wallet {
        */\r
        async ckd (index: number): Promise<Account> {\r
                const input = `${this.seed}${dec.toHex(index, 8)}`\r
-               const key = blake2b().update(hex.toBytes(input)).digest('hex')\r
+               const key = blake2b(32).update(hex.toBytes(input)).digest('hex')\r
                if (typeof key !== 'string') {\r
                        throw new TypeError('BLAKE2b child key derivation returned invalid data')\r
                }\r
index e8e79482cc958887c178ac8fe43708542b79ba65..f4833c1946351070526d0697a797ba8f4cf64b23 100644 (file)
@@ -15,7 +15,7 @@ describe('derive child accounts from the same seed', async function () {
        const wallet = await Bip44Wallet.fromSeed(NANO_TEST_VECTORS.PASSWORD, NANO_TEST_VECTORS.BIP39_SEED)\r
        await wallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
 \r
-       it('should derive the first account from the given BIP-44 seed', async function () {\r
+       it('should derive the first account from the given BIP-44 seed', async () => {\r
                const accounts = await wallet.accounts()\r
 \r
                assert.equal(accounts.length, 1)\r
@@ -24,7 +24,7 @@ describe('derive child accounts from the same seed', async function () {
                assert.equal(accounts[0].address, NANO_TEST_VECTORS.ADDRESS_0)\r
        })\r
 \r
-       it('should derive low indexed accounts from the given BIP-44 seed', async function () {\r
+       it('should derive low indexed accounts from the given BIP-44 seed', async () => {\r
                const accounts = await wallet.accounts(1, 2)\r
 \r
                assert.equal(accounts.length, 2)\r
@@ -36,7 +36,7 @@ describe('derive child accounts from the same seed', async function () {
                assert.equal(accounts[1].address, NANO_TEST_VECTORS.ADDRESS_2)\r
        })\r
 \r
-       it('should derive high indexed accounts from the given seed', async function () {\r
+       it('should derive high indexed accounts from the given seed', async () => {\r
                const accounts = await wallet.accounts(0x70000000, 0x700000ff)\r
 \r
                assert.equal(accounts.length, 0x100)\r
@@ -49,7 +49,7 @@ describe('derive child accounts from the same seed', async function () {
                }\r
        })\r
 \r
-       it('should derive accounts for a BLAKE2b wallet', async function () {\r
+       it('should derive accounts for a BLAKE2b wallet', async () => {\r
                const bwallet = await Blake2bWallet.create(NANO_TEST_VECTORS.PASSWORD)\r
                await bwallet.unlock(NANO_TEST_VECTORS.PASSWORD)\r
                const lowAccounts = await bwallet.accounts(0, 2)\r
@@ -79,7 +79,7 @@ describe('derive child accounts from the same seed', async function () {
 describe('Ledger device accounts', { skip: true }, async () => {\r
        const wallet = await LedgerWallet.create()\r
 \r
-       it('should fetch the first account from a Ledger device', async function () {\r
+       it('should fetch the first account from a Ledger device', async () => {\r
                const accounts = await wallet.accounts()\r
 \r
                assert.equal(accounts.length, 1)\r