use crate::seed::SeedRng;
use std::time::Duration;
pub struct CoverTrafficGenerator {
rng: SeedRng,
profile: TrafficProfile,
}
#[derive(Debug, Clone, Copy)]
pub enum TrafficProfile {
Messenger,
WebBrowsing,
ApiPolling,
}
pub struct CoverEvent {
pub delay: Duration,
pub size: usize,
}
impl CoverTrafficGenerator {
pub fn new(rng: SeedRng, profile: TrafficProfile) -> Self {
Self { rng, profile }
}
pub fn next_event(&mut self) -> CoverEvent {
let (d_lo, d_hi, s_lo, s_hi) = match self.profile {
TrafficProfile::Messenger => (50u64, 2000u64, 256u64, 900u64),
TrafficProfile::WebBrowsing => (200u64, 5000u64, 512u64, 4096u64),
TrafficProfile::ApiPolling => (500u64, 3000u64, 128u64, 512u64),
};
let delay = Duration::from_millis(self.rng.range(d_lo, d_hi));
let size = self.rng.range(s_lo, s_hi) as usize;
CoverEvent { delay, size }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn messenger_events_within_bounds() {
let mut g = CoverTrafficGenerator::new(SeedRng::new([2u8; 32]), TrafficProfile::Messenger);
for _ in 0..50 {
let e = g.next_event();
assert!(e.delay.as_millis() >= 50 && e.delay.as_millis() < 2000);
assert!(e.size >= 256 && e.size < 900);
}
}
#[test]
fn web_browsing_events_within_bounds() {
let mut g =
CoverTrafficGenerator::new(SeedRng::new([3u8; 32]), TrafficProfile::WebBrowsing);
for _ in 0..50 {
let e = g.next_event();
assert!(e.delay.as_millis() >= 200 && e.delay.as_millis() < 5000);
assert!(e.size >= 512 && e.size < 4096);
}
}
#[test]
fn api_polling_events_within_bounds() {
let mut g = CoverTrafficGenerator::new(SeedRng::new([4u8; 32]), TrafficProfile::ApiPolling);
for _ in 0..50 {
let e = g.next_event();
assert!(e.delay.as_millis() >= 500 && e.delay.as_millis() < 3000);
assert!(e.size >= 128 && e.size < 512);
}
}
#[test]
fn deterministic_from_same_seed() {
let seed = [0xABu8; 32];
let mut g1 = CoverTrafficGenerator::new(SeedRng::new(seed), TrafficProfile::Messenger);
let mut g2 = CoverTrafficGenerator::new(SeedRng::new(seed), TrafficProfile::Messenger);
for _ in 0..20 {
let e1 = g1.next_event();
let e2 = g2.next_event();
assert_eq!(e1.delay, e2.delay);
assert_eq!(e1.size, e2.size);
}
}
#[test]
fn different_seeds_produce_different_events() {
let mut g1 = CoverTrafficGenerator::new(SeedRng::new([1u8; 32]), TrafficProfile::Messenger);
let mut g2 = CoverTrafficGenerator::new(SeedRng::new([2u8; 32]), TrafficProfile::Messenger);
let mut differ = false;
for _ in 0..10 {
let e1 = g1.next_event();
let e2 = g2.next_event();
if e1.delay != e2.delay || e1.size != e2.size {
differ = true;
break;
}
}
assert!(differ, "different seeds should produce different sequences");
}
#[test]
fn events_vary_across_calls() {
let mut g =
CoverTrafficGenerator::new(SeedRng::new([7u8; 32]), TrafficProfile::WebBrowsing);
let events: Vec<_> = (0..20).map(|_| g.next_event()).collect();
let all_same_delay = events.windows(2).all(|w| w[0].delay == w[1].delay);
assert!(!all_same_delay, "events should vary across calls");
}
#[test]
fn profiles_have_distinct_characteristics() {
let seed = [0x42u8; 32];
let mut m = CoverTrafficGenerator::new(SeedRng::new(seed), TrafficProfile::Messenger);
let mut w = CoverTrafficGenerator::new(SeedRng::new(seed), TrafficProfile::WebBrowsing);
let mut m_total_size = 0usize;
let mut w_total_size = 0usize;
for _ in 0..100 {
m_total_size += m.next_event().size;
w_total_size += w.next_event().size;
}
assert!(
w_total_size > m_total_size,
"WebBrowsing should produce larger packets on average"
);
}
}