]> git.codecow.com Git - nano25519.git/commitdiff
Export constants for consumer use. Increase minimum message buffer allocation to...
authorChris Duncan <chris@codecow.com>
Fri, 28 Aug 2026 17:54:58 +0000 (10:54 -0700)
committerChris Duncan <chris@codecow.com>
Fri, 28 Aug 2026 17:54:58 +0000 (10:54 -0700)
12 files changed:
asconfig.json
src/assembly/crypto_verify.ts
src/assembly/index.ts
src/assembly/utils.ts
src/index.ts
src/lib/derive.ts
src/lib/index.ts
src/lib/sign.ts
src/lib/verify.ts
src/lib/wasm.ts
test/index.html
test/node.mjs

index efb473e8c37e24c8d1034bcde6f6310ca156c5fb..f75849a29dda576eedf7e143e2eb1df9f027a20a 100644 (file)
@@ -10,7 +10,7 @@
                "bindings": "esm",
                "sourceMap": false,
                "debug": false,
-               "initialMemory": 2,
+               "initialMemory": 4,
                "runtime": "stub",
                "exportRuntime": false,
                "enable": [
index b903b140e9737c077856360cc66fc57e2d3710dc..8b3e9b281c82c39c0641f52fc1a28350efe89d63 100644 (file)
@@ -41,7 +41,7 @@ export function crypto_verify_decodepubkey (pub: StaticArray<u8>): i32 {
  *
  * - S < 2²⁵³, instead of S < L
  * - Skip canonical and small-order checks on public key
- * - Skip checks on r and sb_ah
+ * - Byte-for-byte comparison of `r` to `s`
  *
  * IMPORTANT: Callers MUST call `crypto_verify_decodepubkey` first in order to
  * set `A` for the scalar multiplication step before checking `sB = R + hA`.
@@ -64,7 +64,7 @@ export function crypto_verify_relaxed (s: StaticArray<u8>, M: StaticArray<u8>, p
        ge_double_scalarmult_vartime_to_p3(sb_ah, h, A, S)
        ge_p3_tobytes(check_r, sb_ah)
 
-       return equalbytes(s, check_r, KEY_BYTELENGTH) - 1
+       return equalbytes(s, check_r, 32) - 1
 }
 
 /**
@@ -105,5 +105,5 @@ export function crypto_verify_strict (s: StaticArray<u8>, M: StaticArray<u8>, ml
        return ge_has_small_order(check) - 1
 
        // ge_p3_tobytes(check_r, sb_ah)
-       // return equalbytes(s, check_r, KEY_BYTELENGTH) - 1
+       // return equalbytes(s, check_r, 32) - 1
 }
index c67eabae36f14b3a0d276f518df0df48dcacbfb8..1a13bc0b8bfaded8485298e04ad5edd8082b785c 100644 (file)
@@ -7,10 +7,13 @@ import { crypto_verify_decodepubkey, crypto_verify_relaxed, crypto_verify_strict
 
 export const BLOCKHASH_BYTELENGTH: i32 = 32
 export const KEY_BYTELENGTH: i32 = 32
-export const MAX_VERIFY_BLOCKS: i32 = 32
-export const MAX_MESSAGE_BYTELENGTH: i32 = 1 << 15
 export const SIGNATURE_BYTELENGTH: i32 = 64
-export const SIGNEDBLOCK_BYTELENGTH: i32 = BLOCKHASH_BYTELENGTH + SIGNATURE_BYTELENGTH
+
+const PAGE_BYTELENGTH: i32 = 1 << 16
+const SIGNEDBLOCK_BYTELENGTH: i32 = BLOCKHASH_BYTELENGTH + SIGNATURE_BYTELENGTH
+export const MAX_VERIFY_BLOCKS: i32 = 32
+export const MAX_VERIFY_BLOCKS_BYTELENGTH: i32 = MAX_VERIFY_BLOCKS * SIGNEDBLOCK_BYTELENGTH
+export const MAX_MESSAGE_BYTELENGTH: i32 = MAX_VERIFY_BLOCKS_BYTELENGTH > PAGE_BYTELENGTH ? MAX_VERIFY_BLOCKS_BYTELENGTH : PAGE_BYTELENGTH
 
 // Static I/O buffers
 const INPUT_MSG = new StaticArray<u8>(MAX_MESSAGE_BYTELENGTH)
@@ -22,37 +25,37 @@ 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 the pointer to the static message input buffer (32 KiB). */
+/** Returns a pointer to the static message input buffer. */
 export function ptrInputMsg (): usize {
        return changetype<usize>(INPUT_MSG)
 }
 
-/** Returns the pointer to the static private key input buffer (32 bytes). */
+/** Returns a pointer to the static private key input buffer (32 bytes). */
 export function ptrInputPrv (): usize {
        return changetype<usize>(INPUT_PRV)
 }
 
-/** Returns the pointer to the static public key input buffer (32 bytes). */
+/** Returns a pointer to the static public key input buffer (32 bytes). */
 export function ptrInputPub (): usize {
        return changetype<usize>(INPUT_PUB)
 }
 
-/** Returns the pointer to the static signature input buffer (64 bytes). */
+/** Returns a pointer to the static signature input buffer (64 bytes). */
 export function ptrInputSig (): usize {
        return changetype<usize>(INPUT_SIG)
 }
 
-/** Returns the pointer to the derive() static output buffer (64 bytes). */
+/** Returns a pointer to the derive() static output buffer (64 bytes). */
 export function ptrOutputDerive (): usize {
        return changetype<usize>(OUTPUT_DERIVE)
 }
 
-/** Returns the pointer to the sign() static output buffer (64 bytes). */
+/** Returns a pointer to the sign() static output buffer (64 bytes). */
 export function ptrOutputSign (): usize {
        return changetype<usize>(OUTPUT_SIGN)
 }
 
-/** Returns the pointer to the verify() static output buffer (64 bytes). */
+/** Returns a pointer to the verify() static output buffer (64 bytes). */
 export function ptrOutputVerify (): usize {
        return changetype<usize>(OUTPUT_VERIFY)
 }
index 34536777d0d36d6580c99d202a764a500c7575a2..4f9a00742f9d2b6481bcecb87dba9ac56540ddfc 100644 (file)
@@ -75,3 +75,14 @@ export function sodium_is_zero (n: StaticArray<u8>, nlen: i32): u8 {
        d &= 255
        return u8(((d - 1) >> 8) & 1)
 }
+
+/** Unit tests on initialization */
+const testbytesA = new StaticArray<u8>(32).fill(0)
+const testbytesB = new StaticArray<u8>(32).fill(0)
+for (let i = 0; i < 32; i++) {
+       testbytesA[i] = 1
+       if (equalbytes(testbytesA, testbytesB, 32)) {
+               throw new Error('equalbytes failed unit test')
+       }
+       testbytesA[i] = 0
+}
index 4c539186405c1bab05208c3c5ab1bdafc3af9419..c4ef6a9051cc716c795390e369939ee71b07a4f7 100644 (file)
@@ -3,6 +3,8 @@
 
 import * as nano25519 from './lib'
 
+export { constants } from './lib'
+
 /**
  * Nano public key derivation using WebAssembly.
  * @param {Uint8Array<ArrayBuffer>} prv - 32-byte private key
index 6eed51e7f73823f990a09eb457ee57222c18fa64..5a831f885d7fdc087c5e52ef06e5b01aca175fb6 100644 (file)
@@ -1,7 +1,10 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { clear, exports, INPUT_PRV, isBytes, KEY_BYTELENGTH, normalize, OUTPUT_DERIVE } from './wasm'
+import { clear, constants, exports, isBytes, normalize, pointers, } from './wasm'
+
+const { KEY_BYTELENGTH, } = constants
+const { INPUT_PRV, OUTPUT_DERIVE, } = pointers
 
 export function derive (prv: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> | void {
        if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === KEY_BYTELENGTH)) {
index 7b00dca38ab44c7914fb9b61cec8612d0571b681..9e34dabcddb6787d8e5c09ddec8a393fbcde6b27 100644 (file)
@@ -4,4 +4,5 @@
 export { derive } from './derive'
 export { sign } from './sign'
 export { verify, verify_blocks } from './verify'
+export { constants } from './wasm'
 
index 402dea9c46f16ca10651ed4a98750b7801878a17..9b543e7bfea8929b5e95cd0f029516743a3f2a4f 100644 (file)
@@ -1,7 +1,10 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { clear, exports, INPUT_MSG, INPUT_PRV, INPUT_PUB, isBytes, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, normalize, OUTPUT_SIGN, SIGNATURE_BYTELENGTH } from './wasm'
+import { clear, constants, exports, isBytes, normalize, pointers, } from './wasm'
+
+const { KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, SIGNATURE_BYTELENGTH, } = constants
+const { INPUT_MSG, INPUT_PRV, INPUT_PUB, OUTPUT_SIGN, } = pointers
 
 export function sign (msg: unknown, prv: unknown, pub: unknown, out?: unknown): string | Uint8Array<ArrayBuffer> | void {
        if (typeof out !== 'undefined' && !(isBytes(out) && out.byteLength === 64)) {
index f2e433c2a8025c36b6e982754931563093b01ef9..415bb341b8d75d44045b387d7d019ae9d7422cd6 100644 (file)
@@ -1,7 +1,10 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { BLOCKHASH_BYTELENGTH, INPUT_MSG, INPUT_PUB, INPUT_SIG, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIFY_BLOCKS, OUTPUT_VERIFY, SIGNATURE_BYTELENGTH, clear, exports, normalize } from './wasm'
+import { clear, constants, exports, normalize, pointers, } from './wasm'
+
+const { BLOCKHASH_BYTELENGTH, KEY_BYTELENGTH, MAX_MESSAGE_BYTELENGTH, MAX_VERIFY_BLOCKS, SIGNATURE_BYTELENGTH, } = constants
+const { INPUT_MSG, INPUT_PUB, INPUT_SIG, OUTPUT_VERIFY, } = pointers
 
 export function verify (sig: unknown, msg: unknown, pub: unknown): boolean {
        let buffer = new Uint8Array(exports.memory.buffer)
index 1bd81d3b068ce5b327911b4b702f8e1190739a1a..7e51979d80ee0f7e44ddba53193ea883208ee1ae 100644 (file)
@@ -19,10 +19,10 @@ type Exports = {
                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
-               SIGNATURE_BYTELENGTH: WebAssembly.Global
-               SIGNEDBLOCK_BYTELENGTH: WebAssembly.Global
                memory: WebAssembly.Memory
        }
 }
@@ -54,20 +54,40 @@ export const { exports } = new WebAssembly.Instance(module, {
        }
 }) as Exports
 
-export const BLOCKHASH_BYTELENGTH = exports.BLOCKHASH_BYTELENGTH.value
-export const KEY_BYTELENGTH = exports.KEY_BYTELENGTH.value
-export const MAX_MESSAGE_BYTELENGTH = exports.MAX_MESSAGE_BYTELENGTH.value
-export const MAX_VERIFY_BLOCKS = exports.MAX_VERIFY_BLOCKS.value
-export const SIGNATURE_BYTELENGTH = exports.SIGNATURE_BYTELENGTH.value
-export const SIGNEDBLOCK_BYTELENGTH = exports.SIGNEDBLOCK_BYTELENGTH.value
+const BLOCKHASH_BYTELENGTH = exports.BLOCKHASH_BYTELENGTH.value
+const KEY_BYTELENGTH = exports.KEY_BYTELENGTH.value
+const SIGNATURE_BYTELENGTH = exports.SIGNATURE_BYTELENGTH.value
 
-export const INPUT_MSG = exports.ptrInputMsg()
-export const INPUT_PRV = exports.ptrInputPrv()
-export const INPUT_PUB = exports.ptrInputPub()
-export const INPUT_SIG = exports.ptrInputSig()
-export const OUTPUT_DERIVE = exports.ptrOutputDerive()
-export const OUTPUT_SIGN = exports.ptrOutputSign()
-export const OUTPUT_VERIFY = exports.ptrOutputVerify()
+const MAX_VERIFY_BLOCKS = exports.MAX_VERIFY_BLOCKS.value
+const MAX_VERIFY_BLOCKS_BYTELENGTH = exports.MAX_VERIFY_BLOCKS_BYTELENGTH.value
+const MAX_MESSAGE_BYTELENGTH = exports.MAX_MESSAGE_BYTELENGTH.value
+
+export const constants = {
+       BLOCKHASH_BYTELENGTH,
+       KEY_BYTELENGTH,
+       SIGNATURE_BYTELENGTH,
+       MAX_VERIFY_BLOCKS,
+       MAX_VERIFY_BLOCKS_BYTELENGTH,
+       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 const pointers = {
+       INPUT_MSG,
+       INPUT_PRV,
+       INPUT_PUB,
+       INPUT_SIG,
+       OUTPUT_DERIVE,
+       OUTPUT_SIGN,
+       OUTPUT_VERIFY,
+}
 
 export function clear (memory: Uint8Array): void {
        memory.fill(0, INPUT_MSG, INPUT_MSG + MAX_MESSAGE_BYTELENGTH)
index 99b4512045a709dcd89f9f437573df6e5931a83a..bb30ca91aa943b4dcd9e06657a3ffab9336718d2 100644 (file)
@@ -380,7 +380,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                                                if (api === 'nano25519 with bulk block verification') {
                                                        publicKey = derive(privateKey, 'nano25519')
                                                        const blocks = []
-                                                       for (let b = 0; blockhash < 32; b++) {
+                                                       for (let b = 0; b < nano25519.constants.MAX_VERIFY_BLOCKS; b++) {
                                                                blockHash = random()
                                                                signature = sign(blockHash, privateKey, publicKey, 'nano25519')
                                                                blocks[b] = { hash: blockHash, signature }
@@ -391,7 +391,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
                                                        if (verified.some(v => v !== true)) {
                                                                throw new Error(`invalid result\nsignature: ${signature}\nblock hash: ${blockHash}\npublic key: ${publicKey}`)
                                                        }
-                                                       duration = (end - start) / 32
+                                                       duration = (end - start) / nano25519.constants.MAX_VERIFY_BLOCKS
                                                        deriveTimes[i].push(0)
                                                        signTimes[i].push(0)
                                                        verifyTimes[i].push(duration)
index 38b1f8e28491631c562a1ebcb5330ac8e982f8a1..bad4e112e3c7ebca58ad5f67714612e541006300 100644 (file)
@@ -1,7 +1,7 @@
 //! SPDX-FileCopyrightText: 2026 Chris Duncan <chris@codecow.com>
 //! SPDX-License-Identifier: GPL-3.0-or-later
 
-import { derive, sign, verify, verify_blocks } from 'nano25519'
+import { constants, derive, sign, verify, verify_blocks } from 'nano25519'
 import { NANO_ORG_VECTOR, PROBLEM_VECTOR, PYTHON_ED25519_BLAKE2B_VECTORS } from './vectors.mjs'
 
 /**
@@ -65,17 +65,18 @@ passes += +test
 failures += +!test
 
 try {
-       result = sign('1'.repeat(65538), NANO_ORG_VECTOR.privateKey, NANO_ORG_VECTOR.publicKey)
+       result = sign('1'.repeat((constants.MAX_MESSAGE_BYTELENGTH << 1) + 1), NANO_ORG_VECTOR.privateKey, NANO_ORG_VECTOR.publicKey)
        result = false
 } catch (err) {
+       console.log(err)
        if (err != null && typeof err === 'object' && 'message' in err) {
-               result = err.message === `Invalid hexadecimal length 65538 for message`
+               result = err.message === `Invalid hexadecimal length ${(constants.MAX_MESSAGE_BYTELENGTH << 1) + 1} for message`
        } else {
                result = false
        }
 }
 test = result === true
-check(`sign message too long (65538 '1's)`, test)
+check(`sign message too long (${(constants.MAX_MESSAGE_BYTELENGTH << 1) + 1} '1's)`, test)
 passes += +test
 failures += +!test
 
@@ -145,32 +146,18 @@ passes += +test
 failures += +!test
 
 try {
-       result = sign(NANO_ORG_VECTOR.blockHash + '0', NANO_ORG_VECTOR.privateKey, NANO_ORG_VECTOR.publicKey)
-       result = false
-} catch (err) {
-       if (err != null && typeof err === 'object' && 'message' in err) {
-               result = err.message === 'Invalid hexadecimal length 65 for message'
-       } else {
-               result = false
-       }
-}
-test = result === true
-check(`sign message odd character count ${NANO_ORG_VECTOR.blockHash + '0'}`, test)
-passes += +test
-failures += +!test
-
-try {
-       result = sign('1'.repeat(65538), NANO_ORG_VECTOR.privateKey, NANO_ORG_VECTOR.publicKey)
+       result = sign(new Uint8Array(constants.MAX_MESSAGE_BYTELENGTH + 1).fill(1), NANO_ORG_VECTOR.privateKeyBytes, NANO_ORG_VECTOR.publicKeyBytes)
        result = false
 } catch (err) {
+       console.log(err)
        if (err != null && typeof err === 'object' && 'message' in err) {
-               result = err.message === 'Invalid hexadecimal length 65538 for message'
+               result = err.message === `message must be no more than ${constants.MAX_MESSAGE_BYTELENGTH} bytes`
        } else {
                result = false
        }
 }
 test = result === true
-check(`sign message too long (65538 '1's)`, test)
+check(`sign message too long (${constants.MAX_MESSAGE_BYTELENGTH + 1} bytes)`, test)
 passes += +test
 failures += +!test
 
@@ -391,7 +378,7 @@ failures += +!test
 
 try {
        const blocks = []
-       for (let i = 0; i < 32; i++) {
+       for (let i = 0; i < constants.MAX_VERIFY_BLOCKS; i++) {
                blocks[i] = { hash: NANO_ORG_VECTOR.blockHashBytes, signature: NANO_ORG_VECTOR.signatureBytes }
        }
        const verified = verify_blocks(NANO_ORG_VECTOR.publicKeyBytes, blocks)
@@ -401,7 +388,7 @@ try {
        result = false
 }
 test = result === true
-check(`verify_blocks 32 max`, test)
+check(`verify_blocks ${constants.MAX_VERIFY_BLOCKS} max`, test)
 passes += +test
 failures += +!test
 
@@ -426,14 +413,14 @@ failures += +!test
 
 try {
        const blocks = []
-       for (let i = 0; i < 33; i++) {
+       for (let i = 0; i < constants.MAX_VERIFY_BLOCKS + 1; i++) {
                blocks[i] = { hash: NANO_ORG_VECTOR.blockHashBytes, signature: NANO_ORG_VECTOR.signatureBytes }
        }
        verify_blocks(NANO_ORG_VECTOR.publicKeyBytes, blocks)
        result = false
 } catch (err) {
        if (err != null && typeof err === 'object' && 'message' in err) {
-               result = err.message === 'Bulk Nano block verification must be no more than 32 blocks'
+               result = err.message === `Bulk Nano block verification must be no more than ${constants.MAX_VERIFY_BLOCKS} blocks`
        } else {
                result = false
        }