// gen_vectors.mjs // // Generate `xmss-verify-core/src/vectors.rs` from the OFFICIAL RFC 8391 // XMSS-SHA2_10_256 known-answer vector committed at // aerenew/contracts/test/fixtures/xmss-sha2_10_256-kat.json // (github.com/XMSS/xmss-reference deterministic test/vectors.c, oid=1, idx=512, msg=0x25). // // This exists so the Rust test vector is provably DERIVED from the committed // official fixture, with no hand transcription of the 67 WOTS+ chains and 10 // authentication-path nodes. Run: // node scripts/gen_vectors.mjs // // Deterministic: same input JSON -> byte-identical vectors.rs. import { readFileSync, writeFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { dirname, resolve } from "node:path"; const here = dirname(fileURLToPath(import.meta.url)); const katPath = resolve( here, "../../../contracts/test/fixtures/xmss-sha2_10_256-kat.json" ); const outPath = resolve(here, "../xmss-verify-core/src/vectors.rs"); const kat = JSON.parse(readFileSync(katPath, "utf8")); const strip = (h) => h.replace(/^0x/, ""); const bytes = (h) => { const s = strip(h); if (s.length % 2 !== 0) throw new Error(`odd hex: ${h}`); const out = []; for (let i = 0; i < s.length; i += 2) out.push(parseInt(s.slice(i, i + 2), 16)); return out; }; const arr32 = (h) => { const b = bytes(h); if (b.length !== 32) throw new Error(`expected 32 bytes, got ${b.length}: ${h}`); return b; }; const fmtHash = (b) => "[" + b.map((x) => "0x" + x.toString(16).padStart(2, "0")).join(", ") + "]"; const fmtHashArray = (list, indent) => list.map((h) => `${indent}${fmtHash(arr32(h))},`).join("\n"); const msgBytes = bytes(kat.msg); if (kat.wots.length !== 67) throw new Error(`expected 67 WOTS chains, got ${kat.wots.length}`); if (kat.auth.length !== 10) throw new Error(`expected 10 auth nodes, got ${kat.auth.length}`); const out = `// GENERATED by scripts/gen_vectors.mjs from the OFFICIAL committed KAT // aerenew/contracts/test/fixtures/xmss-sha2_10_256-kat.json // Source: ${kat.source} // Do NOT edit by hand: re-run \`node scripts/gen_vectors.mjs\`. // // RFC 8391 XMSS-SHA2_10_256 (OID 0x00000001): n=32, w=16, len=67, h=10, SHA-256. // This is the same official reference vector the on-chain AereXmssVerifier.sol and // its independent JS oracle (contracts/test/xmssVerifier.test.js) are validated with. use crate::{Hash, XmssPubKey, XmssSig, H, LEN}; /// Long-term XMSS public root (first 32 bytes of the XMSS public key). pub const PUB_ROOT: Hash = ${fmtHash(arr32(kat.pubRoot))}; /// XMSS public SEED (last 32 bytes of the XMSS public key). pub const PUB_SEED: Hash = ${fmtHash(arr32(kat.pubSeed))}; /// Leaf index used by the signer for this signature. pub const IDX: u32 = ${kat.idx}; /// Per-signature randomizer R (from the signature). pub const R: Hash = ${fmtHash(arr32(kat.R))}; /// The signed message bytes. pub const MSG: [u8; ${msgBytes.length}] = ${fmtHash(msgBytes)}; /// The 67 WOTS+ one-time-signature chain values. pub const WOTS: [Hash; LEN] = [ ${fmtHashArray(kat.wots, " ")} ]; /// The h = 10 Merkle authentication-path nodes. pub const AUTH: [Hash; H] = [ ${fmtHashArray(kat.auth, " ")} ]; /// The official reference public key as a [\`XmssPubKey\`]. pub fn official_pubkey() -> XmssPubKey { XmssPubKey { root: PUB_ROOT, seed: PUB_SEED } } /// The official reference signature as a [\`XmssSig\`]. pub fn official_sig() -> XmssSig { XmssSig { idx: IDX, r: R, wots: WOTS, auth: AUTH } } `; writeFileSync(outPath, out); console.log(`wrote ${outPath}`); console.log(` pubRoot ${kat.pubRoot}`); console.log(` idx ${kat.idx}, msg ${kat.msg}, ${kat.wots.length} WOTS chains, ${kat.auth.length} auth nodes`);