#![no_main] use libfuzzer_sys::fuzz_target; use soliton::streaming::{stream_decrypt_init, stream_encrypt_init}; fuzz_target!(|data: &[u8]| { // Round-trip fuzz: encrypt arbitrary data, then decrypt and verify. // Exercises the full encrypt→decrypt pipeline with variable-size inputs, // compression toggle, and multi-chunk splitting. if data.is_empty() { return; } // First byte selects compression mode; rest is plaintext. let compress = data[0] & 0x01 != 0; let plaintext = &data[1..]; let key = [0x42u8; 32]; let aad = b"fuzz-rt"; let mut enc = match stream_encrypt_init(&key, aad, compress) { Ok(e) => e, Err(_) => return, }; let header = enc.header(); let chunk_size = soliton::constants::STREAM_CHUNK_SIZE; let mut chunks: Vec> = Vec::new(); // Encrypt in STREAM_CHUNK_SIZE pieces. if plaintext.len() > chunk_size { let full_chunks = plaintext.len() / chunk_size; for i in 0..full_chunks { let start = i * chunk_size; let end = start + chunk_size; let is_last = end >= plaintext.len(); match enc.encrypt_chunk(&plaintext[start..end], is_last) { Ok(ct) => chunks.push(ct), Err(_) => return, } if is_last { break; } } if !enc.is_finalized() { let start = (plaintext.len() / chunk_size) * chunk_size; match enc.encrypt_chunk(&plaintext[start..], true) { Ok(ct) => chunks.push(ct), Err(_) => return, } } } else { match enc.encrypt_chunk(plaintext, true) { Ok(ct) => chunks.push(ct), Err(_) => return, } } // Decrypt sequentially. let mut dec = match stream_decrypt_init(&key, &header, aad) { Ok(d) => d, Err(_) => panic!("decrypt_init failed on valid header"), }; let mut recovered = Vec::new(); for ct in &chunks { match dec.decrypt_chunk(ct) { Ok((pt, _)) => recovered.extend_from_slice(&pt), Err(e) => panic!("decrypt_chunk failed: {e:?}"), } } assert_eq!(recovered, plaintext, "round-trip mismatch"); });