]> git.codecow.com Git - libnemo.git/commitdiff
Replace internal derive-sign-verify NanoNaCl with library nano25519.
authorChris Duncan <chris@zoso.dev>
Mon, 23 Mar 2026 06:42:52 +0000 (23:42 -0700)
committerChris Duncan <chris@zoso.dev>
Mon, 23 Mar 2026 06:42:52 +0000 (23:42 -0700)
package.json
src/lib/account/index.ts
src/lib/block.ts
src/lib/crypto/index.ts
src/lib/crypto/nano-nacl.ts [deleted file]
src/lib/tools.ts
src/lib/vault/index.ts
src/lib/vault/vault-worker.ts

index b182e47c2b982e8af47091eb76e4896d861eed4e..2448643773768023bde5d7f387dbf2cb96042509 100644 (file)
@@ -61,7 +61,8 @@
                "@ledgerhq/hw-transport-web-ble": "^6.33.0",
                "@ledgerhq/hw-transport-webhid": "^6.33.0",
                "@ledgerhq/hw-transport-webusb": "^6.32.0",
-               "nano-pow": "^5.1.10"
+               "nano-pow": "^5.1.10",
+               "nano25519": "^1.0.1"
        },
        "devDependencies": {
                "@types/node": "^25.5.0",
index 7c780a962e0655540c521a1f003491d6080b360e..aba05b8cbfa63d88cde58c68c091325e9da03d03 100644 (file)
@@ -1,10 +1,10 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>\r
 //! SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
+import * as nano25519 from 'nano25519'\r
 import { Block } from '../block'\r
 import { ACCOUNT_KEY_BYTE_LENGTH, ACCOUNT_KEY_HEX_LENGTH } from '../constants'\r
 import { bytes, hex } from '../convert'\r
-import { NanoNaCl } from '../crypto'\r
 import { Rpc } from '../rpc'\r
 import { Address } from './address'\r
 import { _refresh } from './refresh'\r
@@ -302,7 +302,7 @@ export class Account {
                                if (privateKey.byteLength !== ACCOUNT_KEY_BYTE_LENGTH) {\r
                                        throw new TypeError(`Private key must be ${ACCOUNT_KEY_BYTE_LENGTH} bytes`)\r
                                }\r
-                               const publicKey = await NanoNaCl.convert(privateKey)\r
+                               const publicKey = await nano25519.derive(privateKey)\r
                                const address = new Address(publicKey)\r
                                this.#isInternal = true\r
                                const account = new this(address, publicKey, index)\r
index e5cd617b9d0677b7dc3cc5d47b8d7aaef9fbe9f7..790d2ea59a7fee8bd21feca92a9b6ad323f33407 100644 (file)
@@ -2,10 +2,11 @@
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
 import { NanoPow } from 'nano-pow'
+import * as nano25519 from 'nano25519'
 import { Account } from './account'
-import { BURN_PUBLIC_KEY, PREAMBLE, DIFFICULTY_RECEIVE, DIFFICULTY_SEND, UNITS } from './constants'
+import { BURN_PUBLIC_KEY, DIFFICULTY_RECEIVE, DIFFICULTY_SEND, PREAMBLE, UNITS } from './constants'
 import { bytes, dec, hex } from './convert'
-import { Blake2b, NanoNaCl } from './crypto'
+import { Blake2b } from './crypto'
 import { Rpc } from './rpc'
 import { Tools } from './tools'
 import { Wallet } from './wallet'
@@ -411,7 +412,7 @@ export class Block {
                return new Promise(async (resolve, reject) => {
                        try {
                                if (typeof input === 'string' && /^[A-F0-9]{64}$/i.test(input)) {
-                                       const signature = await NanoNaCl.detached(hex.toBytes(this.hash), hex.toBytes(input))
+                                       const signature = await nano25519.sign(hex.toBytes(this.hash), hex.toBytes(input))
                                        this.signature = bytes.toHex(signature)
                                } else if (input instanceof Wallet && typeof index === 'number'
                                        && (frontier === undefined || frontier instanceof (this.constructor as typeof Block))
@@ -443,7 +444,7 @@ export class Block {
                        if (typeof key !== 'string') {
                                throw new Error('Invalid key')
                        }
-                       return await NanoNaCl.verify(hex.toBytes(this.hash), hex.toBytes(this.signature ?? ''), hex.toBytes(key))
+                       return await nano25519.verify(hex.toBytes(this.hash), hex.toBytes(this.signature ?? ''), hex.toBytes(key))
                } catch (err) {
                        throw new Error('Failed to verify block signature', { cause: err })
                }
index c0f3d8c73b8863b2ae7c056cc01c601de7d641ca..dee8ec61dbd63388964637c30e13e121a9eccd39 100644 (file)
@@ -4,8 +4,7 @@
 import { Bip39 } from './bip39'
 import { Bip44 } from './bip44'
 import { Blake2b } from './blake2b'
-import { NanoNaCl } from './nano-nacl'
 import { Secp256k1 } from './secp256k1'
 import { WalletAesGcm } from './wallet-aes-gcm'
 
-export { Bip39, Bip44, Blake2b, NanoNaCl, Secp256k1, WalletAesGcm }
+export { Bip39, Bip44, Blake2b, Secp256k1, WalletAesGcm }
diff --git a/src/lib/crypto/nano-nacl.ts b/src/lib/crypto/nano-nacl.ts
deleted file mode 100644 (file)
index 8ce4bd3..0000000
+++ /dev/null
@@ -1,609 +0,0 @@
-//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>\r
-//! SPDX-License-Identifier: GPL-3.0-or-later\r
-\r
-import { Blake2b } from '.'\r
-\r
-/**\r
-* Ported in 2014 by Dmitry Chestnykh and Devi Mandiri.\r
-* Public domain.\r
-*\r
-* Implementation derived from TweetNaCl version 20140427: http://tweetnacl.cr.yp.to/\r
-*\r
-* Modified to hash secret key to public key using BLAKE2b instead of SHA-512 per\r
-* Nano cryptocurrency specifications: https://docs.nano.org/integration-guides/the-basics/\r
-*\r
-* Original source commit: https://github.com/dchest/tweetnacl-js/blob/71df1d6a1d78236ca3e9f6c788786e21f5a651a6/nacl-fast.js\r
-*/\r
-export class NanoNaCl {\r
-       static crypto_sign_BYTES: 64 = 64\r
-       static crypto_sign_PUBLICKEYBYTES: 32 = 32\r
-       static crypto_sign_PRIVATEKEYBYTES: 32 = 32\r
-       static crypto_sign_SEEDBYTES: 32 = 32\r
-       static D: Float64Array = new Float64Array([0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203])\r
-       static D2: Float64Array = new Float64Array([0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406])\r
-       static X: Float64Array = new Float64Array([0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169])\r
-       static Y: Float64Array = new Float64Array([0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666])\r
-       static I: Float64Array = new Float64Array([0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83])\r
-       static XY: Float64Array = new Float64Array([0xdd90, 0xa5b7, 0x8ab3, 0x6dde, 0x52f5, 0x7751, 0x9f80, 0x20f0, 0xe37d, 0x64ab, 0x4e8e, 0x66ea, 0x7665, 0xd78b, 0x5f0f, 0xe787])\r
-\r
-       /**\r
-       * Checks first 32 bytes of two byte arrays to see if they are equivalent.\r
-       *\r
-       * @returns 0 if first 32 bytes are equal, else 1 if there is at least one bit difference\r
-       */\r
-       static vn (x: Uint8Array, y: Uint8Array): number {\r
-               let d: number = 0\r
-               for (let i = 0; i < 32; i++) {\r
-                       d |= x[i] ^ y[i]\r
-               }\r
-               return ((1 & ((d - 1) >>> 8)) - 1) & 1\r
-       }\r
-\r
-       static pow2523 (out: Float64Array, i: Float64Array): void {\r
-               const c: Float64Array = new Float64Array(16)\r
-               c.set(i.subarray(0, 16), 0)\r
-               for (let a = 0; a < 249; a++) {\r
-                       this.Square(c, c)\r
-                       this.Multiply(c, c, i)\r
-               }\r
-               this.Square(c, c)\r
-               this.Square(c, c)\r
-               this.Multiply(c, c, i)\r
-               out.set(c, 0)\r
-       }\r
-\r
-       static car25519 (out: Float64Array): void {\r
-               let v, c\r
-               const s = 1 << 16\r
-               c = 1\r
-               for (let i = 0; i < 16; i++) {\r
-                       v = out[i] + c + s - 1\r
-                       out[i] = v % s\r
-                       c = (v / s) | 0\r
-               }\r
-               out[0] += 38 * (c - 1)\r
-       }\r
-\r
-       static inv25519 (out: Float64Array, i: Float64Array): void {\r
-               const c: Float64Array = new Float64Array(16)\r
-               c.set(i.subarray(0, 16), 0)\r
-               for (let a = 0; a < 249; a++) {\r
-                       this.Square(c, c)\r
-                       this.Multiply(c, c, i)\r
-               }\r
-               this.Square(c, c)\r
-               this.Square(c, c)\r
-               this.Multiply(c, c, i)\r
-               this.Square(c, c)\r
-               this.Square(c, c)\r
-               this.Multiply(c, c, i)\r
-               this.Square(c, c)\r
-               this.Multiply(c, c, i)\r
-               out.set(c, 0)\r
-       }\r
-\r
-       static neq25519 (a: Float64Array, b: Float64Array): number {\r
-               const c: Uint8Array = new Uint8Array(32)\r
-               const d: Uint8Array = new Uint8Array(32)\r
-               this.pack25519(c, a)\r
-               this.pack25519(d, b)\r
-               return this.vn(c, d)\r
-       }\r
-\r
-       static pack25519 (out: Uint8Array, n: Float64Array): void {\r
-               let b: number\r
-               const m: Float64Array = new Float64Array(16)\r
-               const t: Float64Array = new Float64Array(16)\r
-               t.set(n, 0)\r
-               this.car25519(t)\r
-               this.car25519(t)\r
-               this.car25519(t)\r
-               for (let j = 0; j < 2; j++) {\r
-                       m[0] = t[0] - 0xffed\r
-                       for (let i = 1; i < 15; i++) {\r
-                               m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1)\r
-                               m[i - 1] &= 0xffff\r
-                       }\r
-                       m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1)\r
-                       b = (m[15] >> 16) & 1\r
-                       m[14] &= 0xffff\r
-                       this.sel25519(t, m, 1 - b)\r
-               }\r
-               for (let i = 0; i < 16; i++) {\r
-                       out[2 * i] = t[i] & 0xff\r
-                       out[2 * i + 1] = t[i] >> 8\r
-               }\r
-       }\r
-\r
-       static par25519 (a: Float64Array): 0 | 1 {\r
-               const d: Uint8Array = new Uint8Array(32)\r
-               this.pack25519(d, a)\r
-               return (d[0] & 1) as 0 | 1\r
-       }\r
-\r
-       static sel25519 (p: Float64Array, q: Float64Array, b: number): void {\r
-               let t\r
-               const c = ~(b - 1)\r
-               for (let i = 0; i < 16; i++) {\r
-                       t = c & (p[i] ^ q[i])\r
-                       p[i] ^= t\r
-                       q[i] ^= t\r
-               }\r
-       }\r
-\r
-       static unpack25519 (out: Float64Array, n: Uint8Array): void {\r
-               for (let i = 0; i < 16; i++) {\r
-                       out[i] = n[2 * i] + (n[2 * i + 1] << 8)\r
-               }\r
-               out[15] &= (1 << 15) - 1\r
-       }\r
-\r
-       static Add (out: Float64Array, a: Float64Array, b: Float64Array): void {\r
-               for (let i = 0; i < 16; i++) {\r
-                       out[i] = a[i] + b[i]\r
-               }\r
-       }\r
-\r
-       static Subtract (out: Float64Array, a: Float64Array, b: Float64Array): void {\r
-               for (let i = 0; i < 16; i++) {\r
-                       out[i] = a[i] - b[i]\r
-               }\r
-       }\r
-\r
-       static Multiply (out: Float64Array, a: Float64Array, b: Float64Array): void {\r
-               let v, c\r
-               const s = 1 << 16\r
-               const t = new Float64Array(31)\r
-               t.fill(0)\r
-\r
-               // init t values\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
-               }\r
-\r
-               for (let i = 0; i < 15; i++) {\r
-                       t[i] += 38 * t[i + 16]\r
-               }\r
-               // t15 left as is\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) | 0\r
-               }\r
-               t[0] += 38 * c\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) | 0\r
-               }\r
-               t[0] += 38 * c\r
-\r
-               // assign result to output\r
-               out.set(t.slice(0, 16), 0)\r
-       }\r
-\r
-       static Square (out: Float64Array, a: Float64Array): void {\r
-               let v, c\r
-               const s = 1 << 16\r
-               const t = new Float64Array(31)\r
-               t.fill(0)\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
-                               //@ts-expect-error\r
-                               t[i + j] += a[i] * a[j] * ((i < j) + 1)\r
-                       }\r
-               }\r
-\r
-               for (let i = 0; i < 15; i++) {\r
-                       t[i] += 38 * t[i + 16]\r
-               }\r
-               // t15 left as is\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) | 0\r
-               }\r
-               t[0] += 38 * c\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) | 0\r
-               }\r
-               t[0] += 38 * c\r
-\r
-               // assign result to output\r
-               out.set(t.slice(0, 16), 0)\r
-       }\r
-\r
-       static add (p: Float64Array[], q: Float64Array[]): void {\r
-               const a: Float64Array = new Float64Array(16)\r
-               const b: Float64Array = new Float64Array(16)\r
-               const c: Float64Array = new Float64Array(16)\r
-               const d: Float64Array = new Float64Array(16)\r
-               const e: Float64Array = new Float64Array(16)\r
-               const f: Float64Array = new Float64Array(16)\r
-               const g: Float64Array = new Float64Array(16)\r
-               const h: Float64Array = new Float64Array(16)\r
-               const t: Float64Array = new Float64Array(16)\r
-\r
-               this.Subtract(a, p[1], p[0])\r
-               this.Subtract(t, q[1], q[0])\r
-               this.Multiply(a, a, t)\r
-               this.Add(b, p[0], p[1])\r
-               this.Add(t, q[0], q[1])\r
-               this.Multiply(b, b, t)\r
-               this.Multiply(c, p[3], q[3])\r
-               this.Multiply(c, c, this.D2)\r
-               this.Multiply(d, p[2], q[2])\r
-               this.Add(d, d, d)\r
-               this.Subtract(e, b, a)\r
-               this.Subtract(f, d, c)\r
-               this.Add(g, d, c)\r
-               this.Add(h, b, a)\r
-\r
-               this.Multiply(p[0], e, f)\r
-               this.Multiply(p[1], h, g)\r
-               this.Multiply(p[2], g, f)\r
-               this.Multiply(p[3], e, h)\r
-       }\r
-\r
-       static cswap (p: Float64Array[], q: Float64Array[], b: number): void {\r
-               for (let i = 0; i < 4; i++) {\r
-                       this.sel25519(p[i], q[i], b)\r
-               }\r
-       }\r
-\r
-       static pack (r: Uint8Array, p: Float64Array[]): void {\r
-               const tx: Float64Array = new Float64Array(16)\r
-               const ty: Float64Array = new Float64Array(16)\r
-               const zi: Float64Array = new Float64Array(16)\r
-               this.inv25519(zi, p[2])\r
-               this.Multiply(tx, p[0], zi)\r
-               this.Multiply(ty, p[1], zi)\r
-               this.pack25519(r, ty)\r
-               r[31] ^= this.par25519(tx) << 7\r
-       }\r
-\r
-       static scalarmult (p: Float64Array[], q: Float64Array[], 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 = 255; i >= 0; --i) {\r
-                       const b = (s[(i / 8) | 0] >> (i & 7)) & 1\r
-                       this.cswap(p, q, b)\r
-                       this.add(q, p)\r
-                       this.add(p, p)\r
-                       this.cswap(p, q, b)\r
-               }\r
-       }\r
-\r
-       static scalarbase (p: Float64Array[], s: Uint8Array): void {\r
-               const q: Float64Array[] = [new Float64Array(16), new Float64Array(16), new Float64Array(16), new Float64Array(16)]\r
-               q[0].set(this.X, 0)\r
-               q[1].set(this.Y, 0)\r
-               q[2].set([1], 0)\r
-               q[3].set(this.XY, 0)\r
-               this.scalarmult(p, q, s)\r
-       }\r
-\r
-       static L = new Float64Array([\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
-       static modL (r: Uint8Array, x: Float64Array): void {\r
-               let c, t, v\r
-               for (let i = 63; i >= 32; --i) {\r
-                       c = 0\r
-                       for (let j = i - 32, k = i - 12; j < k; j++) {\r
-                               t = 16 * x[i] * this.L[j - (i - 32)]\r
-                               v = x[j] + c - t\r
-                               c = ((v + 128) / 256) | 0\r
-                               x[j] = v - (c * 256)\r
-                       }\r
-                       x[i - 12] += c\r
-                       x[i] = 0\r
-               }\r
-               c = 0\r
-               for (let j = 0; j < 32; j++) {\r
-                       x[j] += c - (x[31] >> 4) * this.L[j]\r
-                       c = x[j] >> 8\r
-                       x[j] &= 255\r
-               }\r
-               for (let j = 0; j < 32; j++) {\r
-                       x[j] -= c * this.L[j]\r
-               }\r
-               for (let i = 0; i < 32; i++) {\r
-                       x[i + 1] += x[i] >> 8\r
-                       r[i] = x[i] & 255\r
-               }\r
-       }\r
-\r
-       static reduce (r: Uint8Array): void {\r
-               let x = new Float64Array(64)\r
-               x.set(r, 0)\r
-               r.fill(0)\r
-               this.modL(r, x)\r
-       }\r
-\r
-       static unpackneg (r: Float64Array[], p: Uint8Array): number {\r
-               const t: Float64Array = new Float64Array(16)\r
-               const chk: Float64Array = new Float64Array(16)\r
-               const num: Float64Array = new Float64Array(16)\r
-               const den: Float64Array = new Float64Array(16)\r
-               const den2: Float64Array = new Float64Array(16)\r
-               const den4: Float64Array = new Float64Array(16)\r
-               const den6: Float64Array = new Float64Array(16)\r
-\r
-               r[2].fill(0).set([1], 0)\r
-               this.unpack25519(r[1], p)\r
-               this.Square(num, r[1])\r
-               this.Multiply(den, num, this.D)\r
-               this.Subtract(num, num, r[2])\r
-               this.Add(den, r[2], den)\r
-\r
-               this.Square(den2, den)\r
-               this.Square(den4, den2)\r
-               this.Multiply(den6, den4, den2)\r
-               this.Multiply(t, den6, num)\r
-               this.Multiply(t, t, den)\r
-\r
-               this.pow2523(t, t)\r
-               this.Multiply(t, t, num)\r
-               this.Multiply(t, t, den)\r
-               this.Multiply(t, t, den)\r
-               this.Multiply(r[0], t, den)\r
-\r
-               this.Square(chk, r[0])\r
-               this.Multiply(chk, chk, den)\r
-\r
-               // if neq is true, multiply r[0] by I, else multiply by 1 for a no-op\r
-               let neq = this.neq25519(chk, num)\r
-               const I = new Float64Array(this.I)\r
-               for (let i = 0; i < 16; i++) {\r
-                       I[i] *= neq\r
-               }\r
-               I[0] += neq ^ 1\r
-               this.Multiply(r[0], r[0], I)\r
-\r
-               this.Square(chk, r[0])\r
-               this.Multiply(chk, chk, den)\r
-\r
-               neq = 0 - this.neq25519(chk, num)\r
-\r
-               // if par25519, subtract r[0] from 0 to swap sign, else keep current values\r
-               //@ts-expect-error\r
-               const par = (this.par25519(r[0]) === p[31] >> 7) ^ 1\r
-               for (let i = 0; i < 16; i++) {\r
-                       r[0][i] = (2 * par * r[0][i]) - r[0][i]\r
-               }\r
-               this.Multiply(r[3], r[0], r[1])\r
-               return neq\r
-       }\r
-\r
-       static crypto_sign (sm: Uint8Array, m: Uint8Array, n: number, sk: Uint8Array, pk: Uint8Array): void {\r
-               const p: Float64Array[] = [new Float64Array(16), new Float64Array(16), new Float64Array(16), new Float64Array(16)]\r
-\r
-               const d = new Blake2b(64).update(sk).digest()\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
-\r
-               const r = new Blake2b(64).update(sm.subarray(32)).digest()\r
-               this.reduce(r)\r
-               this.scalarbase(p, r)\r
-               this.pack(sm, p)\r
-\r
-               sm.set(pk, 32)\r
-               const h = new Blake2b(64).update(sm).digest()\r
-               this.reduce(h)\r
-\r
-               const x = new Float64Array(64)\r
-               x.set(r.subarray(0, 32))\r
-               for (let i = 0; i < 32; i++) {\r
-                       for (let j = 0; j < 32; j++) {\r
-                               x[i + j] += h[i] * d[j]\r
-                       }\r
-               }\r
-\r
-               this.modL(sm.subarray(32), x)\r
-       }\r
-\r
-       static crypto_sign_open (m: Uint8Array, sm: Uint8Array, n: number, pk: Uint8Array): number {\r
-               const t = new Uint8Array(32)\r
-               const p: Float64Array[] = [new Float64Array(16), new Float64Array(16), new Float64Array(16), new Float64Array(16)]\r
-               const q: Float64Array[] = [new Float64Array(16), new Float64Array(16), new Float64Array(16), new Float64Array(16)]\r
-\r
-               // eventually used in returned result but allow processing to continue\r
-               const neg = this.unpackneg(q, pk) & 1\r
-\r
-               m.set(sm.subarray(0, n), 0)\r
-               m.set(pk.subarray(0, 32), 32)\r
-               const h = new Blake2b(64).update(m).digest()\r
-               this.reduce(h)\r
-               this.scalarmult(p, q, h)\r
-\r
-               this.scalarbase(q, sm.subarray(32))\r
-               this.add(p, q)\r
-               this.pack(t, p)\r
-\r
-               n -= 64\r
-\r
-               // if any bits unequal, zero out and return -1\r
-               const vn = this.vn(sm, t) ^ 1\r
-               for (let i = 0; i < n; i++) {\r
-                       m[i] = sm[i + 64] * vn\r
-               }\r
-               n = (n * vn) - (vn ^ 1)\r
-               return (n * (neg ^ 1)) - neg\r
-       }\r
-\r
-       /**\r
-       * Derives a public key from a private key "seed".\r
-       *\r
-       * @param {Uint8Array<ArrayBuffer>} seed - 32-byte private key\r
-       * @returns {Uint8Array<ArrayBuffer>} 32-byte public key\r
-       */\r
-       static convert (seed: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>\r
-       static convert (seed: unknown): Uint8Array<ArrayBuffer> {\r
-               try {\r
-                       if (!(seed instanceof Uint8Array)) {\r
-                               throw new TypeError('Seed must be Uint8Array')\r
-                       }\r
-                       if (seed.byteLength !== this.crypto_sign_SEEDBYTES) {\r
-                               throw new Error(`Seed must be ${this.crypto_sign_SEEDBYTES} bytes`)\r
-                       }\r
-                       const s = new Uint8Array(seed)\r
-                       seed = undefined\r
-                       const pk = new Uint8Array(this.crypto_sign_PUBLICKEYBYTES)\r
-                       const p: Float64Array[] = [new Float64Array(16), new Float64Array(16), new Float64Array(16), new Float64Array(16)]\r
-\r
-                       const hash = new Blake2b(64).update(s).digest()\r
-                       hash[0] &= 248\r
-                       hash[31] &= 127\r
-                       hash[31] |= 64\r
-\r
-                       this.scalarbase(p, hash)\r
-                       this.pack(pk, p)\r
-\r
-                       return pk\r
-               } catch (err) {\r
-                       throw new Error('Failed to convert seed to public key', { cause: err })\r
-               }\r
-       }\r
-\r
-       /**\r
-       * Signs the message using the secret key and returns a signature.\r
-       *\r
-       * @param {Uint8Array<ArrayBuffer>} message - Message to sign\r
-       * @param {Uint8Array<ArrayBuffer>} privateKey - 32-byte key to use for signing\r
-       * @returns {Uint8Array<ArrayBuffer>} 64-byte signature\r
-       */\r
-       static detached (message: Uint8Array<ArrayBuffer>, privateKey: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>\r
-       static detached (message: unknown, privateKey: unknown): Uint8Array<ArrayBuffer> {\r
-               try {\r
-                       const sm = this.sign(message as Uint8Array<ArrayBuffer>, privateKey as Uint8Array<ArrayBuffer>)\r
-                       return new Uint8Array(sm.buffer.slice(0, this.crypto_sign_BYTES))\r
-               } catch (err) {\r
-                       throw new Error('Failed to sign and return signature', { cause: err })\r
-               }\r
-       }\r
-\r
-       /**\r
-       * Verifies the signed message and returns the message without signature.\r
-       *\r
-       * @param {Uint8Array<ArrayBuffer>} signedMessage - Signed message\r
-       * @param {Uint8Array<ArrayBuffer>} publicKey - 32-byte key used to sign message\r
-       * @returns {Uint8Array<ArrayBuffer>} Message without signature\r
-       */\r
-       static open (signedMessage: Uint8Array<ArrayBuffer>, publicKey: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>\r
-       static open (signedMessage: unknown, publicKey: unknown): Uint8Array<ArrayBuffer> {\r
-               try {\r
-                       if (!(signedMessage instanceof Uint8Array) || signedMessage.byteLength < this.crypto_sign_BYTES) {\r
-                               throw new TypeError('Signed message must be Uint8Array')\r
-                       }\r
-                       if (!(publicKey instanceof Uint8Array) || publicKey.byteLength !== this.crypto_sign_PUBLICKEYBYTES) {\r
-                               throw new Error(`Public key must be ${this.crypto_sign_PUBLICKEYBYTES}-byte Uint8Array`)\r
-                       }\r
-                       const sm = new Uint8Array(signedMessage)\r
-                       const smLen = sm.byteLength\r
-                       const pub = new Uint8Array(publicKey)\r
-                       const tmp = new Uint8Array(smLen)\r
-                       const mLen = this.crypto_sign_open(tmp, sm, smLen, pub)\r
-                       if (mLen < 0) {\r
-                               throw new Error('Signature verification failed')\r
-                       }\r
-                       const m = new Uint8Array(mLen)\r
-                       m.set(tmp.subarray(0, mLen), 0)\r
-                       return m\r
-               } catch (err) {\r
-                       throw new Error('Failed to open message', { cause: err })\r
-               }\r
-       }\r
-\r
-       /**\r
-       * Signs the message using the secret key and returns a signed message.\r
-       *\r
-       * @param {Uint8Array<ArrayBuffer>} message - Message to be signed\r
-       * @param {Uint8Array<ArrayBuffer>} privateKey - 32-byte key to use for signing\r
-       * @returns {Uint8Array<ArrayBuffer>} Signed message\r
-       */\r
-       static sign (message: Uint8Array<ArrayBuffer>, privateKey: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer>\r
-       static sign (message: unknown, privateKey: unknown): Uint8Array<ArrayBuffer> {\r
-               try {\r
-                       if (!(message instanceof Uint8Array)) {\r
-                               throw new TypeError('Message must be Uint8Array')\r
-                       }\r
-                       if (!(privateKey instanceof Uint8Array) || privateKey.byteLength !== this.crypto_sign_SEEDBYTES) {\r
-                               throw new Error(`Private key must be ${this.crypto_sign_PRIVATEKEYBYTES}-byte Uint8Array`)\r
-                       }\r
-                       const prv = new Uint8Array(privateKey)\r
-                       privateKey = undefined\r
-                       const mLen = message.byteLength\r
-                       const msg = new Uint8Array(message)\r
-                       const signed = new Uint8Array(this.crypto_sign_BYTES + mLen)\r
-                       const pub = this.convert(prv)\r
-                       this.crypto_sign(signed, msg, mLen, prv, pub)\r
-                       return signed\r
-               } catch (err) {\r
-                       throw new Error('Failed to sign and return message', { cause: err })\r
-               }\r
-       }\r
-\r
-       /**\r
-       * Verifies a detached signature for a message.\r
-       *\r
-       * @param {Uint8Array<ArrayBuffer>} signedMessage - Signed message\r
-       * @param {Uint8Array<ArrayBuffer>} signature - 64-byte signature\r
-       * @param {Uint8Array<ArrayBuffer>} publicKey - 32-byte key used for signing\r
-       * @returns {boolean} - True if `publicKey` was used to sign `msg` and generate `sig`, else false\r
-       */\r
-       static verify (signedMessage: Uint8Array<ArrayBuffer>, signature: Uint8Array<ArrayBuffer>, publicKey: Uint8Array<ArrayBuffer>): boolean\r
-       static verify (signedMessage: unknown, signature: unknown, publicKey: unknown): boolean {\r
-               try {\r
-                       if (!(signedMessage instanceof Uint8Array)) {\r
-                               throw new TypeError('Signed message must be Uint8Array')\r
-                       }\r
-                       if (!(signature instanceof Uint8Array) || signature.byteLength !== this.crypto_sign_BYTES) {\r
-                               throw new Error(`Signature must be ${this.crypto_sign_BYTES}-byte Uint8Array`)\r
-                       }\r
-                       if (!(publicKey instanceof Uint8Array) || publicKey.byteLength !== this.crypto_sign_PUBLICKEYBYTES) {\r
-                               throw new Error(`Public key must be ${this.crypto_sign_PUBLICKEYBYTES}-byte Uint8Array`)\r
-                       }\r
-                       const msg = new Uint8Array(signedMessage)\r
-                       const sig = new Uint8Array(signature)\r
-                       const pub = new Uint8Array(publicKey)\r
-                       const smLen = this.crypto_sign_BYTES + msg.byteLength\r
-                       const sm = new Uint8Array(smLen)\r
-                       const m = new Uint8Array(smLen)\r
-                       sm.set(sig, 0)\r
-                       sm.set(msg, this.crypto_sign_BYTES)\r
-                       return (this.crypto_sign_open(m, sm, smLen, pub) >= 0)\r
-               } catch (err) {\r
-                       throw new Error('Failed to verify signature on message with the given public key', { cause: err })\r
-               }\r
-       }\r
-}\r
index 59ca5d4659dc728c17bc0afd8f07d27e2895e2d6..2e1a89d96086161d157d73d4aba594a3e7878a0f 100644 (file)
@@ -1,11 +1,12 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
+import * as nano25519 from 'nano25519'
 import { Account } from './account'
 import { Block } from './block'
 import { MAX_SUPPLY, UNITS } from './constants'
 import { bytes, hex } from './convert'
-import { Blake2b, NanoNaCl } from './crypto'
+import { Blake2b } from './crypto'
 import { Rpc } from './rpc'
 import { Wallet } from './wallet'
 
@@ -140,7 +141,7 @@ export class Tools {
        static async sign (key: string | Uint8Array<ArrayBuffer>, ...input: string[]): Promise<string> {
                if (typeof key === 'string') key = hex.toBytes(key)
                try {
-                       const signature = await NanoNaCl.detached(this.hash(input), key)
+                       const signature = await nano25519.sign(this.hash(input), key)
                        return bytes.toHex(signature)
                } catch (err) {
                        throw new Error(`Failed to sign message with private key`, { cause: err })
@@ -218,7 +219,7 @@ export class Tools {
        static async verify (key: string | Uint8Array<ArrayBuffer>, signature: string, ...input: string[]): Promise<boolean> {
                if (typeof key === 'string') key = hex.toBytes(key)
                try {
-                       return await NanoNaCl.verify(this.hash(input), hex.toBytes(signature), key)
+                       return await nano25519.verify(this.hash(input), hex.toBytes(signature), key)
                } catch (err) {
                        throw new Error('Failed to verify signature', { cause: err })
                } finally {
index 710deb0f1c561b7ae35fd3982400321117ec2dec..93ae64b580ffb6a529fcd3e2e126bdf39767c642 100644 (file)
@@ -1,9 +1,10 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
+import * as nano25519 from 'nano25519'
 import { Worker as NodeWorker } from 'node:worker_threads'
 import { default as CONSTANTS } from '../constants'
-import { Bip39, Bip44, Blake2b, NanoNaCl, Secp256k1, WalletAesGcm } from '../crypto'
+import { Bip39, Bip44, Blake2b, Secp256k1, WalletAesGcm } from '../crypto'
 import { Data } from '../database'
 import { Passkey } from './passkey'
 import { VaultTimer } from './vault-timer'
@@ -122,13 +123,15 @@ export class Vault {
        }
 }
 
+const nano25519Url = URL.createObjectURL(new Blob([`${nano25519}`], { type: 'text/javascript' }))
+
 const blob = `
+       const nano25519 = await import(${nano25519Url})
        ${CONSTANTS}
        const ${Secp256k1.name} = ${Secp256k1}
        const ${Bip39.name} = ${Bip39}
        const ${Bip44.name} = ${Bip44}
        const ${Blake2b.name} = ${Blake2b}
-       const ${NanoNaCl.name} = ${NanoNaCl}
        const ${WalletAesGcm.name} = ${WalletAesGcm}
        const ${Passkey.name} = ${Passkey}
        const ${VaultTimer.name} = ${VaultTimer}
@@ -137,7 +140,6 @@ const blob = `
        ${Bip39.name === 'Bip39' ? '' : `const Bip39 = ${Bip39.name}`}
        ${Bip44.name === 'Bip44' ? '' : `const Bip44 = ${Bip44.name}`}
        ${Blake2b.name === 'Blake2b' ? '' : `const Blake2b = ${Blake2b.name}`}
-       ${NanoNaCl.name === 'NanoNaCl' ? '' : `const NanoNaCl = ${NanoNaCl.name}`}
        ${WalletAesGcm.name === 'WalletAesGcm' ? '' : `const WalletAesGcm = ${WalletAesGcm.name}`}
        ${Passkey.name === 'Passkey' ? '' : `const Passkey = ${Passkey.name}`}
        ${VaultTimer.name === 'VaultTimer' ? '' : `const VaultTimer = ${VaultTimer.name}`}
index 66436e0b345f5356fcd8e5c7c1083256aab94b1b..20e081585795a1af58cd60828591879876a46902 100644 (file)
@@ -1,8 +1,9 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@zoso.dev>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
+import * as nano25519 from 'nano25519'
 import { BIP44_COIN_NANO } from '../constants'
-import { Bip39, Bip44, Blake2b, NanoNaCl, WalletAesGcm } from '../crypto'
+import { Bip39, Bip44, Blake2b, WalletAesGcm } from '../crypto'
 import { WalletType } from '../wallet'
 import { Passkey } from './passkey'
 import { VaultTimer } from './vault-timer'
@@ -193,7 +194,7 @@ export class VaultWorker {
                                        ? Bip44.ckd('Bitcoin seed', this.#seed, 0x100, index, 0, 0)
                                        : Blake2b.ckd(this.#seed, index)
                        return derive.then(prv => {
-                               const pub = NanoNaCl.convert(new Uint8Array(prv))
+                               const pub = nano25519.derive(new Uint8Array(prv))
                                this.#timer = new VaultTimer(() => this.lock(), this.#timeout)
                                return { index, publicKey: pub.buffer }
                        })
@@ -259,7 +260,7 @@ export class VaultWorker {
                                ? Blake2b.ckd(this.#seed, index)
                                : Bip44.ckd(this.#type === 'Exodus' ? 'Bitcoin seed' : 'ed25519 seed', this.#seed, BIP44_COIN_NANO, index)
                        return derive.then(prv => {
-                               const sig = NanoNaCl.detached(new Uint8Array(data), new Uint8Array(prv))
+                               const sig = nano25519.sign(new Uint8Array(data), new Uint8Array(prv))
                                this.#timer = new VaultTimer(() => this.lock(), this.#timeout)
                                return { signature: sig.buffer }
                        })