Skip to main content

apfsds_obfuscation/
timing.rs

1//! Timing jitter for traffic obfuscation
2
3use std::time::Duration;
4
5/// Default jitter range in milliseconds
6pub const DEFAULT_JITTER_MS: u64 = 50;
7
8/// Default inter-frame delay range in microseconds
9pub const DEFAULT_INTER_FRAME_DELAY_US: (u64, u64) = (100, 5000);
10
11/// Jitter strategy for timing randomization
12#[derive(Debug, Clone)]
13pub enum JitterStrategy {
14    /// Fixed range (uniform distribution)
15    Fixed { max_ms: u64 },
16    /// Normal distribution (more realistic)
17    Normal { mean_ms: f64, std_dev_ms: f64 },
18    /// Exponential distribution (models network delays)
19    Exponential { lambda: f64 },
20    /// Adaptive based on network conditions
21    Adaptive { base_ms: u64, factor: f64 },
22}
23
24impl Default for JitterStrategy {
25    fn default() -> Self {
26        Self::Fixed {
27            max_ms: DEFAULT_JITTER_MS,
28        }
29    }
30}
31
32/// Timing configuration
33#[derive(Debug, Clone)]
34pub struct TimingConfig {
35    /// Jitter strategy
36    pub jitter_strategy: JitterStrategy,
37
38    /// Inter-frame delay range (us)
39    pub inter_frame_delay: (u64, u64),
40
41    /// Reconnect interval range (seconds)
42    pub reconnect_interval: (u64, u64),
43
44    /// Noise traffic interval range (seconds)
45    pub noise_interval: (u64, u64),
46}
47
48impl Default for TimingConfig {
49    fn default() -> Self {
50        Self {
51            jitter_strategy: JitterStrategy::default(),
52            inter_frame_delay: DEFAULT_INTER_FRAME_DELAY_US,
53            reconnect_interval: (60, 180),
54            noise_interval: (10, 30),
55        }
56    }
57}
58
59impl TimingConfig {
60    /// Generate a random jitter duration
61    pub fn random_jitter(&self) -> Duration {
62        let jitter_ms = match &self.jitter_strategy {
63            JitterStrategy::Fixed { max_ms } => {
64                // Uniform distribution [0, max_ms]
65                fastrand::u64(0..=*max_ms)
66            }
67            JitterStrategy::Normal {
68                mean_ms,
69                std_dev_ms,
70            } => {
71                // Box-Muller transform for normal distribution
72                let u1 = fastrand::f64();
73                let u2 = fastrand::f64();
74                let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
75                let jitter = mean_ms + z * std_dev_ms;
76                jitter.max(0.0) as u64
77            }
78            JitterStrategy::Exponential { lambda } => {
79                // Exponential distribution
80                let u = fastrand::f64();
81                let jitter = -(1.0 / lambda) * u.ln();
82                (jitter * 1000.0).max(0.0) as u64
83            }
84            JitterStrategy::Adaptive { base_ms, factor } => {
85                // Adaptive jitter based on base and factor
86                // In real implementation, factor could be adjusted based on network RTT
87                let adaptive_max = (*base_ms as f64 * factor) as u64;
88                fastrand::u64(0..=adaptive_max)
89            }
90        };
91
92        Duration::from_millis(jitter_ms)
93    }
94
95    /// Generate a random inter-frame delay
96    pub fn random_inter_frame_delay(&self) -> Duration {
97        let (min, max) = self.inter_frame_delay;
98        Duration::from_micros(fastrand::u64(min..=max))
99    }
100
101    /// Generate a random reconnect interval
102    pub fn random_reconnect_interval(&self) -> Duration {
103        let (min, max) = self.reconnect_interval;
104        Duration::from_secs(fastrand::u64(min..=max))
105    }
106
107    /// Generate a random noise interval
108    pub fn random_noise_interval(&self) -> Duration {
109        let (min, max) = self.noise_interval;
110        Duration::from_secs(fastrand::u64(min..=max))
111    }
112}
113
114/// Async sleep with jitter (requires tokio)
115pub async fn sleep_with_jitter(base: Duration, max_jitter_ms: u64) {
116    let jitter = Duration::from_millis(fastrand::u64(0..=max_jitter_ms));
117    tokio::time::sleep(base + jitter).await;
118}
119
120/// Calculate delay based on packet timing to avoid detection
121pub fn calculate_adaptive_delay(
122    last_packet_time_ms: u64,
123    current_time_ms: u64,
124    target_rate_bps: u64,
125) -> Duration {
126    // Calculate expected interval based on target rate
127    // (simplified - in practice would consider packet sizes)
128    let expected_interval_ms = 1000 / (target_rate_bps / 8 / 1500).max(1);
129
130    let elapsed = current_time_ms.saturating_sub(last_packet_time_ms);
131
132    if elapsed >= expected_interval_ms {
133        // We're on time or behind, minimal delay
134        Duration::from_millis(fastrand::u64(0..10))
135    } else {
136        // We're ahead, add delay
137        let delay = expected_interval_ms - elapsed;
138        Duration::from_millis(delay + fastrand::u64(0..10))
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_default_config() {
148        let config = TimingConfig::default();
149
150        assert_eq!(config.max_jitter_ms, 50);
151        assert_eq!(config.reconnect_interval, (60, 180));
152    }
153
154    #[test]
155    fn test_random_jitter() {
156        let config = TimingConfig::default();
157
158        for _ in 0..100 {
159            let jitter = config.random_jitter();
160            assert!(jitter <= Duration::from_millis(50));
161        }
162    }
163
164    #[test]
165    fn test_random_reconnect() {
166        let config = TimingConfig::default();
167
168        for _ in 0..100 {
169            let interval = config.random_reconnect_interval();
170            assert!(interval >= Duration::from_secs(60));
171            assert!(interval <= Duration::from_secs(180));
172        }
173    }
174
175    #[test]
176    fn test_adaptive_delay() {
177        // We're behind schedule
178        let delay = calculate_adaptive_delay(0, 1000, 1_000_000);
179        assert!(delay < Duration::from_millis(100));
180
181        // We're ahead of schedule
182        let delay = calculate_adaptive_delay(995, 1000, 1_000_000);
183        // Should have some delay since we sent recently
184    }
185}