From: Chris Duncan Date: Thu, 20 Aug 2026 00:08:38 +0000 (-0700) Subject: Promote primitives to top-level module functions. X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=b0db910ab7409f93f020e993a327a048b69248ee;p=nano25519.git Promote primitives to top-level module functions. --- diff --git a/src/lib/nano25519.ts b/src/lib/nano25519.ts index f2f7324..4ac20b8 100644 --- a/src/lib/nano25519.ts +++ b/src/lib/nano25519.ts @@ -16,210 +16,205 @@ type Exports = { } } -export const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof sign, verify: typeof verify } => { - const wasm: Uint8Array = 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 = 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 | void { - if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === 32)) { - throw new TypeError('Derive output buffer must be 32-byte Uint8Array') - } - 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 | void { - if (typeof s !== 'undefined' && !(isBytes(s) && s.byteLength === 64)) { - throw new TypeError('Sign output buffer must be 64-byte Uint8Array') +export function derive (k: unknown, out?: unknown): string | Uint8Array | void { + if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === 32)) { + throw new TypeError('Derive output buffer must be 32-byte Uint8Array') + } + 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 { - return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer +export function sign (m: unknown, k: unknown, s?: unknown): string | Uint8Array | void { + if (typeof s !== 'undefined' && !(isBytes(s) && s.byteLength === 64)) { + throw new TypeError('Sign output buffer must be 64-byte Uint8Array') } - - function normalize (name: string, byteLengthMin: number, byteLengthMax: number, value: unknown): Uint8Array { - 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 { + return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer +} + +function normalize (name: string, byteLengthMin: number, byteLengthMax: number, value: unknown): Uint8Array { + 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 + 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 } - -export const nano25519 = nano25519_init(nano25519_wasm) diff --git a/src/lib/worker.ts b/src/lib/worker.ts index 01a2f6d..3f94e36 100644 --- a/src/lib/worker.ts +++ b/src/lib/worker.ts @@ -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 /** diff --git a/src/sync.ts b/src/sync.ts index b857bda..cae33f0 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -1,7 +1,7 @@ //! SPDX-FileCopyrightText: 2026 Chris Duncan //! 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.