aere-research/pq-finality-circuit/xmss-verify-core/tests/kat.rs
Aere Network 4a0b48588c Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:02:30 +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);
}