From b90cc4b697f87f1f2f409f671ffa051f4aff1eee Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Fri, 4 Sep 2026 13:52:03 -0700 Subject: [PATCH] Add canary for equalbytes. Add fabricated valid signature for identity public key. --- test/node.mjs | 20 +++++++++++++++++++- test/vectors.mjs | 4 +++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/test/node.mjs b/test/node.mjs index f26e4b5..4dcf2e5 100644 --- a/test/node.mjs +++ b/test/node.mjs @@ -2,7 +2,7 @@ //! SPDX-License-Identifier: GPL-3.0-or-later import { constants, derive, sign, verify, verify_blocks } from 'nano25519' -import { NANO_ORG_VECTOR, PROBLEM_VECTOR, PYTHON_ED25519_BLAKE2B_VECTORS } from './vectors.mjs' +import { IDENTITY, NANO_ORG_VECTOR, PROBLEM_VECTOR, PYTHON_ED25519_BLAKE2B_VECTORS } from './vectors.mjs' /** * @param {string} name @@ -189,6 +189,24 @@ check(`verify_blocks relaxed check passes problematic signature string ${PROBLEM passes += +test failures += +!test +// identity A and S=0 pin check_r so only R moves; any other A hides equalbytes bugs +const forgery = IDENTITY.signatureBytes.slice() +const accepted = [] +for (let i = 0; i < constants.KEY_BYTELENGTH; i++) { + for (let j = 1; j < 256; j++) { + forgery[i] ^= j + result = verify_blocks(IDENTITY.publicKeyBytes, [{ signature: forgery, hash: new Uint8Array(32) }])[0] + forgery[i] = IDENTITY.signatureBytes[i] + if (result !== false) { + accepted.push({ byte: i, mask: `0x${j.toString(16).padStart(2, '0')}` }) + } + } +} +test = accepted.length === 0 +check(`verify_blocks rejects R xor mask${test ? '' : ` (failed to reject ${accepted})`}`, test) +passes += +test +failures += +!test + // Check output buffer offset handling try { outbuf = new Uint8Array(33) diff --git a/test/vectors.mjs b/test/vectors.mjs index 0d119dc..1922f8c 100644 --- a/test/vectors.mjs +++ b/test/vectors.mjs @@ -6,6 +6,8 @@ export { PYTHON_ED25519_BLAKE2B_VECTORS } from './python_ed25519_blake2b_vectors export const IDENTITY = { publicKey: '0100000000000000000000000000000000000000000000000000000000000000', publicKeyBytes: new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), + signature: '01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + signatureBytes: new Uint8Array([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]), } /** @@ -27,7 +29,7 @@ export const NANO_ORG_VECTOR = { /** * https://github.com/rsnano-node/rsnano-node/blob/develop/types/src/private_key.rs * - * Small-order public key producing malleable signatures + * Identity point as public key producing malleable signatures * * Address: nano_11a11111111111111111111111111111111111111111111111116iq5p4i8 */ -- 2.52.0