aere-research/pq-finality-circuit/xmss-verify-core/tests/kat.rs
Aere Network 6cb0140fae Republished from a clean root: the compiled artifact is gone from history, and the local line of work joins the sanitized public line
The public history carried kat/__pycache__/mlkem768_reference.cpython-314.pyc,
a compiled Python artifact embedding the operator's absolute local path. Text
secret scanners do not read compiled binaries, which is exactly how it slipped
through, and removing it from the tip would have left it reachable through the
old root commits. So this repository is republished from a single clean root.

This root also carries, from the previously unpublished line of work:
- corrected LICENSE year, LICENSING.md, VERIFY-POLICY.md, and
  CITATIONS-UNRESOLVED.md remeasured 2026-08-11 (101 paths, README aligned)
- O-018: run_consensus_verification.py ran 19 of 29 models and reported PASS;
  it now runs all 29, and computemarket_smt.py gains resolveByTimeout /
  reclaimUnsettled cases plus a negative control
- O-006: the word 'audited' removed from next to Bouncy Castle, twice, after a
  concurrent edit resurrected it
- O-014: prior art named and dated - Algorand's native falcon_verify shipped
  about ten months before AERE's precompiles; the primacy claim is withdrawn
  where it was implied
- bench/ scripts parametrized so they actually run for an outsider (the
  earlier textual sanitization left $STAGING unexpanded inside Python strings)
- AIP-2/AIP-3 errata with measured figures, spec remeasurements at 2026-08-01,
  and the spec-zk-stack retractions (owner is an operational key, not the
  Foundation; 'maximally sound' withdrawn; aggregator V1 deprecated)
The redacted bench-host environment files from the sanitized line are kept
exactly as published; the unredacted local variants are not carried.
2026-08-15 13:52:14 +03:00

148 lines
5.1 KiB
Rust

//! REAL known-answer validation of the XMSS-SHA2_10_256 verification core.
//!
//! The vector in `xmss_verify_core::vectors` is generated (by
//! `scripts/gen_vectors.mjs`) from the OFFICIAL github.com/XMSS/xmss-reference
//! deterministic known-answer vector committed at
//! `aerenew/contracts/test/fixtures/xmss-sha2_10_256-kat.json`, the same vector the
//! deployed on-chain `AereXmssVerifier.sol` and its independent JS oracle use.
//!
//! Positive test: the genuine signature verifies (recovered root == official root).
//! Negative tests: every single-bit tamper (WOTS+ value, auth node, message, index,
//! claimed root) MUST fail. If ANY negative test verified, the core would be unsound.
use xmss_verify_core::sha256::sha256;
use xmss_verify_core::vectors as v;
use xmss_verify_core::{xmss_recover_root, xmss_verify, XmssPubKey};
/// Flip the least-significant bit of byte 0 of a 32-byte value.
fn flip(mut h: [u8; 32]) -> [u8; 32] {
h[0] ^= 0x01;
h
}
#[test]
fn sha256_self_check_fips_abc() {
// FIPS 180-4 sample: SHA256("abc").
let got = sha256(b"abc");
let want = [
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22,
0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00,
0x15, 0xad,
];
assert_eq!(got, want, "bundled SHA-256 disagrees with FIPS 180-4 SHA256(\"abc\")");
}
#[test]
fn sha256_self_check_empty() {
// SHA256("") — exercises the pure-padding block path.
let got = sha256(b"");
let want = [
0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9,
0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52,
0xb8, 0x55,
];
assert_eq!(got, want, "bundled SHA-256 disagrees with FIPS 180-4 SHA256(\"\")");
}
#[test]
fn recovers_the_official_reference_root() {
let pk = v::official_pubkey();
let sig = v::official_sig();
let recovered = xmss_recover_root(&pk, &sig, &v::MSG);
assert_eq!(
recovered, v::PUB_ROOT,
"recovered XMSS root != official reference root"
);
}
#[test]
fn accepts_the_official_reference_signature() {
let pk = v::official_pubkey();
let sig = v::official_sig();
assert!(
xmss_verify(&pk, &sig, &v::MSG),
"official RFC 8391 XMSS-SHA2_10_256 KAT signature did not verify"
);
}
#[test]
fn rejects_a_tampered_wots_value() {
let pk = v::official_pubkey();
let mut sig = v::official_sig();
sig.wots[0] = flip(sig.wots[0]);
assert!(!xmss_verify(&pk, &sig, &v::MSG), "tampered WOTS+ value verified");
}
#[test]
fn rejects_a_tampered_wots_tail_value() {
let pk = v::official_pubkey();
let mut sig = v::official_sig();
sig.wots[66] = flip(sig.wots[66]); // last (checksum) chain
assert!(!xmss_verify(&pk, &sig, &v::MSG), "tampered checksum WOTS+ value verified");
}
#[test]
fn rejects_a_tampered_auth_path_node() {
let pk = v::official_pubkey();
let mut sig = v::official_sig();
sig.auth[9] = flip(sig.auth[9]); // top auth node
assert!(!xmss_verify(&pk, &sig, &v::MSG), "tampered authentication-path node verified");
}
#[test]
fn rejects_a_tampered_message() {
let pk = v::official_pubkey();
let sig = v::official_sig();
assert!(!xmss_verify(&pk, &sig, &[0x26]), "signature verified over a different message");
}
#[test]
fn rejects_a_wrong_leaf_index() {
let pk = v::official_pubkey();
let mut sig = v::official_sig();
sig.idx += 1;
assert!(!xmss_verify(&pk, &sig, &v::MSG), "signature verified at the wrong leaf index");
}
#[test]
fn rejects_a_wrong_claimed_root() {
// A different committed root must not be recoverable from this signature.
let pk = XmssPubKey { root: flip(v::PUB_ROOT), seed: v::PUB_SEED };
let sig = v::official_sig();
assert!(!xmss_verify(&pk, &sig, &v::MSG), "signature verified against a wrong claimed root");
}
#[test]
fn rejects_a_tampered_randomizer() {
let pk = v::official_pubkey();
let mut sig = v::official_sig();
sig.r = flip(sig.r);
assert!(!xmss_verify(&pk, &sig, &v::MSG), "signature verified with a tampered R randomizer");
}
#[test]
fn chain_lengths_checksum_shape() {
// The 3 checksum digits are a deterministic function of the 64 message digits;
// recompute the WOTS+ checksum independently and confirm the core agrees.
let pk = v::official_pubkey();
let sig = v::official_sig();
// Rebuild the exact message hash the verifier uses, then compare chain_lengths.
let mut buf = [0u8; 32 + 32 + 32 + 32 + 1];
buf[31] = 2; // toByte(2,32)
buf[32..64].copy_from_slice(&sig.r);
buf[64..96].copy_from_slice(&pk.root);
buf[124..128].copy_from_slice(&sig.idx.to_be_bytes());
buf[128] = v::MSG[0];
let mhash = sha256(&buf);
let d = xmss_verify_core::chain_lengths(&mhash);
let mut csum: u32 = 0;
for i in 0..32 {
csum += (15 - (mhash[i] >> 4) as u32) + (15 - (mhash[i] & 0x0f) as u32);
}
let c = csum << 4;
assert_eq!(d[64], (c >> 12) & 0x0f);
assert_eq!(d[65], (c >> 8) & 0x0f);
assert_eq!(d[66], (c >> 4) & 0x0f);
}