]> git.codecow.com Git - nano25519.git/commitdiff
Declare I/O buffers as StaticArray for memory safety. Set memory with live value...
authorChris Duncan <chris@codecow.com>
Thu, 20 Aug 2026 04:20:49 +0000 (21:20 -0700)
committerChris Duncan <chris@codecow.com>
Thu, 20 Aug 2026 04:20:49 +0000 (21:20 -0700)
asconfig.json
src/assembly/index.ts
src/lib/nano25519.ts
test/index.html

index 1fa175313872905377f7f5fa2bbf2988c9e57ebd..331f04d2364a1188d1c2088f42b3ea1b1850216e 100644 (file)
@@ -9,7 +9,8 @@
                "uncheckedBehavior": "always",
                "bindings": "esm",
                "sourceMap": false,
-               "debug": true,
+               "debug": false,
+               "initialMemory": 8,
                "runtime": "stub",
                "exportRuntime": false,
                "enable": [
index 72e781ae093973bf760ffe14043fd7beea387d45..bee7ad498a6f878e4796dd0b7dd042f24bf38335 100644 (file)
@@ -12,29 +12,29 @@ const SIGNATURE_BYTES: i32 = 64
 
 // Static I/O buffers
 const INPUT_BUFFER_BYTES: i32 = SIGNATURE_BYTES + MESSAGE_BYTES
-const OUTPUT_BUFFER_BYTES: i32 = SIGNATURE_BYTES
 const MESSAGE_BUFFER_BYTES: i32 = 32768
-const INPUT_BUFFER = memory.data(INPUT_BUFFER_BYTES)
-const OUTPUT_BUFFER = memory.data(OUTPUT_BUFFER_BYTES)
-const MESSAGE_BUFFER = memory.data(MESSAGE_BUFFER_BYTES)
-
-/** Returns the pointer to the static output buffer (64 bytes). */
-export function getOutputPointer (): usize {
-       return OUTPUT_BUFFER
-}
+const OUTPUT_BUFFER_BYTES: i32 = SIGNATURE_BYTES
+const INPUT_BUFFER = new StaticArray<u8>(INPUT_BUFFER_BYTES)
+const MESSAGE_BUFFER = new StaticArray<u8>(MESSAGE_BUFFER_BYTES)
+const OUTPUT_BUFFER = new StaticArray<u8>(OUTPUT_BUFFER_BYTES)
 
 /** Returns the pointer to the static input buffer (128 bytes). */
 export function getInputPointer (): usize {
-       return INPUT_BUFFER
+       return changetype<usize>(INPUT_BUFFER)
 }
 
 /** Returns the pointer to the static message buffer (32 KiB). */
 export function getMessagePointer (): usize {
-       return MESSAGE_BUFFER
+       return changetype<usize>(MESSAGE_BUFFER)
+}
+
+/** Returns the pointer to the static output buffer (64 bytes). */
+export function getOutputPointer (): usize {
+       return changetype<usize>(OUTPUT_BUFFER)
 }
 
-const derive_pub = new StaticArray<u8>(PUBLICKEY_BYTES)
 const derive_prv = new StaticArray<u8>(PRIVATEKEY_BYTES)
+const derive_pub = new StaticArray<u8>(PUBLICKEY_BYTES)
 /**
  * Derive a 32-byte Nano public key from a 32-byte private key. Parameters are
  * read as bytes from the input buffer in the following order:
@@ -44,20 +44,30 @@ const derive_prv = new StaticArray<u8>(PRIVATEKEY_BYTES)
  * The public key is written to the output buffer.
  */
 export function derive (): void {
-       const pub = derive_pub
-       const prv = derive_prv
+       // Clear local buffers
+       derive_prv.fill(0)
+       derive_pub.fill(0)
+
+       // Copy input buffer to local parameterss, then clear input buffer
+       memory.copy(changetype<usize>(derive_prv), changetype<usize>(INPUT_BUFFER), PRIVATEKEY_BYTES)
+       INPUT_BUFFER.fill(0)
+
+       // Derive, then clear local input
+       crypto_derive(derive_pub, derive_prv)
+       derive_prv.fill(0)
 
-       memory.copy(changetype<usize>(prv), INPUT_BUFFER, PRIVATEKEY_BYTES)
-       memory.fill(INPUT_BUFFER, 0, INPUT_BUFFER_BYTES)
+       // Clear output buffer of prior data, then copy local result to output buffer
+       OUTPUT_BUFFER.fill(0)
+       memory.copy(changetype<usize>(OUTPUT_BUFFER), changetype<usize>(derive_pub), PUBLICKEY_BYTES)
 
-       crypto_derive(pub, prv)
-       memory.fill(OUTPUT_BUFFER, 0, OUTPUT_BUFFER_BYTES)
-       memory.copy(OUTPUT_BUFFER, changetype<usize>(pub), PUBLICKEY_BYTES)
+       // Clear local result
+       derive_pub.fill(0)
 }
 
-const sign_sig = new StaticArray<u8>(SIGNATURE_BYTES)
+const sign_msg = new StaticArray<u8>(MESSAGE_BUFFER_BYTES)
 const sign_prv = new StaticArray<u8>(PRIVATEKEY_BYTES)
 const sign_pub = new StaticArray<u8>(PUBLICKEY_BYTES)
+const sign_sig = new StaticArray<u8>(SIGNATURE_BYTES)
 /**
  * Sign up to 32 KiB of data using a 64-byte secret key. Parameters are read as
  * bytes from the input buffer in the following order:
@@ -71,23 +81,39 @@ const sign_pub = new StaticArray<u8>(PUBLICKEY_BYTES)
  */
 export function sign (mlen: i32): void {
        if (mlen < 0 || mlen > 32768) throw new Error()
-       const sig = sign_sig
-       const prv = sign_prv
-       const pub = sign_pub
-
-       memory.copy(changetype<usize>(prv), INPUT_BUFFER, PRIVATEKEY_BYTES)
-       memory.copy(changetype<usize>(pub), INPUT_BUFFER + PRIVATEKEY_BYTES, PUBLICKEY_BYTES)
-       memory.fill(INPUT_BUFFER, 0, INPUT_BUFFER_BYTES)
 
-       const msg = changetype<StaticArray<u8>>(MESSAGE_BUFFER)
-
-       crypto_sign(sig, msg, mlen, prv, pub)
-       memory.fill(OUTPUT_BUFFER, 0, OUTPUT_BUFFER_BYTES)
-       memory.copy(OUTPUT_BUFFER, changetype<usize>(sig), SIGNATURE_BYTES)
+       // Clear local buffers
+       sign_msg.fill(0)
+       sign_prv.fill(0)
+       sign_pub.fill(0)
+       sign_sig.fill(0)
+
+       // Copy input buffer to local parameters, then clear input buffer
+       memory.copy(changetype<usize>(sign_prv), changetype<usize>(INPUT_BUFFER), PRIVATEKEY_BYTES)
+       memory.copy(changetype<usize>(sign_pub), changetype<usize>(INPUT_BUFFER) + PRIVATEKEY_BYTES, PUBLICKEY_BYTES)
+       INPUT_BUFFER.fill(0)
+
+       // Copy message buffer to local variable, then clear message buffer
+       memory.copy(changetype<usize>(sign_msg), changetype<usize>(MESSAGE_BUFFER), mlen)
+       MESSAGE_BUFFER.fill(0)
+
+       // Sign, then clear local input
+       crypto_sign(sign_sig, sign_msg, mlen, sign_prv, sign_pub)
+       sign_msg.fill(0)
+       sign_prv.fill(0)
+       sign_pub.fill(0)
+
+       // Clear output buffer of prior data, then copy local result to output buffer
+       OUTPUT_BUFFER.fill(0)
+       memory.copy(changetype<usize>(OUTPUT_BUFFER), changetype<usize>(sign_sig), SIGNATURE_BYTES)
+
+       // Clear local result
+       sign_sig.fill(0)
 }
 
-const verify_sig = new StaticArray<u8>(SIGNATURE_BYTES)
+const verify_msg = new StaticArray<u8>(MESSAGE_BUFFER_BYTES)
 const verify_pub = new StaticArray<u8>(PUBLICKEY_BYTES)
+const verify_sig = new StaticArray<u8>(SIGNATURE_BYTES)
 /**
  * Verify a 64-byte detached signature for a variable-length message against a
  * 32-byte public key. Parameters are read as bytes from the input buffer in the
@@ -102,15 +128,27 @@ const verify_pub = new StaticArray<u8>(PUBLICKEY_BYTES)
  */
 export function verify (mlen: i32): i32 {
        if (mlen < 0 || mlen > 32768) throw new Error('invalid message length')
-       const sig = verify_sig
-       const pub = verify_pub
 
-       memory.copy(changetype<usize>(sig), INPUT_BUFFER, SIGNATURE_BYTES)
-       memory.copy(changetype<usize>(pub), INPUT_BUFFER + SIGNATURE_BYTES, PUBLICKEY_BYTES)
-       memory.fill(INPUT_BUFFER, 0, INPUT_BUFFER_BYTES)
+       // Clear local buffers
+       verify_msg.fill(0)
+       verify_pub.fill(0)
+       verify_sig.fill(0)
+
+       // Copy input buffer to local parameters, then clear input buffer
+       memory.copy(changetype<usize>(verify_sig), changetype<usize>(INPUT_BUFFER), SIGNATURE_BYTES)
+       memory.copy(changetype<usize>(verify_pub), changetype<usize>(INPUT_BUFFER) + SIGNATURE_BYTES, PUBLICKEY_BYTES)
+       INPUT_BUFFER.fill(0)
+
+       // Copy message buffer to local variable, then clear message buffer
+       memory.copy(changetype<usize>(verify_msg), changetype<usize>(MESSAGE_BUFFER), mlen)
+       MESSAGE_BUFFER.fill(0)
 
-       const msg = changetype<StaticArray<u8>>(MESSAGE_BUFFER)
+       // Verify, then clear local input
+       const verified = crypto_verify(verify_sig, verify_msg, mlen, verify_pub)
+       verify_msg.fill(0)
+       verify_pub.fill(0)
+       verify_sig.fill(0)
 
-       memory.fill(OUTPUT_BUFFER, 0, OUTPUT_BUFFER_BYTES)
-       return crypto_verify(sig, msg, mlen, pub)
+       // Output buffer not used, so just return
+       return verified
 }
index 000c72e03d04ab67d75f954f2278d7c7fda94cbf..13a31c5cf5f03eb72c5cb1999efaddb0d62ae987 100644 (file)
@@ -6,12 +6,12 @@ import nano25519_wasm from '../../build/nano25519.wasm'
 
 type Exports = {
        exports: {
-               derive: () => void,
-               sign: (mlen: number) => void,
-               verify: (mlen: number) => number,
-               getOutputPointer: () => number,
-               getInputPointer: () => number,
-               getMessagePointer: () => number,
+               derive: () => void
+               sign: (mlen: number) => void
+               verify: (mlen: number) => number
+               getInputPointer: () => number
+               getMessagePointer: () => number
+               getOutputPointer: () => number
                memory: WebAssembly.Memory
        }
 }
@@ -38,8 +38,7 @@ const { exports } = new WebAssembly.Instance(module, {
                        col >>>= 0
                        const message = `Nano25519WasmError: ${getString(msg)}, ${getString(file)}, row ${row}, col ${col}`
                        throw new Error(message)
-               },
-               memory: new WebAssembly.Memory({ initial: 1, maximum: 1 })
+               }
        }
 }) as Exports
 
