]> git.codecow.com Git - nano25519.git/commitdiff
Throw on invalid output buffer immediately. Wrap signing to always zero key and buffer.
authorChris Duncan <chris@zoso.dev>
Thu, 13 Aug 2026 04:44:49 +0000 (21:44 -0700)
committerChris Duncan <chris@zoso.dev>
Thu, 13 Aug 2026 04:44:49 +0000 (21:44 -0700)
src/lib/nano25519.ts

index 61fa85fdd6e97e3e125625563590712704465940..4c8e2a4967bebda0a2913bec60fa17f585e9571f 100644 (file)
@@ -68,13 +68,13 @@ const nano25519_init = (bytes: number[]): { derive: typeof derive, sign: typeof
        }) as Exports
 
        function derive (k: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> {
+               out ??= new Uint8Array(32)
+               if (!(isBytes(out) && out.byteLength === 32)) {
+                       throw new TypeError('Derive output buffer must be 32-byte Uint8Array<ArrayBuffer>')
+               }
                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<ArrayBuffer>')
-                       }
                        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<ArrayBuffer> {
-               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<ArrayBuffer>')
                }
-               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 {