]> git.codecow.com Git - nano25519.git/commitdiff
Promote primitives to top-level module functions.
authorChris Duncan <chris@codecow.com>
Thu, 20 Aug 2026 00:08:38 +0000 (17:08 -0700)
committerChris Duncan <chris@codecow.com>
Thu, 20 Aug 2026 00:08:38 +0000 (17:08 -0700)
src/lib/nano25519.ts
src/lib/worker.ts
src/sync.ts

index f2f732402391475e6c79cab7f918711a04b305ff..4ac20b8af0f02ecbdbf1ea3697fc8797ede556b6 100644 (file)
@@ -16,210 +16,205 @@ type Exports = {
        }
 }
 
-export const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof sign, verify: typeof verify } => {
-       const wasm: Uint8Array<ArrayBuffer> = Uint8Array.from(bytes)
-       const module = new WebAssembly.Module(wasm)
-       const { exports } = new WebAssembly.Instance(module, {
-               env: {
-                       abort: (msg: any, file: any, row: any, col: any) => {
-                               const getString = (pointer: number): string | null => {
-                                       const end = pointer + new Uint32Array(exports.memory.buffer)[pointer - 4 >>> 2] >>> 1
-                                       const buf = new Uint16Array(exports.memory.buffer)
-                                       let start = pointer >>> 1
-                                       let string = ''
-                                       while (end - start > 1024) {
-                                               string += String.fromCharCode(...buf.subarray(start, start += 1024))
-                                       }
-                                       return string + String.fromCharCode(...buf.subarray(start, end))
-                               }
-                               // ~lib/builtins/abort(~lib/string/String | null?, ~lib/string/String | null?, u32?, u32?) => void
-                               msg >>>= 0
-                               file >>>= 0
-                               row >>>= 0
-                               col >>>= 0
-                               const message = `Nano25519WasmError: ${getString(msg)}, ${getString(file)}, row ${row}, col ${col}`
-                               throw new Error(message)
-                       },
-                       "performance.now" () {
-                               // ~lib/bindings/dom/performance.now() => f64
-                               return performance.now()
-                       },
-                       // ~lib/builtins/trace(~lib/string/String, i32?, f64?, f64?, f64?, f64?, f64?) => void
-                       trace: (message: any, n?: number, a0?: number, a1?: number, a2?: number, a3?: number, a4?: number): void => {
+const wasm: Uint8Array<ArrayBuffer> = Uint8Array.from(nano25519_wasm)
+const module = new WebAssembly.Module(wasm)
+const { exports } = new WebAssembly.Instance(module, {
+       env: {
+               abort: (msg: any, file: any, row: any, col: any) => {
+                       const getString = (pointer: number): string | null => {
+                               const end = pointer + new Uint32Array(exports.memory.buffer)[pointer - 4 >>> 2] >>> 1
+                               const buf = new Uint16Array(exports.memory.buffer)
+                               let start = pointer >>> 1
                                let string = ''
-                               const pointer: number = message >>> 0
-                               if (pointer) {
-                                       const end = pointer + new Uint32Array(exports.memory.buffer)[pointer - 4 >>> 2] >>> 1
-                                       const memU16 = new Uint16Array(exports.memory.buffer)
-                                       let start = pointer >>> 1
-                                       while (end - start > 1024) {
-                                               string += String.fromCharCode(...memU16.subarray(start, start += 1024))
-                                       }
-                                       message = string + String.fromCharCode(...memU16.subarray(start, end))
+                               while (end - start > 1024) {
+                                       string += String.fromCharCode(...buf.subarray(start, start += 1024))
                                }
-                               (() => {
-                                       // @external.js
-                                       console.log(message, ...[a0, a1, a2, a3, a4].slice(0, n))
-                               })()
-                       },
-                       memory: new WebAssembly.Memory({ initial: 1, maximum: 1 })
-               }
-       }) as Exports
-
-       function derive (k: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> | void {
-               if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === 32)) {
-                       throw new TypeError('Derive output buffer must be 32-byte Uint8Array<ArrayBuffer>')
-               }
-               const privateKey = new Uint8Array(32)
-               const publicKey = new Uint8Array(32)
-               let buffer = new Uint8Array(exports.memory.buffer)
-               try {
-                       privateKey.set(normalize('private key', 32, 32, k))
-                       let inPtr = exports.getInputPointer()
-                       for (let i = 0; i < 32; i++) {
-                               buffer[inPtr + i] = privateKey[i]
-                       }
-                       exports.derive()
-                       const outPtr = exports.getOutputPointer()
-                       buffer = new Uint8Array(exports.memory.buffer)
-                       for (let i = 0; i < 32; i++) {
-                               publicKey[i] = buffer[outPtr + i]
+                               return string + String.fromCharCode(...buf.subarray(start, end))
                        }
-                       if (typeof k === 'string') {
-                               let hex = ''
-                               for (const byte of publicKey) {
-                                       hex += byte.toString(16).padStart(2, '0')
+                       // ~lib/builtins/abort(~lib/string/String | null?, ~lib/string/String | null?, u32?, u32?) => void
+                       msg >>>= 0
+                       file >>>= 0
+                       row >>>= 0
+                       col >>>= 0
+                       const message = `Nano25519WasmError: ${getString(msg)}, ${getString(file)}, row ${row}, col ${col}`
+                       throw new Error(message)
+               },
+               "performance.now" () {
+                       // ~lib/bindings/dom/performance.now() => f64
+                       return performance.now()
+               },
+               // ~lib/builtins/trace(~lib/string/String, i32?, f64?, f64?, f64?, f64?, f64?) => void
+               trace: (message: any, n?: number, a0?: number, a1?: number, a2?: number, a3?: number, a4?: number): void => {
+                       let string = ''
+                       const pointer: number = message >>> 0
+                       if (pointer) {
+                               const end = pointer + new Uint32Array(exports.memory.buffer)[pointer - 4 >>> 2] >>> 1
+                               const memU16 = new Uint16Array(exports.memory.buffer)
+                               let start = pointer >>> 1
+                               while (end - start > 1024) {
+                                       string += String.fromCharCode(...memU16.subarray(start, start += 1024))
                                }
-                               return hex
-                       } else if (isBytes(k) && out != null) {
-                               out.set(publicKey)
-                               publicKey.fill(0)
-                               return
-                       } else {
-                               return publicKey
+                               message = string + String.fromCharCode(...memU16.subarray(start, end))
                        }
-               } finally {
-                       clear(buffer)
-                       privateKey.fill(0)
-               }
+                       (() => {
+                               // @external.js
+                               console.log(message, ...[a0, a1, a2, a3, a4].slice(0, n))
+                       })()
+               },
+               memory: new WebAssembly.Memory({ initial: 1, maximum: 1 })
        }
+}) as Exports
 
-       function sign (m: unknown, k: unknown, s?: unknown): string | Uint8Array<ArrayBuffer> | void {
-               if (typeof s !== 'undefined' && !(isBytes(s) && s.byteLength === 64)) {
-                       throw new TypeError('Sign output buffer must be 64-byte Uint8Array<ArrayBuffer>')
+export function derive (k: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> | void {
+       if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === 32)) {
+               throw new TypeError('Derive output buffer must be 32-byte Uint8Array<ArrayBuffer>')
+       }
+       const privateKey = new Uint8Array(32)
+       const publicKey = new Uint8Array(32)
+       let buffer = new Uint8Array(exports.memory.buffer)
+       try {
+               privateKey.set(normalize('private key', 32, 32, k))
+               let inPtr = exports.getInputPointer()
+               for (let i = 0; i < 32; i++) {
+                       buffer[inPtr + i] = privateKey[i]
                }
-               const secretKey = new Uint8Array(64)
-               const signature = new Uint8Array(64)
-               let buffer = new Uint8Array(exports.memory.buffer)
-               try {
-                       secretKey.set(normalize('secret key', 64, 64, k))
-                       let inPtr = exports.getInputPointer()
-                       for (let i = 0; i < 64; i++) {
-                               buffer[inPtr + i] = secretKey[i]
-                       }
-                       const message = normalize('message', 0, 32768, m)
-                       let mPtr = exports.getMessagePointer()
-                       for (let i = 0; i < message.byteLength; i++) {
-                               buffer[mPtr + i] = message[i]
-                       }
-                       exports.sign(message.byteLength)
-                       const outPtr = exports.getOutputPointer()
-                       buffer = new Uint8Array(exports.memory.buffer)
-                       for (let i = 0; i < 64; i++) {
-                               signature[i] = buffer[outPtr + i]
-                       }
-                       if (typeof k === 'string') {
-                               let hex = ''
-                               for (const byte of signature) {
-                                       hex += byte.toString(16).padStart(2, '0')
-                               }
-                               return hex
-                       } else if (isBytes(k) && s != null) {
-                               s.set(signature)
-                               signature.fill(0)
-                               return
-                       } else {
-                               return signature
-                       }
-               } finally {
-                       clear(buffer)
-                       secretKey.fill(0)
+               exports.derive()
+               const outPtr = exports.getOutputPointer()
+               buffer = new Uint8Array(exports.memory.buffer)
+               for (let i = 0; i < 32; i++) {
+                       publicKey[i] = buffer[outPtr + i]
                }
-       }
-
-       function verify (s: unknown, m: unknown, k: unknown): boolean {
-               let buffer = new Uint8Array(exports.memory.buffer)
-               try {
-                       const signature = normalize('signature', 64, 64, s)
-                       const message = normalize('message', 0, 32768, m)
-                       const publicKey = normalize('public key', 32, 32, k)
-                       let mPtr = exports.getMessagePointer()
-                       let inPtr = exports.getInputPointer()
-                       for (let i = 0; i < message.byteLength; i++) {
-                               buffer[mPtr + i] = message[i]
-                       }
-                       for (let i = 0; i < 64; i++) {
-                               buffer[inPtr + i] = signature[i]
-                       }
-                       inPtr += 64
-                       for (let i = 0; i < 32; i++) {
-                               buffer[inPtr + i] = publicKey[i]
+               if (typeof k === 'string') {
+                       let hex = ''
+                       for (const byte of publicKey) {
+                               hex += byte.toString(16).padStart(2, '0')
                        }
-                       const v = exports.verify(message.byteLength)
-                       return v === 0
-               } finally {
-                       clear(buffer)
+                       return hex
+               } else if (isBytes(k) && out != null) {
+                       out.set(publicKey)
+                       publicKey.fill(0)
+                       return
+               } else {
+                       return publicKey
                }
+       } finally {
+               clear(buffer)
+               privateKey.fill(0)
        }
+}
 
-       function clear (buffer: Uint8Array): void {
-               let inPtr = exports.getInputPointer()
-               let outPtr = exports.getOutputPointer()
-               buffer.fill(0, inPtr, inPtr + 96)
-               buffer.fill(0, outPtr, outPtr + 64)
-       }
-
-       function isBytes (a: unknown): a is Uint8Array<ArrayBuffer> {
-               return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer
+export function sign (m: unknown, k: unknown, s?: unknown): string | Uint8Array<ArrayBuffer> | void {
+       if (typeof s !== 'undefined' && !(isBytes(s) && s.byteLength === 64)) {
+               throw new TypeError('Sign output buffer must be 64-byte Uint8Array<ArrayBuffer>')
        }
-
-       function normalize (name: string, byteLengthMin: number, byteLengthMax: number, value: unknown): Uint8Array<ArrayBuffer> {
-               if (typeof name !== 'string') {
-                       throw new TypeError(`Invalid name ${name}`)
+       const secretKey = new Uint8Array(64)
+       const signature = new Uint8Array(64)
+       let buffer = new Uint8Array(exports.memory.buffer)
+       try {
+               secretKey.set(normalize('secret key', 64, 64, k))
+               let inPtr = exports.getInputPointer()
+               for (let i = 0; i < 64; i++) {
+                       buffer[inPtr + i] = secretKey[i]
                }
-               if (typeof byteLengthMin !== 'number') {
-                       throw new TypeError(`Invalid minimum byte length for ${name}`)
+               const message = normalize('message', 0, 32768, m)
+               let mPtr = exports.getMessagePointer()
+               for (let i = 0; i < message.byteLength; i++) {
+                       buffer[mPtr + i] = message[i]
                }
-               if (typeof byteLengthMax !== 'number') {
-                       throw new TypeError(`Invalid maximum byte length for ${name}`)
+               exports.sign(message.byteLength)
+               const outPtr = exports.getOutputPointer()
+               buffer = new Uint8Array(exports.memory.buffer)
+               for (let i = 0; i < 64; i++) {
+                       signature[i] = buffer[outPtr + i]
                }
-               if (typeof value === 'string') {
-                       if (/[^0-9a-f]/i.test(value)) {
-                               throw new TypeError(`Invalid hexadecimal characters in ${name}`)
-                       }
-                       if (value.length & 1 || value.length < (byteLengthMin << 1) || value.length > (byteLengthMax << 1)) {
-                               throw new TypeError(`Invalid hexadecimal length ${value.length} for ${name}`)
+               if (typeof k === 'string') {
+                       let hex = ''
+                       for (const byte of signature) {
+                               hex += byte.toString(16).padStart(2, '0')
                        }
-                       value = new Uint8Array(value.match(/[0-9a-f]{2}/gi)?.map(b => parseInt(b, 16)) || [])
+                       return hex
+               } else if (isBytes(k) && s != null) {
+                       s.set(signature)
+                       signature.fill(0)
+                       return
+               } else {
+                       return signature
                }
-               if (value instanceof ArrayBuffer) {
-                       value = new Uint8Array(value)
+       } finally {
+               clear(buffer)
+               secretKey.fill(0)
+       }
+}
+
+export function verify (s: unknown, m: unknown, k: unknown): boolean {
+       let buffer = new Uint8Array(exports.memory.buffer)
+       try {
+               const signature = normalize('signature', 64, 64, s)
+               const message = normalize('message', 0, 32768, m)
+               const publicKey = normalize('public key', 32, 32, k)
+               let mPtr = exports.getMessagePointer()
+               let inPtr = exports.getInputPointer()
+               for (let i = 0; i < message.byteLength; i++) {
+                       buffer[mPtr + i] = message[i]
                }
-               if (!(value instanceof Uint8Array)) {
-                       throw new TypeError(`${name} must be Uint8Array`)
+               for (let i = 0; i < 64; i++) {
+                       buffer[inPtr + i] = signature[i]
                }
-               if (!('buffer' in value && value.buffer instanceof ArrayBuffer)) {
-                       throw new TypeError(`${name} must be backed by an ArrayBuffer`)
+               inPtr += 64
+               for (let i = 0; i < 32; i++) {
+                       buffer[inPtr + i] = publicKey[i]
                }
-               if (value.byteLength < byteLengthMin) {
-                       throw new TypeError(`${name} must be at least ${byteLengthMin} bytes`)
+               const v = exports.verify(message.byteLength)
+               return v === 0
+       } finally {
+               clear(buffer)
+       }
+}
+
+function clear (buffer: Uint8Array): void {
+       let inPtr = exports.getInputPointer()
+       let outPtr = exports.getOutputPointer()
+       buffer.fill(0, inPtr, inPtr + 96)
+       buffer.fill(0, outPtr, outPtr + 64)
+}
+
+function isBytes (a: unknown): a is Uint8Array<ArrayBuffer> {
+       return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer
+}
+
+function normalize (name: string, byteLengthMin: number, byteLengthMax: number, value: unknown): Uint8Array<ArrayBuffer> {
+       if (typeof name !== 'string') {
+               throw new TypeError(`Invalid name ${name}`)
+       }
+       if (typeof byteLengthMin !== 'number') {
+               throw new TypeError(`Invalid minimum byte length for ${name}`)
+       }
+       if (typeof byteLengthMax !== 'number') {
+               throw new TypeError(`Invalid maximum byte length for ${name}`)
+       }
+       if (typeof value === 'string') {
+               if (/[^0-9a-f]/i.test(value)) {
+                       throw new TypeError(`Invalid hexadecimal characters in ${name}`)
                }
-               if (value.byteLength > byteLengthMax) {
-                       throw new TypeError(`${name} must be no more than ${byteLengthMax} bytes`)
+               if (value.length & 1 || value.length < (byteLengthMin << 1) || value.length > (byteLengthMax << 1)) {
+                       throw new TypeError(`Invalid hexadecimal length ${value.length} for ${name}`)
                }
-               return value as Uint8Array<ArrayBuffer>
+               value = new Uint8Array(value.match(/[0-9a-f]{2}/gi)?.map(b => parseInt(b, 16)) || [])
+       }
+       if (value instanceof ArrayBuffer) {
+               value = new Uint8Array(value)
        }
-       return { derive, sign, verify }
+       if (!(value instanceof Uint8Array)) {
+               throw new TypeError(`${name} must be Uint8Array`)
+       }
+       if (!('buffer' in value && value.buffer instanceof ArrayBuffer)) {
+               throw new TypeError(`${name} must be backed by an ArrayBuffer`)
+       }
+       if (value.byteLength < byteLengthMin) {
+               throw new TypeError(`${name} must be at least ${byteLengthMin} bytes`)
+       }
+       if (value.byteLength > byteLengthMax) {
+               throw new TypeError(`${name} must be no more than ${byteLengthMax} bytes`)
+       }
+       return value as Uint8Array<ArrayBuffer>
 }
-
-export const nano25519 = nano25519_init(nano25519_wasm)
index 01a2f6d78f1a184b346f5232f67135448c20da0e..3f94e36f80e743c9e94d0db2a51e29e58f79dc7a 100644 (file)
@@ -3,9 +3,7 @@
 
 import { UUID } from 'node:crypto'
 import { MessagePort as NodeMessagePort } from 'node:worker_threads'
-import { nano25519_init } from './nano25519'
-//@ts-expect-error
-import nano25519_wasm from '../../build/nano25519.wasm'
+import { derive, sign, verify } from './nano25519'
 
 type Action = 'derive' | 'sign' | 'start' | 'verify'
 
@@ -19,8 +17,6 @@ type Data = {
        signature?: string
 }
 
-const { derive, sign, verify } = nano25519_init(nano25519_wasm)
-
 let host: NodeMessagePort | null = null
 
 /**
index b857bda458d58abfa36735b9786392336617e35c..cae33f030050c474f91c564a9fb00702e88cd933 100644 (file)
@@ -1,7 +1,7 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { nano25519 } from "./lib/nano25519"
+import * as nano25519 from './lib/nano25519'
 
 /**
  * Nano public key derivation using WebAssembly.