Skip to main content

shadow_protocols/
timing.rs

1//! Timing Engine for Traffic Pattern Obfuscation
2//! 
3//! Mimics realistic timing patterns from legitimate protocols to resist timing analysis.
4
5use rand::Rng;
6use rand_distr::{Distribution, Normal, Exp};
7use std::time::{Duration, Instant};
8
9/// Timing pattern type
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum TimingPattern {
12    /// Constant inter-packet delay
13    Constant(Duration),
14    /// Gaussian distribution
15    Gaussian { mean_ms: u64, stddev_ms: u64 },
16    /// Exponential distribution (Poisson process)
17    Exponential { rate_per_sec: f64 },
18    /// WebRTC-like pattern (burst + jitter)
19    WebRTC,
20    /// HTTPS-like pattern (request-response)
21    HTTPS,
22    /// Custom pattern
23    Custom,
24}
25
26/// Timing engine that generates realistic inter-packet delays
27pub struct TimingEngine {
28    pattern: TimingPattern,
29    last_send: Instant,
30    packet_count: u64,
31}
32
33impl TimingEngine {
34    /// Create new timing engine with pattern
35    pub fn new(pattern: TimingPattern) -> Self {
36        Self {
37            pattern,
38            last_send: Instant::now(),
39            packet_count: 0,
40        }
41    }
42
43    /// Get next delay duration
44    pub fn next_delay(&mut self) -> Duration {
45        self.packet_count += 1;
46        
47        match self.pattern {
48            TimingPattern::Constant(d) => d,
49            
50            TimingPattern::Gaussian { mean_ms, stddev_ms } => {
51                let mut rng = rand::thread_rng();
52                let normal = Normal::new(mean_ms as f64, stddev_ms as f64).unwrap();
53                let delay_ms = normal.sample(&mut rng).max(0.0) as u64;
54                Duration::from_millis(delay_ms)
55            }
56            
57            TimingPattern::Exponential { rate_per_sec } => {
58                let mut rng = rand::thread_rng();
59                let exp = Exp::new(rate_per_sec).unwrap();
60                let delay_sec = exp.sample(&mut rng);
61                Duration::from_secs_f64(delay_sec)
62            }
63            
64            TimingPattern::WebRTC => {
65                self.webrtc_timing()
66            }
67            
68            TimingPattern::HTTPS => {
69                self.https_timing()
70            }
71            
72            TimingPattern::Custom => {
73                // Placeholder for custom patterns
74                Duration::from_millis(50)
75            }
76        }
77    }
78
79    /// WebRTC-like timing: 30fps video with jitter
80    fn webrtc_timing(&mut self) -> Duration {
81        let mut rng = rand::thread_rng();
82        
83        // Base: 33.3ms per frame (30fps)
84        let base_ms = 33;
85        
86        // Add Gaussian jitter: mean=0, stddev=5ms
87        let jitter = Normal::new(0.0, 5.0).unwrap().sample(&mut rng);
88        
89        // Occasional burst packets (audio/video sync)
90        let burst = if self.packet_count % 30 == 0 {
91            // Every 30 packets, send burst
92            rng.gen_range(0..5)
93        } else {
94            base_ms
95        };
96        
97        Duration::from_millis((burst as f64 + jitter).max(1.0) as u64)
98    }
99
100    /// HTTPS-like timing: request-response pattern
101    fn https_timing(&mut self) -> Duration {
102        let mut rng = rand::thread_rng();
103        
104        // Alternate between request and response
105        if self.packet_count % 2 == 0 {
106            // Request: user think time (exponential)
107            let think_time = Exp::new(0.5).unwrap().sample(&mut rng);
108            Duration::from_secs_f64(think_time)
109        } else {
110            // Response: server processing (Gaussian)
111            let processing: f64 = Normal::new(50.0, 20.0).unwrap().sample(&mut rng);
112            Duration::from_millis(processing.max(10.0) as u64)
113        }
114    }
115
116    /// Wait for next packet time (maintains target rate)
117    pub async fn wait_next(&mut self) {
118        let delay = self.next_delay();
119        let elapsed = self.last_send.elapsed();
120        
121        if delay > elapsed {
122            tokio::time::sleep(delay - elapsed).await;
123        }
124        
125        self.last_send = Instant::now();
126    }
127
128    /// Change timing pattern
129    pub fn set_pattern(&mut self, pattern: TimingPattern) {
130        self.pattern = pattern;
131    }
132
133    /// Get current pattern
134    pub fn pattern(&self) -> TimingPattern {
135        self.pattern
136    }
137
138    /// Reset timing state
139    pub fn reset(&mut self) {
140        self.last_send = Instant::now();
141        self.packet_count = 0;
142    }
143}
144
145/// Burst timing generator for packet trains
146pub struct BurstTiming {
147    /// Packets per burst
148    burst_size: usize,
149    /// Inter-burst delay
150    burst_delay: Duration,
151    /// Inter-packet delay within burst
152    packet_delay: Duration,
153    /// Current position in burst
154    position: usize,
155}
156
157impl BurstTiming {
158    /// Create new burst timing
159    pub fn new(burst_size: usize, burst_delay: Duration, packet_delay: Duration) -> Self {
160        Self {
161            burst_size,
162            burst_delay,
163            packet_delay,
164            position: 0,
165        }
166    }
167
168    /// Get next delay
169    pub fn next_delay(&mut self) -> Duration {
170        self.position += 1;
171        
172        if self.position >= self.burst_size {
173            self.position = 0;
174            self.burst_delay
175        } else {
176            self.packet_delay
177        }
178    }
179}
180
181/// Adaptive timing that adjusts to network conditions
182pub struct AdaptiveTiming {
183    /// Base timing engine
184    engine: TimingEngine,
185    /// Measured RTT samples
186    rtt_samples: Vec<Duration>,
187    /// Maximum samples to keep
188    max_samples: usize,
189}
190
191impl AdaptiveTiming {
192    /// Create new adaptive timing
193    pub fn new(pattern: TimingPattern) -> Self {
194        Self {
195            engine: TimingEngine::new(pattern),
196            rtt_samples: Vec::new(),
197            max_samples: 100,
198        }
199    }
200
201    /// Record RTT measurement
202    pub fn record_rtt(&mut self, rtt: Duration) {
203        self.rtt_samples.push(rtt);
204        
205        if self.rtt_samples.len() > self.max_samples {
206            self.rtt_samples.remove(0);
207        }
208        
209        // Adapt timing based on RTT
210        self.adapt();
211    }
212
213    /// Adapt timing pattern based on measurements
214    fn adapt(&mut self) {
215        if self.rtt_samples.is_empty() {
216            return;
217        }
218
219        // Calculate mean RTT
220        let mean_rtt: Duration = self.rtt_samples.iter().sum::<Duration>() / self.rtt_samples.len() as u32;
221        
222        // Adjust timing to avoid congestion
223        // Simple heuristic: if RTT > 200ms, slow down
224        if mean_rtt > Duration::from_millis(200) {
225            // Slow down by 20%
226            let current_delay = self.engine.next_delay();
227            let new_delay = current_delay + current_delay / 5;
228            self.engine.set_pattern(TimingPattern::Constant(new_delay));
229        }
230    }
231
232    /// Get next delay
233    pub fn next_delay(&mut self) -> Duration {
234        self.engine.next_delay()
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn test_constant_timing() {
244        let mut engine = TimingEngine::new(TimingPattern::Constant(Duration::from_millis(100)));
245        
246        for _ in 0..10 {
247            assert_eq!(engine.next_delay(), Duration::from_millis(100));
248        }
249    }
250
251    #[test]
252    fn test_gaussian_timing() {
253        let mut engine = TimingEngine::new(TimingPattern::Gaussian {
254            mean_ms: 50,
255            stddev_ms: 10,
256        });
257        
258        let mut delays = Vec::new();
259        for _ in 0..100 {
260            delays.push(engine.next_delay().as_millis());
261        }
262        
263        // Check that mean is approximately 50ms
264        let mean: u128 = delays.iter().sum::<u128>() / delays.len() as u128;
265        assert!((mean as i128 - 50).abs() < 15, "Mean: {}", mean);
266    }
267
268    #[test]
269    fn test_burst_timing() {
270        let mut burst = BurstTiming::new(
271            5,
272            Duration::from_millis(100),
273            Duration::from_millis(10),
274        );
275        
276        // First 4 packets: short delay
277        for _ in 0..4 {
278            assert_eq!(burst.next_delay(), Duration::from_millis(10));
279        }
280        
281        // 5th packet: long delay (burst boundary)
282        assert_eq!(burst.next_delay(), Duration::from_millis(100));
283    }
284
285    #[test]
286    fn test_pattern_switching() {
287        let mut engine = TimingEngine::new(TimingPattern::Constant(Duration::from_millis(50)));
288        
289        assert_eq!(engine.next_delay(), Duration::from_millis(50));
290        
291        engine.set_pattern(TimingPattern::Constant(Duration::from_millis(100)));
292        
293        assert_eq!(engine.next_delay(), Duration::from_millis(100));
294    }
295}