From 5a3e7a4d3ed86c6923fda13942e45cd81a52d51e Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Wed, 12 Aug 2026 21:44:49 -0700 Subject: [PATCH] Throw on invalid output buffer immediately. Wrap signing to always zero key and buffer. --- src/lib/nano25519.ts | 56 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/lib/nano25519.ts b/src/lib/nano25519.ts index 61fa85f..4c8e2a4 100644 --- a/src/lib/nano25519.ts +++ b/src/lib/nano25519.ts @@ -68,13 +68,13 @@ const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof }) as Exports function derive (k: unknown, out?: unknown): string | Uint8Array { + out ??= new Uint8Array(32) + if (!(isBytes(out) && out.byteLength === 32)) { + throw new TypeError('Derive output buffer must be 32-byte Uint8Array') + } let privateKey = new Uint8Array(32) let buffer = new DataView(exports.memory.buffer) try { - out ??= new Uint8Array(32) - if (!(isBytes(out) && out.byteLength === 32)) { - throw new TypeError('Derive output buffer must be 32-byte Uint8Array') - } privateKey.set(normalize('private key', 32, 32, k)) let inPtr = exports.getInputPointer() for (let i = 0; i < 32; i++) { @@ -104,32 +104,38 @@ const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof } function sign (m: unknown, k: unknown, s?: unknown): string | Uint8Array { - let secretKey = new Uint8Array(64) - let buffer: DataView | undefined = new DataView(exports.memory.buffer) - secretKey.set(normalize('secret key', 64, 64, k)) - const message = normalize('message', 0, 32768, m) - let mPtr = exports.getMessagePointer() - let inPtr = exports.getInputPointer() - for (let i = 0; i < message.byteLength; i++) { - buffer.setUint8(mPtr + i, message[i]) - } - for (let i = 0; i < 64; i++) { - buffer.setUint8(inPtr + i, secretKey[i]) - } - exports.sign(message.byteLength) - const outPtr = exports.getOutputPointer() s ??= new Uint8Array(64) if (!(isBytes(s) && s.byteLength === 64)) { throw new TypeError('Sign output buffer must be 64-byte Uint8Array') } - buffer = new DataView(exports.memory.buffer) - for (let i = 0; i < 64; i++) { - s[i] = buffer.getUint8(outPtr + i) + let secretKey = new Uint8Array(64) + let buffer: DataView | undefined = new DataView(exports.memory.buffer) + try { + secretKey.set(normalize('secret key', 64, 64, k)) + let inPtr = exports.getInputPointer() + for (let i = 0; i < 64; i++) { + buffer.setUint8(inPtr + i, secretKey[i]) + } + secretKey.fill(0) + const message = normalize('message', 0, 32768, m) + let mPtr = exports.getMessagePointer() + for (let i = 0; i < message.byteLength; i++) { + buffer.setUint8(mPtr + i, message[i]) + } + exports.sign(message.byteLength) + const outPtr = exports.getOutputPointer() + buffer = new DataView(exports.memory.buffer) + for (let i = 0; i < 64; i++) { + s[i] = buffer.getUint8(outPtr + i) + } + clear(buffer) + return typeof k === 'string' + ? [...s].map(b => b.toString(16).padStart(2, '0')).join('') + : s + } finally { + secretKey.fill(0) + clear(buffer) } - clear(buffer) - return typeof k === 'string' - ? [...s].map(b => b.toString(16).padStart(2, '0')).join('') - : s } function verify (s: unknown, m: unknown, k: unknown): boolean { -- 2.52.0