]> git.codecow.com Git - nano25519.git/commitdiff
Switch from direct memory access to get/set exports.
authorChris Duncan <chris@codecow.com>
Wed, 23 Sep 2026 18:17:21 +0000 (11:17 -0700)
committerChris Duncan <chris@codecow.com>
Wed, 23 Sep 2026 18:17:21 +0000 (11:17 -0700)
asconfig.json
src/assembly/crypto_sign.ts
src/assembly/crypto_verify.ts
src/assembly/index.ts
src/lib/derive.ts
src/lib/primordials.ts
src/lib/sign.ts
src/lib/verify.ts
src/lib/wasm.ts
test/node.mjs

index 9e2ea4e2a53c9d23336f663fd80650e264ddb27f..fa1b91708dd8d5b097cf44d48f921a605258ee69 100644 (file)
                "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
        }
index c3429da14d21e8d35fc9de51b1e00b760715879a..d0b3be5df5bca973534e9b4e6c1073fc94cf19e3 100644 (file)
@@ -47,7 +47,7 @@ export function crypto_sign (RS: StaticArray<u8>, M: StaticArray<u8>, 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)
index fa06b448ffde103110536ae0a57f3d8b9bf33e37..0909f77551d11a2dca147f6af26fecbfc4fe0113 100644 (file)
@@ -72,14 +72,7 @@ export function crypto_verify_donna (sig: StaticArray<u8>, M: StaticArray<u8>, A
  */
 export function crypto_verify_sodium (sig: StaticArray<u8>, M: StaticArray<u8>, mlen: i32, pub: StaticArray<u8>): 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<usize>(S), changetype<usize>(sig) + 32, 32)
@@ -99,7 +92,57 @@ export function crypto_verify_sodium (sig: StaticArray<u8>, M: StaticArray<u8>,
        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<u8>, M: StaticArray<u8>, mlen: i32, pub: StaticArray<u8>): i32 {
+
+       if (!verify_pubkey(pub)) return 0
+
+       // fail if scalar `S` is non-canonical (`L ≤ S`)
+       memory.copy(changetype<usize>(S), changetype<usize>(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<u8>): 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
 }
index dfc27d14f2395e0da3b4391260c16fc3bc8e0757..5eaf811630555e64e46f1d112b3d1234c62578a0 100644 (file)
@@ -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<u8>(KEY_BYTELENGTH)
 const OUTPUT_SIGN = new StaticArray<u8>(SIGNATURE_BYTELENGTH)
 const OUTPUT_VERIFY = new StaticArray<u8>(MAX_VERIFY_BLOCKS)
 
-/** Returns a pointer to the static message input buffer. */
-export function ptrInputMsg (): usize {
-       return changetype<usize>(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<usize>(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<usize>(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<usize>(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<usize>(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<usize>(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<usize>(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<u8>(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
 }
index b47a79a0b3a3f8a0330fa1ed0621a93584ea06f8..74d03979b110a3cf09aee3d1ac86b28ce8933007 100644 (file)
@@ -1,8 +1,8 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! 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)
index 64e4e3aeee087472a8004a78bdc0681fbea0d97a..cab2ca8cc6c3f609b8ad9cd9a3b26bff292cd9e6 100644 (file)
@@ -43,6 +43,7 @@ export type Bytes = Uint8Array<ArrayBuffer>
 
 /** Declared here to avoid import loops. */
 export class Nano25519TypeError extends TypeError { }
+export class Nano25519WasmError extends Error { }
 
 const isArray = Array.isArray
 
index cd12b2a2c12447541e365cffbadb886cf8cd594a..b9e5a04f348e134b19a911b6340e2d0f737d6a6b 100644 (file)
@@ -1,8 +1,8 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! 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)
index 8356a00582c47d079ba4aa039f27c48d0dc1b49a..bee6f208b0d60bedbb68a7b906fe05adca93d36e 100644 (file)
@@ -1,30 +1,28 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! 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 {
index 7eb7a2a26a8ad27adeed86a68ef4689d64a33a82..16e67d2bce1f496bc2815e6634feb9f6e58e2de8 100644 (file)
@@ -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
index 802eb119dbe25a9d728661998141687a60b1ee4d..cc82c925bc7789ea58a7f820da94ef75be66e96e 100644 (file)
@@ -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
        }