Skip to main content

phantom_protocol/
validation.rs

1use anyhow::{bail, Result};
2
3pub struct InputValidator;
4
5impl InputValidator {
6    /// Validate message size to prevent DoS (OOM)
7    pub fn validate_message_size(data: &[u8], max_size: usize) -> Result<()> {
8        if data.is_empty() {
9            bail!("Empty message");
10        }
11        if data.len() > max_size {
12            bail!(
13                "Message too large: {} bytes (max: {})",
14                data.len(),
15                max_size
16            );
17        }
18        Ok(())
19    }
20
21    /// Validate Group ID length (must be 16 bytes)
22    pub fn validate_group_id(group_id: &[u8]) -> Result<[u8; 16]> {
23        if group_id.len() != 16 {
24            bail!(
25                "Invalid group_id length: expected 16, got {}",
26                group_id.len()
27            );
28        }
29
30        // Check for zero-ID (reserved)
31        if group_id.iter().all(|&b| b == 0) {
32            bail!("Zero group_id not allowed");
33        }
34
35        let mut gid = [0u8; 16];
36        gid.copy_from_slice(group_id);
37        Ok(gid)
38    }
39
40    /// Validate Epoch to prevent processing very old or future frames
41    pub fn validate_epoch(epoch: u64, current_epoch: u64) -> Result<()> {
42        const MAX_EPOCH_DRIFT: u64 = 50; // Allow some drift for commit implementation
43
44        // Future check
45        if epoch > current_epoch + MAX_EPOCH_DRIFT {
46            bail!(
47                "Epoch too far in future: {} (current: {})",
48                epoch,
49                current_epoch
50            );
51        }
52
53        // Past check (strictness depends on application logic, usually we reject old epochs)
54        // Here we allow some history for reordering
55        if current_epoch > MAX_EPOCH_DRIFT && epoch < current_epoch - MAX_EPOCH_DRIFT {
56            bail!("Epoch too old: {} (current: {})", epoch, current_epoch);
57        }
58
59        Ok(())
60    }
61}