//! 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); }