#![no_main] use libfuzzer_sys::fuzz_target; use soliton::primitives::argon2::{argon2id, Argon2Params}; fuzz_target!(|data: &[u8]| { // Wire layout: [m_cost (4)] [t_cost (4)] [p_cost (4)] [out_len (2)] [salt_len (1)] [salt (..)] [password (..)] // Minimum: 15 bytes (4+4+4+2+1). if data.len() < 15 { return; } let m_cost = u32::from_le_bytes(data[0..4].try_into().unwrap()); let t_cost = u32::from_le_bytes(data[4..8].try_into().unwrap()); let p_cost = u32::from_le_bytes(data[8..12].try_into().unwrap()); let out_len = u16::from_le_bytes(data[12..14].try_into().unwrap()) as usize; let salt_len = data[14] as usize; let rest = &data[15..]; if rest.len() < salt_len { return; } let salt = &rest[..salt_len]; let password = &rest[salt_len..]; // Cap m_cost to prevent actual multi-GiB allocation during fuzzing. // The validation boundary is at 4_194_304 — we test up to 2× that // but never allocate: invalid params are rejected before allocation. // For valid params, cap at 1024 KiB (1 MiB) to keep fuzzing fast. let m_cost = if m_cost > 8_388_608 { m_cost } else { m_cost.min(1024) }; // Cap out_len to prevent large allocation. let out_len = out_len.min(4097); if out_len == 0 { return; } let params = Argon2Params { m_cost, t_cost, p_cost, }; let mut out = vec![0u8; out_len]; // argon2id must never panic regardless of parameters. Exercises: // salt minimum check, output length bounds, cost parameter caps, // argon2 library parameter validation, error-path output zeroization. let _ = argon2id(password, salt, params, &mut out); });