From c140693a58974355265e3ea6f7fba4d69d6949cb Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sun, 27 Sep 2026 01:48:37 -0700 Subject: [PATCH] Restrict input to bytes only, no array buffers. Fix typings and test. --- package.json | 2 +- src/lib/primordials.ts | 26 ++++++++++++------------- src/lib/wasm.ts | 44 +++++++++++++++++++++--------------------- test/node.mjs | 10 +++++++--- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 5e9d23e..9ec3d1c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "compile": "node scripts/blake2b-gen.mjs && asc ./src/assembly/index.ts && tsc", "prepublishOnly": "npm run test:prod", "test": "npm run build && npm run test:all", - "test:all": "npm run test:be && npm run test:le", + "test:all": "npm run test:le && npm run test:be", "test:be": "node --import ./test/big-endian.mjs ./test/node.mjs", "test:le": "node ./test/node.mjs", "test:prod": "npm run build:prod && npm run test:all" diff --git a/src/lib/primordials.ts b/src/lib/primordials.ts index 101ff48..bbfc930 100644 --- a/src/lib/primordials.ts +++ b/src/lib/primordials.ts @@ -207,19 +207,17 @@ export function normalize (name: string, byteLengthMin: number, byteLengthMax: n return hexToBytes(value) } - if (isArrayBuffer(value)) { - value = new Bytes(value) - } - if (!isBytes(value)) { - throw new TypeError(`${name} must be Uint8Array or ArrayBuffer`) - } - const bytes = copy(value) - const byteLength = getByteLength(bytes) - if (byteLength < byteLengthMin) { - throw new TypeError(`${name} must be at least ${byteLengthMin} bytes`) - } - if (byteLength > byteLengthMax) { - throw new TypeError(`${name} must be no more than ${byteLengthMax} bytes`) + if (isBytes(value)) { + const bytes = copy(value) + const byteLength = getByteLength(bytes) + if (byteLength < byteLengthMin) { + throw new TypeError(`${name} must be at least ${byteLengthMin} bytes`) + } + if (byteLength > byteLengthMax) { + throw new TypeError(`${name} must be no more than ${byteLengthMax} bytes`) + } + return bytes } - return bytes + + throw new TypeError(`${name} must be a hex string or Uint8Array`) } diff --git a/src/lib/wasm.ts b/src/lib/wasm.ts index cf14aaf..d986d30 100644 --- a/src/lib/wasm.ts +++ b/src/lib/wasm.ts @@ -19,28 +19,28 @@ type Exports = WebAssembly.Instance['exports'] & { 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 - BUFFER_INPUT_MSG: WebAssembly.Global - BUFFER_INPUT_PRV: WebAssembly.Global - BUFFER_INPUT_PUB: WebAssembly.Global - BUFFER_INPUT_SIG: WebAssembly.Global - BUFFER_OUTPUT_DERIVE: WebAssembly.Global - BUFFER_OUTPUT_SIGN: WebAssembly.Global - BUFFER_OUTPUT_VERIFY: WebAssembly.Global - ERROR_BYTE_OUT_OF_RANGE: WebAssembly.Global - ERROR_INDEX_OUT_OF_RANGE: WebAssembly.Global - ERROR_INVALID_BLOCK_COUNT: WebAssembly.Global - ERROR_INVALID_MESSAGE_LENGTH: WebAssembly.Global - ERROR_INVALID_PUBLIC_KEY: WebAssembly.Global - ERROR_SELFTEST_XFAIL_ONE: WebAssembly.Global - ERROR_SELFTEST_XFAIL_PAIR: WebAssembly.Global - ERROR_SELFTEST_XPASS: WebAssembly.Global - ERROR_UNKNOWN: WebAssembly.Global + BLOCKHASH_BYTELENGTH: WebAssembly.Global<'i32'> + KEY_BYTELENGTH: WebAssembly.Global<'i32'> + SIGNATURE_BYTELENGTH: WebAssembly.Global<'i32'> + MAX_VERIFY_BLOCKS: WebAssembly.Global<'i32'> + MAX_VERIFY_BLOCKS_BYTELENGTH: WebAssembly.Global<'i32'> + MAX_MESSAGE_BYTELENGTH: WebAssembly.Global<'i32'> + BUFFER_INPUT_MSG: WebAssembly.Global<'i32'> + BUFFER_INPUT_PRV: WebAssembly.Global<'i32'> + BUFFER_INPUT_PUB: WebAssembly.Global<'i32'> + BUFFER_INPUT_SIG: WebAssembly.Global<'i32'> + BUFFER_OUTPUT_DERIVE: WebAssembly.Global<'i32'> + BUFFER_OUTPUT_SIGN: WebAssembly.Global<'i32'> + BUFFER_OUTPUT_VERIFY: WebAssembly.Global<'i32'> + ERROR_BYTE_OUT_OF_RANGE: WebAssembly.Global<'i32'> + ERROR_INDEX_OUT_OF_RANGE: WebAssembly.Global<'i32'> + ERROR_INVALID_BLOCK_COUNT: WebAssembly.Global<'i32'> + ERROR_INVALID_MESSAGE_LENGTH: WebAssembly.Global<'i32'> + ERROR_INVALID_PUBLIC_KEY: WebAssembly.Global<'i32'> + ERROR_SELFTEST_XFAIL_ONE: WebAssembly.Global<'i32'> + ERROR_SELFTEST_XFAIL_PAIR: WebAssembly.Global<'i32'> + ERROR_SELFTEST_XPASS: WebAssembly.Global<'i32'> + ERROR_UNKNOWN: WebAssembly.Global<'i32'> } let locked = false diff --git a/test/node.mjs b/test/node.mjs index b37e459..1a9e965 100644 --- a/test/node.mjs +++ b/test/node.mjs @@ -116,7 +116,7 @@ try { result = false } catch (err) { if (err != null && typeof err === 'object' && 'message' in err) { - result = err.message === `private key must be Uint8Array or ArrayBuffer` + result = err.message === `private key must be a hex string or Uint8Array` } else { result = false } @@ -134,7 +134,7 @@ try { result = false } catch (err) { if (err != null && typeof err === 'object' && 'message' in err) { - result = err.message === `private key must be Uint8Array or ArrayBuffer` + result = err.message === `private key must be a hex string or Uint8Array` } else { result = false } @@ -410,6 +410,7 @@ for (const { privateKey, publicKey, message, signature } of PYTHON_ED25519_BLAKE */ function sweepMessage (length, flips) { const message = new Uint8Array(length).map((_, i) => (i * 37 + 11) & 255) + /** @type {{ byte: number, mask: string, result: unknown }[]} */ const missed = [] let signature try { @@ -425,7 +426,7 @@ function sweepMessage (length, flips) { try { result = verify(signature, message, NANO_ORG_VECTOR.publicKeyBytes) } catch (err) { - result = `threw ${err?.message}` + result = `threw ${typeof err === 'object' && err != null && 'message' in err ? err?.message : err}` } message[i] ^= mask if (result !== false) { @@ -435,8 +436,10 @@ function sweepMessage (length, flips) { return { baseline: true, missed } } +/** @type {[number, (length: number) => [number, number][]][]} */ const sweeps = [ [constants.MAX_MESSAGE_BYTELENGTH, (length) => { + /** @type {[number, number][]} */ const flips = [] for (let chunk = 0; chunk < length >> 5; chunk++) { flips.push([(chunk << 5) + (chunk & 31), 1 << ((chunk >> 5) & 7)]) @@ -444,6 +447,7 @@ const sweeps = [ return flips }], [constants.MAX_MESSAGE_BYTELENGTH - 1, (length) => { + /** @type {[number, number][]} */ const flips = [] for (let i = (length & ~31) - 32; i < length; i++) { flips.push([i, 1 << (i & 7)]) -- 2.52.0