@@ -149,11 +148,11 @@ export function verify (s: unknown, m: unknown, k: unknown): boolean {
        }
 }
 
-function clear (buffer: Uint8Array): void {
+function clear (memory: Uint8Array): void {
        let inPtr = exports.getInputPointer()
        let outPtr = exports.getOutputPointer()
-       buffer.fill(0, inPtr, inPtr + 96)
-       buffer.fill(0, outPtr, outPtr + 64)
+       memory.fill(0, inPtr, inPtr + 96)
+       memory.fill(0, outPtr, outPtr + 64)
 }
 
 function isBytes (a: unknown): a is Uint8Array<ArrayBuffer> {
index 8c6c447e8b941fb23beaf541d06f41a398aaf9be..1dbf0a7fc21f0e2cac23ee19a6e61be91b4ec771 100644 (file)
@@ -31,11 +31,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                        }
                }
        </script>
-       <script
-               src="https://unpkg.com/nanocurrency-web@2.0.0/dist/index.min.js"
-               integrity="sha512-FY0tmOBy8gMOUmqgh2Vg5RgjzCbDGXQK3x0PZ9K6MdP0VDxYhLIcSK7mpUOjfLSNQLrlTwgnhKNCRPmi3cOMfQ=="
-               crossorigin="anonymous"
-       ></script>
+       <script src="https://unpkg.com/nanocurrency-web@2.0.0/dist/index.min.js" integrity="sha512-FY0tmOBy8gMOUmqgh2Vg5RgjzCbDGXQK3x0PZ9K6MdP0VDxYhLIcSK7mpUOjfLSNQLrlTwgnhKNCRPmi3cOMfQ==" crossorigin="anonymous"></script>
        <script type="module">
                import libsodium from 'libsodium'
                import sodium from 'libsodium-wrappers'
@@ -517,10 +513,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
                        <option>TweetNaCl.js</option>
                </select>
        </span>
-       <label for="size">Test Size</label>
-       <input id="size" type="number" value="100" min="1" autofocus />
        <label for="runs">Test Runs</label>
        <input id="runs" type="number" value="100" min="1" autofocus />
+       <label for="size">Test Size</label>
+       <input id="size" type="number" value="100" min="1" autofocus />
        <button id="btnStartTest" disabled>Go</button>
        <hr />
        <h3 id="status">LOADING</h3>