stochastic-routing-extended 1.0.2

SRX (Stochastic Routing eXtended) — a next-generation VPN protocol with stochastic routing, DPI evasion, post-quantum cryptography, and multi-transport channel splitting
Documentation
//! Cover traffic generation: background noise that mimics real user activity.

use crate::seed::SeedRng;
use std::time::Duration;

/// Generates background cover traffic to mask real VPN data patterns.
///
/// Emulates various traffic profiles: messaging apps, web browsing, API calls.
pub struct CoverTrafficGenerator {
    rng: SeedRng,
    profile: TrafficProfile,
}

/// Pre-defined traffic profiles for cover traffic.
#[derive(Debug, Clone, Copy)]
pub enum TrafficProfile {
    /// Simulate messaging app (short bursts, long pauses).
    Messenger,
    /// Simulate web browsing (periodic page loads).
    WebBrowsing,
    /// Simulate API polling (regular intervals).
    ApiPolling,
}

/// A single cover traffic event.
pub struct CoverEvent {
    /// Delay before sending this packet.
    pub delay: Duration,
    /// Size of the cover packet in bytes.
    pub size: usize,
}

impl CoverTrafficGenerator {
    pub fn new(rng: SeedRng, profile: TrafficProfile) -> Self {
        Self { rng, profile }
    }

    /// Generate the next cover traffic event (deterministic from [`SeedRng`] stream).
    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;
        }
        // WebBrowsing has larger size range (512-4096) vs Messenger (256-900)
        assert!(
            w_total_size > m_total_size,
            "WebBrowsing should produce larger packets on average"
        );
    }
}