From: Chris Duncan Date: Wed, 23 Sep 2026 18:17:21 +0000 (-0700) Subject: Switch from direct memory access to get/set exports. X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=989e7b288b0c53fd2928cf189a7b26a06497ca36;p=nano25519.git Switch from direct memory access to get/set exports. --- diff --git a/asconfig.json b/asconfig.json index 9e2ea4e..fa1b917 100644 --- a/asconfig.json +++ b/asconfig.json @@ -11,16 +11,15 @@ "sourceMap": false, "debug": false, "initialMemory": 4, - "importMemory": true, + "importMemory": false, + "noExportMemory": true, "runtime": "stub", "exportRuntime": false, "enable": [ "simd" ], "disable": [ - "mutable-globals", - "sign-extension", - "nontrapping-f2i" + "mutable-globals" ], "noColors": true } diff --git a/src/assembly/crypto_sign.ts b/src/assembly/crypto_sign.ts index c3429da..d0b3be5 100644 --- a/src/assembly/crypto_sign.ts +++ b/src/assembly/crypto_sign.ts @@ -47,7 +47,7 @@ export function crypto_sign (RS: StaticArray, M: StaticArray, mlen: i32, c |= A[i] ^ pub[i] } // Return early if `A` does not match public key - if (c != 0) return 1 + if (c != 0) return -1 // Hash private key to `h` blake2b.init().update(prv, KEY_BYTELENGTH).digest(h) diff --git a/src/assembly/crypto_verify.ts b/src/assembly/crypto_verify.ts index fa06b44..0909f77 100644 --- a/src/assembly/crypto_verify.ts +++ b/src/assembly/crypto_verify.ts @@ -72,14 +72,7 @@ export function crypto_verify_donna (sig: StaticArray, M: StaticArray, A */ export function crypto_verify_sodium (sig: StaticArray, M: StaticArray, mlen: i32, pub: StaticArray): i32 { - // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 ≤ k`) - if (!ge_is_canonical(pub)) return 0 - - // fail if public key cannot be decoded - if (ge_frombytes_negate_vartime(A, pub) != 0) return 0 - - // fail if public key `k` is small order - if (ge_has_small_order(A) != 0) return 0 + if (!verify_pubkey(pub)) return 0 // fail if private scalar `S` is non-canonical (`L ≤ S`) memory.copy(changetype(S), changetype(sig) + 32, 32) @@ -99,7 +92,57 @@ export function crypto_verify_sodium (sig: StaticArray, M: StaticArray, ge_sub_p3(check, expected_r, sb_ah) return ge_has_small_order(check) +} + +/** + * Verify signature `sig` was made by signing message `M` using public key + * `pub`. + * + * Unlike the other `crypto_verify` methods above, this function is intended for + * identity verification and signature deduplication, so malleable signatures + * are rejected using a byte-wise comparison. + * @returns 0 if signature fails to verify, else return 1 if signature is good + */ +export function crypto_verify_strict (sig: StaticArray, M: StaticArray, mlen: i32, pub: StaticArray): i32 { + + if (!verify_pubkey(pub)) return 0 + + // fail if scalar `S` is non-canonical (`L ≤ S`) + memory.copy(changetype(S), changetype(sig) + 32, 32) + if (!sc_is_canonical(S)) return 0 + + if (ge_frombytes(expected_r, sig) != 0) return 0 + if (ge_has_small_order(expected_r) != 0) return 0 + + // signature is nonce point R and scalar S (R || S) + // data to hash is nonce point R, public key A, and message M + // from parameter arguments: R = s[0,32], A = pk, M = m + // R, S, A, and M are all 32-byte values in this implementation + blake2b.init().update(sig, 32).update(pub, 32).update(M, mlen).digest(h) + sc_reduce(h) + + ge_double_scalarmult_vartime_to_p3(sb_ah, h, A, S) + ge_sub_p3(check, expected_r, sb_ah) + + ge_p3_tobytes(check_r, sb_ah) + return equalbytes(sig, check_r, KEY_BYTELENGTH) +} + +/** + * Verify public key `k` is canonical, decodeable, and not small order. Used by + * `crypto_verify_sodium` and `crypto_verify_strict`. + * @returns 0 if pubkey fails checks, else return 1 if pubkey is good + */ +function verify_pubkey (pub: StaticArray): boolean { + + // fail if public key `k` is non-canonical (`p = 2²⁵⁵-19 ≤ k`) + if (!ge_is_canonical(pub)) return false + + // fail if public key cannot be decoded + if (ge_frombytes_negate_vartime(A, pub) != 0) return false + + // fail if public key `k` is small order + if (ge_has_small_order(A) != 0) return false - // ge_p3_tobytes(check_r, sb_ah) - // return equalbytes(sig, check_r, KEY_BYTELENGTH) + return true } diff --git a/src/assembly/index.ts b/src/assembly/index.ts index dfc27d1..5eaf811 100644 --- a/src/assembly/index.ts +++ b/src/assembly/index.ts @@ -5,7 +5,6 @@ import { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIF import { crypto_derive, } from './crypto_derive' import { crypto_sign, } from './crypto_sign' import { crypto_verify_decodepubkey, crypto_verify_donna, crypto_verify_sodium, } from './crypto_verify' -import { errors, } from './errors' import './tests' export { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIFY_BLOCKS, MAX_VERIFY_BLOCKS_BYTELENGTH, SIGNATURE_BYTELENGTH } @@ -20,39 +19,76 @@ const OUTPUT_DERIVE = new StaticArray(KEY_BYTELENGTH) const OUTPUT_SIGN = new StaticArray(SIGNATURE_BYTELENGTH) const OUTPUT_VERIFY = new StaticArray(MAX_VERIFY_BLOCKS) -/** Returns a pointer to the static message input buffer. */ -export function ptrInputMsg (): usize { - return changetype(INPUT_MSG) +/** Sets a byte in the static message input buffer. */ +export function setInputMsgByte (index: i32, v0: i32): i32 { + if (index < 0 || MAX_MESSAGE_BYTELENGTH <= index) { + return -1 + } + if (v0 < 0 || 0xff < v0) { + return -2 + } + INPUT_MSG[index] = u8(v0) + return 0 } -/** Returns a pointer to the static private key input buffer (32 bytes). */ -export function ptrInputPrv (): usize { - return changetype(INPUT_PRV) +/** Sets a byte in the static private key input buffer (32 bytes). */ +export function setInputPrvByte (index: i32, value: i32): i32 { + if (index < 0 || KEY_BYTELENGTH <= index) { + return -1 + } + if (value < 0 || 0xff < value) { + return -2 + } + INPUT_PRV[index] = u8(value) + return 0 } -/** Returns a pointer to the static public key input buffer (32 bytes). */ -export function ptrInputPub (): usize { - return changetype(INPUT_PUB) +/** Sets a byte in the static public key input buffer (32 bytes). */ +export function setInputPubByte (index: i32, value: i32): i32 { + if (index < 0 || KEY_BYTELENGTH <= index) { + return -1 + } + if (value < 0 || 0xff < value) { + return -2 + } + INPUT_PUB[index] = u8(value) + return 0 } -/** Returns a pointer to the static signature input buffer (64 bytes). */ -export function ptrInputSig (): usize { - return changetype(INPUT_SIG) +/** Sets a byte in the static signature input buffer (64 bytes). */ +export function setInputSigByte (index: i32, value: i32): i32 { + if (index < 0 || SIGNATURE_BYTELENGTH <= index) { + return -1 + } + if (value < 0 || 0xff < value) { + return -2 + } + INPUT_SIG[index] = u8(value) + return 0 } -/** Returns a pointer to the derive() static output buffer (32 bytes). */ -export function ptrOutputDerive (): usize { - return changetype(OUTPUT_DERIVE) +/** Gets a byte from the derive() static output buffer (32 bytes). */ +export function getOutputDeriveByte (index: i32): i32 { + if (index < 0 || KEY_BYTELENGTH <= index) { + return -1 + } + return OUTPUT_DERIVE[index] } -/** Returns a pointer to the sign() static output buffer (64 bytes). */ -export function ptrOutputSign (): usize { - return changetype(OUTPUT_SIGN) +/** Gets a byte from the sign() static output buffer (64 bytes). */ +export function getOutputSignByte (index: i32): i32 { + if (index < 0 || SIGNATURE_BYTELENGTH <= index) { + return -1 + } + return OUTPUT_SIGN[index] } -/** Returns a pointer to the verify() static output buffer (64 bytes). */ -export function ptrOutputVerify (): usize { - return changetype(OUTPUT_VERIFY) +/** Gets a byte from the verify() static output buffer (64 bytes). */ +export function getOutputVerifyByte (index: i32): i32 { + if (index < 0 || MAX_VERIFY_BLOCKS <= index) { + return -1 + } + return OUTPUT_VERIFY[index] } const h = new StaticArray(BLOCKHASH_BYTELENGTH) @@ -102,7 +138,7 @@ export function derive (): void { * from the message and written to the output buffer. * @param {i32} mlen Byte length of message to be signed, up to 65536 */ -export function sign (mlen: i32): void { +export function sign (mlen: i32): i32 { // Clear output buffer so errors do not retain stale prior data OUTPUT_SIGN.fill(0) @@ -116,7 +152,7 @@ export function sign (mlen: i32): void { INPUT_PRV.fill(0) INPUT_PUB.fill(0) INPUT_MSG.fill(0) - throw new Error(errors[2]) + return -2 } // Copy input buffers to local parameters, then clear input buffer @@ -134,8 +170,8 @@ export function sign (mlen: i32): void { // Clear signature output buffer if signing failed if (result != 0) { OUTPUT_SIGN.fill(0) - throw new Error(errors[result]) } + return result } /** @@ -144,7 +180,7 @@ export function sign (mlen: i32): void { * @param {i32} mlen Byte length of message that was signed, up to 65536 * @returns {boolean} True if message was signed by public key's private key */ -export function verify (mlen: i32): void { +export function verify (mlen: i32): i32 { // Clear output buffer so errors do not retain stale prior data OUTPUT_VERIFY.fill(0) @@ -156,7 +192,7 @@ export function verify (mlen: i32): void { if (mlen < 0 || mlen > MAX_MESSAGE_BYTELENGTH) { INPUT_MSG.fill(0) INPUT_PUB.fill(0) - throw new Error(errors[2]) + return -2 } // Copy input buffer to local parameters, then clear input buffer @@ -172,6 +208,7 @@ export function verify (mlen: i32): void { INPUT_MSG.fill(0) pub.fill(0) sig.fill(0) + return 0 } /** @@ -191,7 +228,7 @@ export function verify (mlen: i32): void { * to the output buffer in the same order they were received. * @param {i32} count Number of signatures to verify, up to 64 */ -export function verify_blocks (count: i32): void { +export function verify_blocks (count: i32): i32 { // Clear output buffer so errors do not retain stale prior data OUTPUT_VERIFY.fill(0) @@ -204,7 +241,7 @@ export function verify_blocks (count: i32): void { if (count < 1 || MAX_VERIFY_BLOCKS < count) { INPUT_MSG.fill(0) INPUT_PUB.fill(0) - throw new Error(errors[3]) + return -3 } // Copy public key input buffer to local parameter, then clear input buffer @@ -230,4 +267,5 @@ export function verify_blocks (count: i32): void { h.fill(0) pub.fill(0) sig.fill(0) + return 0 } diff --git a/src/lib/derive.ts b/src/lib/derive.ts index b47a79a..74d0397 100644 --- a/src/lib/derive.ts +++ b/src/lib/derive.ts @@ -1,8 +1,8 @@ //! SPDX-FileCopyrightText: 2026 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { allocate, byteLength, bytesToHex, copy, fill, isBytes, memoryBuffer, normalize, validateMemoryBuffer, view, } from './primordials' -import { Mutex, Pointers, clearMemory, constants, exports, memory, } from './wasm' +import { allocate, byteLength, bytesToHex, copy, fill, isBytes, normalize } from './primordials' +import { MemoryBuffers, Mutex, clearMemory, constants, derive as wasm_derive, } from './wasm' const { KEY_BYTELENGTH, } = constants @@ -12,16 +12,14 @@ export function derive (prv: unknown, out?: unknown): string | void { } const privateKey = normalize('private key', KEY_BYTELENGTH, KEY_BYTELENGTH, prv) const publicKey = allocate(KEY_BYTELENGTH) - let buffer = view(memoryBuffer(memory)) Mutex.lock() try { for (let i = 0; i < KEY_BYTELENGTH; i++) { - buffer[Pointers.INPUT_PRV + i] = privateKey[i] + MemoryBuffers.INPUT_PRV(i)(privateKey[i]) } - exports.derive() - validateMemoryBuffer(memory, buffer) + wasm_derive() for (let i = 0; i < KEY_BYTELENGTH; i++) { - publicKey[i] = buffer[Pointers.OUTPUT_DERIVE + i] + publicKey[i] = MemoryBuffers.OUTPUT_DERIVE(i) } if (out != null) { copy(out, publicKey) diff --git a/src/lib/primordials.ts b/src/lib/primordials.ts index 64e4e3a..cab2ca8 100644 --- a/src/lib/primordials.ts +++ b/src/lib/primordials.ts @@ -43,6 +43,7 @@ export type Bytes = Uint8Array /** Declared here to avoid import loops. */ export class Nano25519TypeError extends TypeError { } +export class Nano25519WasmError extends Error { } const isArray = Array.isArray diff --git a/src/lib/sign.ts b/src/lib/sign.ts index cd12b2a..b9e5a04 100644 --- a/src/lib/sign.ts +++ b/src/lib/sign.ts @@ -1,8 +1,8 @@ //! SPDX-FileCopyrightText: 2026 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { allocate, byteLength, bytesToHex, copy, fill, isBytes, memoryBuffer, normalize, validateMemoryBuffer, view, } from './primordials' -import { Mutex, Pointers, clearMemory, constants, exports, memory, } from './wasm' +import { allocate, byteLength, bytesToHex, copy, fill, isBytes, Nano25519WasmError, normalize } from './primordials' +import { clearMemory, constants, MemoryBuffers, Mutex, sign as wasm_sign } from './wasm' const { KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, SIGNATURE_BYTELENGTH, } = constants @@ -14,22 +14,23 @@ export function sign (msg: unknown, prv: unknown, pub: unknown, out?: unknown): const privateKey = normalize('private key', KEY_BYTELENGTH, KEY_BYTELENGTH, prv) const publicKey = normalize('public key', KEY_BYTELENGTH, KEY_BYTELENGTH, pub) const signature = allocate(SIGNATURE_BYTELENGTH) - let buffer = view(memoryBuffer(memory)) Mutex.lock() try { for (let i = 0; i < message.byteLength; i++) { - buffer[Pointers.INPUT_MSG + i] = message[i] + MemoryBuffers.INPUT_MSG(i)(message[i]) } for (let i = 0; i < KEY_BYTELENGTH; i++) { - buffer[Pointers.INPUT_PRV + i] = privateKey[i] + MemoryBuffers.INPUT_PRV(i)(privateKey[i]) } for (let i = 0; i < KEY_BYTELENGTH; i++) { - buffer[Pointers.INPUT_PUB + i] = publicKey[i] + MemoryBuffers.INPUT_PUB(i)(publicKey[i]) + } + const result = wasm_sign(message.byteLength) + if (result !== 0) { + throw new Nano25519WasmError('Invalid public key') } - exports.sign(message.byteLength) - validateMemoryBuffer(memory, buffer) for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) { - signature[i] = buffer[Pointers.OUTPUT_SIGN + i] + signature[i] = MemoryBuffers.OUTPUT_SIGN(i) } if (out != null) { copy(out, signature) diff --git a/src/lib/verify.ts b/src/lib/verify.ts index 8356a00..bee6f20 100644 --- a/src/lib/verify.ts +++ b/src/lib/verify.ts @@ -1,30 +1,28 @@ //! SPDX-FileCopyrightText: 2026 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { isArray, memoryBuffer, normalize, validateMemoryBuffer, view } from './primordials' -import { Mutex, Pointers, clearMemory, constants, exports, memory, } from './wasm' +import { isArray, normalize } from './primordials' +import { clearMemory, constants, MemoryBuffers, Mutex, verify as wasm_verify, verify_blocks as wasm_verify_blocks, } from './wasm' const { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIFY_BLOCKS, SIGNATURE_BYTELENGTH, } = constants export function verify (sig: unknown, msg: unknown, pub: unknown): boolean { - let buffer = view(memoryBuffer(memory)) Mutex.lock() try { 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[Pointers.INPUT_MSG + i] = message[i] + MemoryBuffers.INPUT_MSG(i)(message[i]) } for (let i = 0; i < KEY_BYTELENGTH; i++) { - buffer[Pointers.INPUT_PUB + i] = publicKey[i] + MemoryBuffers.INPUT_PUB(i)(publicKey[i]) } for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) { - buffer[Pointers.INPUT_SIG + i] = signature[i] + MemoryBuffers.INPUT_SIG(i)(signature[i]) } - exports.verify(message.byteLength) - validateMemoryBuffer(memory, buffer) - const verified = buffer[Pointers.OUTPUT_VERIFY] + wasm_verify(message.byteLength) + const verified = MemoryBuffers.OUTPUT_VERIFY(0) return verified === 1 } finally { clearMemory() @@ -41,14 +39,13 @@ export function verify_blocks (pub: unknown, data: unknown): boolean[] { throw new RangeError(`Bulk Nano block verification must be no more than ${MAX_VERIFY_BLOCKS} blocks`, { cause: count }) } const verified: boolean[] = [] - let buffer = view(memoryBuffer(memory)) Mutex.lock() try { const publicKey = normalize('public key', KEY_BYTELENGTH, KEY_BYTELENGTH, pub) for (let i = 0; i < KEY_BYTELENGTH; i++) { - buffer[Pointers.INPUT_PUB + i] = publicKey[i] + MemoryBuffers.INPUT_PUB(i)(publicKey[i]) } - for (let b = 0, p = Pointers.INPUT_MSG; b < count; b++) { + for (let b = 0, p = 0; b < count; b++) { const block = blocks[b] if (block == null || typeof block !== 'object') { throw new TypeError('Invalid block', { cause: block }) @@ -62,18 +59,17 @@ export function verify_blocks (pub: unknown, data: unknown): boolean[] { const signature = normalize('signature', SIGNATURE_BYTELENGTH, SIGNATURE_BYTELENGTH, block.signature) const hash = normalize('hash', BLOCKHASH_BYTELENGTH, BLOCKHASH_BYTELENGTH, block.hash) for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) { - buffer[p + i] = signature[i] + MemoryBuffers.INPUT_MSG(p + i)(signature[i]) } p += SIGNATURE_BYTELENGTH for (let i = 0; i < BLOCKHASH_BYTELENGTH; i++) { - buffer[p + i] = hash[i] + MemoryBuffers.INPUT_MSG(p + i)(hash[i]) } p += BLOCKHASH_BYTELENGTH } - exports.verify_blocks(count) - validateMemoryBuffer(memory, buffer) + wasm_verify_blocks(count) for (let i = 0; i < count; i++) { - verified[i] = buffer[Pointers.OUTPUT_VERIFY + i] === 1 + verified[i] = MemoryBuffers.OUTPUT_VERIFY(i) === 1 } return verified } finally { diff --git a/src/lib/wasm.ts b/src/lib/wasm.ts index 7eb7a2a..16e67d2 100644 --- a/src/lib/wasm.ts +++ b/src/lib/wasm.ts @@ -3,32 +3,28 @@ //@ts-expect-error import nano25519_wasm from '../../build/nano25519.wasm' -import { Nano25519TypeError, copy, fill, memoryBuffer, readLong, utf16, view, } from './primordials' +import { Nano25519TypeError, copy } from './primordials' -type Exports = { - exports: { - derive: () => void - sign: (mlen: number) => void - verify: (mlen: number) => void - verify_blocks: (count: number) => void - ptrInputMsg: () => number - ptrInputPrv: () => number - ptrInputPub: () => number - ptrInputSig: () => number - ptrOutputDerive: () => number - ptrOutputSign: () => number - ptrOutputVerify: () => number - BLOCKHASH_BYTELENGTH: WebAssembly.Global - KEY_BYTELENGTH: WebAssembly.Global - SIGNATURE_BYTELENGTH: WebAssembly.Global - MAX_VERIFY_BLOCKS: WebAssembly.Global - MAX_VERIFY_BLOCKS_BYTELENGTH: WebAssembly.Global - MAX_MESSAGE_BYTELENGTH: WebAssembly.Global - } +type Exports = WebAssembly.Instance['exports'] & { + derive: () => void + sign: (mlen: number) => number + verify: (mlen: number) => number + verify_blocks: (count: number) => number + setInputMsgByte: (index: number, value: number) => number + setInputPrvByte: (index: number, value: number) => number + setInputPubByte: (index: number, value: number) => number + setInputSigByte: (index: number, value: number) => number + getOutputDeriveByte: (index: number) => number + getOutputSignByte: (index: number) => number + getOutputVerifyByte: (index: number) => number + BLOCKHASH_BYTELENGTH: WebAssembly.Global + KEY_BYTELENGTH: WebAssembly.Global + SIGNATURE_BYTELENGTH: WebAssembly.Global + MAX_VERIFY_BLOCKS: WebAssembly.Global + MAX_VERIFY_BLOCKS_BYTELENGTH: WebAssembly.Global + MAX_MESSAGE_BYTELENGTH: WebAssembly.Global } -export { Nano25519TypeError } from './primordials' - let locked = false export const Mutex = { lock (): void { @@ -37,37 +33,11 @@ export const Mutex = { } } -/** - * WASM memory created here and imported, instead of building and exporting, so - * that top-level WASM abort calls work while `exports` is still instantiating. - * Host-owned memory means abort handler can always read the error message. - * - * NOTE: `initial` must match `initialMemory` in `asconfig.json`. - */ -export const memory = new WebAssembly.Memory({ initial: 4 }) - const wasm = copy(nano25519_wasm) const module = new WebAssembly.Module(wasm) -export const { exports } = new WebAssembly.Instance(module, { - env: { - memory, - abort: (msg: any, file: any, row: any, col: any): never => { - const str = (pointer: number): string | null => { - const end = pointer + readLong(memoryBuffer(memory), pointer - 4) >>> 1 - const start = pointer >>> 1 - return utf16(memoryBuffer(memory), start, end - start) - } - // ~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: ${str(msg)}, ${str(file)}, row ${row}, col ${col}` - throw new Error(message) - } - } -}) as Exports +const instance = new WebAssembly.Instance(module, { env: { abort: () => { } } }) +const exports = instance.exports as Exports const BLOCKHASH_BYTELENGTH = exports.BLOCKHASH_BYTELENGTH.value const KEY_BYTELENGTH = exports.KEY_BYTELENGTH.value @@ -86,36 +56,54 @@ export const constants = { MAX_MESSAGE_BYTELENGTH } -const INPUT_MSG = exports.ptrInputMsg() -const INPUT_PRV = exports.ptrInputPrv() -const INPUT_PUB = exports.ptrInputPub() -const INPUT_SIG = exports.ptrInputSig() -const OUTPUT_DERIVE = exports.ptrOutputDerive() -const OUTPUT_SIGN = exports.ptrOutputSign() -const OUTPUT_VERIFY = exports.ptrOutputVerify() - -export class Pointers { - static #get (ptr: number): number { - if (locked) return ptr - throw new Nano25519TypeError('Mutex not acquired') +export class MemoryBuffers { + static #check (): void { + if (!locked) throw new Nano25519TypeError('Mutex not acquired') + } + static get INPUT_MSG () { + this.#check() + return (i: number) => (v: number) => exports.setInputMsgByte(i, v) + } + static get INPUT_PRV () { + this.#check() + return (i: number) => (v: number) => exports.setInputPrvByte(i, v) + } + static get INPUT_PUB () { + this.#check() + return (i: number) => (v: number) => exports.setInputPubByte(i, v) + } + static get INPUT_SIG () { + this.#check() + return (i: number) => (v: number) => exports.setInputSigByte(i, v) + } + static get OUTPUT_DERIVE () { + this.#check() + return (i: number) => exports.getOutputDeriveByte(i) + } + static get OUTPUT_SIGN () { + this.#check() + return (i: number) => exports.getOutputSignByte(i) + } + static get OUTPUT_VERIFY () { + this.#check() + return (i: number) => exports.getOutputVerifyByte(i) } - 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 clearMemory (): void { - const bytes = view(memoryBuffer(memory)) - fill(bytes, 0, INPUT_MSG, INPUT_MSG + MAX_MESSAGE_BYTELENGTH) - fill(bytes, 0, INPUT_PRV, INPUT_PRV + KEY_BYTELENGTH) - fill(bytes, 0, INPUT_PUB, INPUT_PUB + KEY_BYTELENGTH) - fill(bytes, 0, INPUT_SIG, INPUT_SIG + SIGNATURE_BYTELENGTH) - fill(bytes, 0, OUTPUT_DERIVE, OUTPUT_DERIVE + KEY_BYTELENGTH) - fill(bytes, 0, OUTPUT_SIGN, OUTPUT_SIGN + SIGNATURE_BYTELENGTH) - fill(bytes, 0, OUTPUT_VERIFY, OUTPUT_VERIFY + MAX_VERIFY_BLOCKS) + for (let i = 0; i < MAX_MESSAGE_BYTELENGTH; i++) { + MemoryBuffers.INPUT_MSG(i)(0) + } + for (let i = 0; i < KEY_BYTELENGTH; i++) { + MemoryBuffers.INPUT_PRV(i)(0) + } + for (let i = 0; i < KEY_BYTELENGTH; i++) { + MemoryBuffers.INPUT_PUB(i)(0) + } + for (let i = 0; i < SIGNATURE_BYTELENGTH; i++) { + MemoryBuffers.INPUT_SIG(i)(0) + } locked = false } + +export const { derive, sign, verify, verify_blocks } = exports diff --git a/test/node.mjs b/test/node.mjs index 802eb11..cc82c92 100644 --- a/test/node.mjs +++ b/test/node.mjs @@ -164,7 +164,7 @@ try { result = false } catch (err) { if (err != null && typeof err === 'object' && 'message' in err && typeof err.message === 'string') { - result = err.message.includes('Nano25519WasmError') && err.message.includes('Invalid public key') + result = err.constructor.name === 'Nano25519WasmError' && err.message === 'Invalid public key' } else { result = false }