intentra/
replay.rs

1//! Replay attack detection using sliding window.
2
3/// 64-bit sliding window for replay detection.
4///
5/// Accepts each sequence number exactly once. Detects and rejects:
6/// - Duplicate packets (same sequence number)
7/// - Packets older than the window
8///
9/// Handles sequence number wraparound using the property that forward jumps
10/// appear as large backward jumps in unsigned arithmetic.
11pub struct ReplayWindow {
12    /// Maximum sequence number seen so far
13    max_seq: Option<u32>,
14    /// Window size
15    window: u32,
16    /// Bit map of recent sequence numbers
17    bitmap: u64,
18}
19
20impl ReplayWindow {
21    /// Create a new replay window with specified size (max 64).
22    pub fn new(window: u32) -> Self {
23        Self {
24            max_seq: None,
25            window: std::cmp::min(window, 64),
26            bitmap: 0,
27        }
28    }
29
30    /// Check if sequence number is valid (not seen before, not too old).
31    pub fn check(&mut self, seq: u32) -> bool {
32        if self.max_seq.is_none() {
33            self.max_seq = Some(seq);
34            self.bitmap = 1;
35            return true;
36        }
37
38        let max = self.max_seq.unwrap();
39
40        let is_ahead = if seq > max {
41            true
42        } else if seq == max {
43            false
44        } else {
45            (max as u64) - (seq as u64) > (1u64 << 31)
46        };
47
48        if is_ahead && seq != max {
49            let forward_dist = if seq > max {
50                seq.wrapping_sub(max)
51            } else {
52                seq.wrapping_add(1)
53            };
54
55            if forward_dist >= 64 {
56                self.bitmap = 0;
57            } else {
58                self.bitmap <<= forward_dist;
59            }
60            self.max_seq = Some(seq);
61            self.bitmap |= 1;
62            return true;
63        }
64
65        if seq == max {
66            return (self.bitmap & 1) == 0;
67        }
68
69        let backward_dist = max.wrapping_sub(seq);
70
71        if backward_dist > self.window {
72            return false;
73        }
74
75        if backward_dist >= 64 {
76            return true;
77        }
78
79        let bit = 1u64 << backward_dist;
80        if (self.bitmap & bit) != 0 {
81            return false;
82        }
83
84        self.bitmap |= bit;
85        true
86    }
87}