#![no_main] use libfuzzer_sys::fuzz_target; use soliton::identity::{generate_identity, hybrid_sign, hybrid_verify, GeneratedIdentity, IdentityPublicKey, IdentitySecretKey}; use std::sync::LazyLock; struct SignerKeys { pk: IdentityPublicKey, sk: IdentitySecretKey, } // Fixed signer — keygen is expensive, amortise across corpus runs. static SIGNER: LazyLock = LazyLock::new(|| { let GeneratedIdentity { public_key: pk, secret_key: sk, .. } = generate_identity().unwrap(); SignerKeys { pk, sk } }); fuzz_target!(|data: &[u8]| { if data.is_empty() { return; } // Property 1: sign(msg) → verify(msg, sig) must always succeed. let Ok(sig) = hybrid_sign(&SIGNER.sk, data) else { panic!("hybrid_sign failed on a valid key"); }; if hybrid_verify(&SIGNER.pk, data, &sig).is_err() { panic!("hybrid_verify rejected a freshly-signed message"); } // Property 2: verify(tampered_msg, sig) must always fail. // Flip the LSB of the first byte — always changes the message content. let mut tampered = data.to_vec(); tampered[0] ^= 0x01; if hybrid_verify(&SIGNER.pk, &tampered, &sig).is_ok() { panic!("hybrid_verify accepted a message that differs by exactly one bit"); } });