use std::panic::{catch_unwind, AssertUnwindSafe};
use crate::honey::{decrypt, encrypt_pin, HoneyOptions};
use crate::kdf::{KdfParams, SALT_LEN};
use crate::lab::engine::{Attack, AttackOutcome, Rng};
const MAGIC: [u8; 4] = *b"QHNY";
fn cheap() -> HoneyOptions<'static> {
HoneyOptions {
pepper: b"",
kdf_params: KdfParams {
mem_kib: 64,
iterations: 1,
parallelism: 1,
},
}
}
pub struct HoneyFuzz;
impl HoneyFuzz {
pub fn new() -> Self {
Self
}
}
impl Default for HoneyFuzz {
fn default() -> Self {
Self::new()
}
}
impl Attack for HoneyFuzz {
fn name(&self) -> &'static str {
"honey/parser-fuzz"
}
fn step(&mut self, rng: &mut Rng) -> AttackOutcome {
let blob = build_adversarial(rng);
let n = blob.len();
let res = catch_unwind(AssertUnwindSafe(|| {
let _ = decrypt(&blob, "clave-fuzz", b"");
}));
if res.is_err() {
return AttackOutcome::Breach(format!("pánico en honey::decrypt con {n} bytes"));
}
AttackOutcome::Advanced
}
}
fn valid_header(rng: &mut Rng, declared_len: u32) -> Vec<u8> {
let mut v = MAGIC.to_vec();
v.push(1); for _ in 0..SALT_LEN {
v.push(rng.byte());
}
v.extend_from_slice(&64u32.to_be_bytes()); v.extend_from_slice(&1u32.to_be_bytes()); v.extend_from_slice(&1u32.to_be_bytes()); v.extend_from_slice(&10u16.to_be_bytes()); v.extend_from_slice(&declared_len.to_be_bytes()); v
}
fn build_adversarial(rng: &mut Rng) -> Vec<u8> {
match rng.below(6) {
0 => (0..rng.below(200)).map(|_| rng.byte()).collect(),
1 => {
let mut v = MAGIC.to_vec();
for _ in 0..rng.below(80) {
v.push(rng.byte());
}
v
}
2 => valid_header(rng, u32::MAX),
3 => {
let mut v = valid_header(rng, 4);
let off = 5 + SALT_LEN; v[off..off + 4].copy_from_slice(&u32::MAX.to_be_bytes());
v
}
4 => {
let full = encrypt_pin("4913", "otra", &cheap()).expect("cifrado");
full[..full.len() / 2].to_vec()
}
_ => {
let mut full = encrypt_pin("4913", "otra", &cheap()).expect("cifrado");
for _ in 0..rng.below(40) {
full.push(rng.byte());
}
full
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lab::engine::run;
#[test]
fn honey_parser_never_panics_on_adversarial_input() {
let mut fuzz = HoneyFuzz::new();
let report = run(&mut fuzz, 0xF0F0, 1000);
assert!(report.attempts > 0);
assert!(
report.is_clean(),
"el parser honey no debe entrar en pánico: {:?}",
report.breaches
);
}
}