//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { clear, constants, exports, isBytes, normalize, pointers, } from './wasm'
+import { Mutex, Pointers, clear, constants, exports, isBytes, normalize } from './wasm'
const { KEY_BYTELENGTH, } = constants
-const { INPUT_PRV, OUTPUT_DERIVE, } = pointers
export function derive (prv: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> | void {
if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === KEY_BYTELENGTH)) {
const publicKey = new Uint8Array(KEY_BYTELENGTH)
let buffer = new Uint8Array(exports.memory.buffer)
try {
+ Mutex.lock()
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- buffer[INPUT_PRV + i] = privateKey[i]
+ buffer[Pointers.INPUT_PRV + i] = privateKey[i]
}
exports.derive()
buffer = new Uint8Array(exports.memory.buffer)
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- publicKey[i] = buffer[OUTPUT_DERIVE + i]
+ publicKey[i] = buffer[Pointers.OUTPUT_DERIVE + i]
}
if (typeof prv === 'string') {
let hex = ''
return publicKey
}
} finally {
- clear(buffer)
privateKey.fill(0)
+ clear(buffer)
}
}
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { clear, constants, exports, isBytes, normalize, pointers, } from './wasm'
+import { Mutex, Pointers, clear, constants, exports, isBytes, normalize } from './wasm'
const { KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, SIGNATURE_BYTELENGTH, } = constants
-const { INPUT_MSG, INPUT_PRV, INPUT_PUB, OUTPUT_SIGN, } = pointers
export function sign (msg: unknown, prv: unknown, pub: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> | void {
if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === SIGNATURE_BYTELENGTH)) {
const signature = new Uint8Array(SIGNATURE_BYTELENGTH)
let buffer = new Uint8Array(exports.memory.buffer)
try {
+ Mutex.lock()
for (let i = 0; i < message.byteLength; i++) {
- buffer[INPUT_MSG + i] = message[i]
+ buffer[Pointers.INPUT_MSG + i] = message[i]
}
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- buffer[INPUT_PRV + i] = privateKey[i]
+ buffer[Pointers.INPUT_PRV + i] = privateKey[i]
}
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- buffer[INPUT_PUB + i] = publicKey[i]
+ buffer[Pointers.INPUT_PUB + i] = publicKey[i]
}
exports.sign(message.byteLength)
buffer = new Uint8Array(exports.memory.buffer)
for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) {
- signature[i] = buffer[OUTPUT_SIGN + i]
+ signature[i] = buffer[Pointers.OUTPUT_SIGN + i]
}
if (typeof prv === 'string') {
let hex = ''
return signature
}
} finally {
- clear(buffer)
privateKey.fill(0)
publicKey.fill(0)
+ clear(buffer)
}
}
//! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { clear, constants, exports, normalize, pointers, } from './wasm'
+import { Mutex, Pointers, clear, constants, exports, normalize, } from './wasm'
const { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIFY_BLOCKS, SIGNATURE_BYTELENGTH, } = constants
-const { INPUT_MSG, INPUT_PUB, INPUT_SIG, OUTPUT_VERIFY, } = pointers
export function verify (sig: unknown, msg: unknown, pub: unknown): boolean {
let buffer = new Uint8Array(exports.memory.buffer)
try {
+ Mutex.lock()
const message = normalize('message', 0, MAX_MESSAGE_BYTELENGTH, msg)
const publicKey = normalize('public key', KEY_BYTELENGTH, KEY_BYTELENGTH, pub)
const signature = normalize('signature', SIGNATURE_BYTELENGTH, SIGNATURE_BYTELENGTH, sig)
for (let i = 0; i < message.byteLength; i++) {
- buffer[INPUT_MSG + i] = message[i]
+ buffer[Pointers.INPUT_MSG + i] = message[i]
}
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- buffer[INPUT_PUB + i] = publicKey[i]
+ buffer[Pointers.INPUT_PUB + i] = publicKey[i]
}
for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) {
- buffer[INPUT_SIG + i] = signature[i]
+ buffer[Pointers.INPUT_SIG + i] = signature[i]
}
exports.verify(message.byteLength)
buffer = new Uint8Array(exports.memory.buffer)
- const verified = buffer[OUTPUT_VERIFY]
+ const verified = buffer[Pointers.OUTPUT_VERIFY]
return verified === 0
} finally {
clear(buffer)
const verified = new Uint8Array(count)
let buffer = new Uint8Array(exports.memory.buffer)
try {
+ Mutex.lock()
const publicKey = normalize('public key', KEY_BYTELENGTH, KEY_BYTELENGTH, pub)
for (let i = 0; i < KEY_BYTELENGTH; i++) {
- buffer[INPUT_PUB + i] = publicKey[i]
+ buffer[Pointers.INPUT_PUB + i] = publicKey[i]
}
- for (let b = 0, p = INPUT_MSG; b < count; b++) {
+ for (let b = 0, p = Pointers.INPUT_MSG; b < count; b++) {
const block = blocks[b]
if (block == null || typeof block !== 'object') {
throw new TypeError('Invalid block', { cause: block })
exports.verify_blocks(count)
buffer = new Uint8Array(exports.memory.buffer)
for (let i = 0; i < count; i++) {
- verified[i] = buffer[OUTPUT_VERIFY + i]
+ verified[i] = buffer[Pointers.OUTPUT_VERIFY + i]
}
return [...verified].map(v => v === 0)
} finally {
}
}
+export class Nano25519TypeError extends TypeError { }
+
+let locked = false
+export const Mutex = {
+ lock (): void {
+ if (locked) throw new Nano25519TypeError('Failed to acquire mutex')
+ locked = true
+ }
+}
+
const wasm: Uint8Array<ArrayBuffer> = Uint8Array.from(nano25519_wasm)
const module = new WebAssembly.Module(wasm)
const OUTPUT_SIGN = exports.ptrOutputSign()
const OUTPUT_VERIFY = exports.ptrOutputVerify()
-export const pointers = {
- INPUT_MSG,
- INPUT_PRV,
- INPUT_PUB,
- INPUT_SIG,
- OUTPUT_DERIVE,
- OUTPUT_SIGN,
- OUTPUT_VERIFY,
+export class Pointers {
+ static #get (ptr: number): number {
+ if (locked) return ptr
+ throw new Nano25519TypeError('Mutex not acquired')
+ }
+ static get INPUT_MSG (): number { return this.#get(INPUT_MSG) }
+ static get INPUT_PRV (): number { return this.#get(INPUT_PRV) }
+ static get INPUT_PUB (): number { return this.#get(INPUT_PUB) }
+ static get INPUT_SIG (): number { return this.#get(INPUT_SIG) }
+ static get OUTPUT_DERIVE (): number { return this.#get(OUTPUT_DERIVE) }
+ static get OUTPUT_SIGN (): number { return this.#get(OUTPUT_SIGN) }
+ static get OUTPUT_VERIFY (): number { return this.#get(OUTPUT_VERIFY) }
}
export function clear (memory: Uint8Array): void {
memory.fill(0, OUTPUT_DERIVE, OUTPUT_DERIVE + KEY_BYTELENGTH)
memory.fill(0, OUTPUT_SIGN, OUTPUT_SIGN + SIGNATURE_BYTELENGTH)
memory.fill(255, OUTPUT_VERIFY, OUTPUT_VERIFY + MAX_VERIFY_BLOCKS)
+ locked = false
}
export function isBytes (a: unknown): a is Uint8Array<ArrayBuffer> {