#![no_main] use libfuzzer_sys::fuzz_target; use soliton::{ identity::{generate_identity, HybridSignature, IdentityPublicKey}, kex::{verify_bundle, PreKeyBundle}, primitives::xwing, }; use std::sync::LazyLock; // KNOWN_IK is used as BOTH the "trusted" key AND the bundle's ik_pub. // verify_bundle's first check is `bundle.ik_pub == known_ik`; a mismatch // returns BundleVerificationFailed immediately without reaching the SPK // signature check. Supplying the same value for both ensures the IK check // always passes, so every input exercises the OPK co-presence guard, // crypto_version validation, and hybrid_verify — the SPK sig-forge surface. static KNOWN_IK: LazyLock = LazyLock::new(|| generate_identity().unwrap().public_key); const SPK: usize = 1216; const SIG: usize = 3373; // Wire layout (ik_pub is always KNOWN_IK — not part of fuzz input): // spk_pub (1216) | spk_sig (3373) | spk_id (4 BE) // | has_opk (1) | [opk_pub (1216) | opk_id (4 BE)] // | crypto_version (rest, strict UTF-8; bail if invalid) const MIN: usize = SPK + SIG + 4 + 1; fuzz_target!(|data: &[u8]| { if data.len() < MIN { return; } let mut off = 0; let Ok(spk_pub) = xwing::PublicKey::from_bytes(data[off..off + SPK].to_vec()) else { return; }; off += SPK; let Ok(spk_sig) = HybridSignature::from_bytes(data[off..off + SIG].to_vec()) else { return; }; off += SIG; let spk_id = u32::from_be_bytes(data[off..off + 4].try_into().unwrap()); off += 4; // Two independent bits control opk_pub and opk_id presence, allowing // the fuzzer to reach the co-presence guard in verify_bundle (one Some, // the other None). Bit 0 = has_opk_pub, bit 1 = has_opk_id. let opk_flags = data[off]; let has_opk_pub = opk_flags & 0x01 != 0; let has_opk_id = opk_flags & 0x02 != 0; off += 1; let opk_pub = if has_opk_pub { if data.len() < off + SPK { return; } let Ok(opk) = xwing::PublicKey::from_bytes(data[off..off + SPK].to_vec()) else { return; }; off += SPK; Some(opk) } else { None }; let opk_id = if has_opk_id { if data.len() < off + 4 { return; } let id = u32::from_be_bytes(data[off..off + 4].try_into().unwrap()); off += 4; Some(id) } else { None }; let Ok(crypto_version) = String::from_utf8(data[off..].to_vec()) else { return; }; let bundle = PreKeyBundle { ik_pub: KNOWN_IK.clone(), crypto_version, spk_pub, spk_id, spk_sig, opk_pub, opk_id, }; // verify_bundle must never panic regardless of input. // Exercises: crypto_version validation, OPK co-presence guard (mismatched // opk_pub/opk_id via independent flag bits), and SPK signature verification // (hybrid_verify) — the main sig-forge attack surface. let _ = verify_bundle(bundle, &KNOWN_IK); });