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
//! Configuration types for SRX protocol nodes.

use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;

/// Top-level configuration for an SRX node (client or server).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SrxConfig {
    /// Role of this node.
    pub role: NodeRole,

    /// Enabled transport protocols.
    pub transports: TransportConfig,

    /// Cryptographic settings.
    pub crypto: CryptoConfig,

    /// Seed and stochastic behavior settings.
    pub stochastic: StochasticConfig,

    /// DPI evasion and masking settings.
    pub masking: MaskingConfig,

    /// Channel management settings.
    pub channel: ChannelConfig,

    /// Replay-window persistence settings.
    pub replay: ReplayConfig,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeRole {
    Client,
    Server,
}

/// Configuration for available transports.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransportConfig {
    pub tcp_enabled: bool,
    pub udp_enabled: bool,
    pub quic_enabled: bool,
    pub websocket_enabled: bool,
    pub grpc_enabled: bool,
    pub http2_enabled: bool,
    /// Base port range for dynamic listen-layout.
    pub port_range: (u16, u16),
}

/// Cryptographic algorithm selection.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CryptoConfig {
    /// AEAD cipher selection.
    pub aead: AeadCipher,
    /// Enable post-quantum hybrid key exchange.
    pub pqc_enabled: bool,
    /// Re-key interval bounds (min, max) in packets.
    pub rekey_interval: (u64, u64),
    /// Parallel AEAD worker threads (`0` = [`crate::crypto::DEFAULT_AEAD_WORKER_COUNT`]).
    pub aead_worker_count: usize,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AeadCipher {
    ChaCha20Poly1305,
    Aes256Gcm,
}

/// Stochastic behavior parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StochasticConfig {
    /// Seed rotation interval.
    pub seed_rotation_interval: Duration,
    /// Enable channel hopping.
    pub channel_hopping: bool,
    /// Enable the non-linear retry scheme.
    pub nonlinear_retries: bool,
}

/// DPI evasion and masking parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MaskingConfig {
    /// Enable jitter modeling on packet timing.
    pub jitter_enabled: bool,
    /// Enable random length padding.
    pub padding_enabled: bool,
    /// Enable background cover traffic.
    pub cover_traffic_enabled: bool,
    /// Protocol mimicry mode.
    pub mimicry_mode: MimicryMode,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum MimicryMode {
    /// No mimicry, raw frames.
    None,
    /// Mimic HTTPS traffic patterns.
    Https,
    /// Mimic gRPC streams.
    Grpc,
    /// Mimic WebRTC data channels.
    WebRtc,
}

/// Channel management parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChannelConfig {
    /// Maximum simultaneous transport channels.
    pub max_channels: usize,
    /// Health check interval per channel.
    pub health_check_interval: Duration,
    /// Enable shadow (backup) session.
    pub shadow_session_enabled: bool,
}

/// Replay state persistence parameters.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReplayConfig {
    /// Enable automatic load/save of replay-window state.
    pub persist_enabled: bool,
    /// Replay state backend adapter.
    pub backend: ReplayBackend,
    /// JSON file path storing replay state (`top` + bitmap).
    pub state_file: PathBuf,
    /// Logical storage key used by non-file backends (SQLite/Redis).
    pub storage_key: String,
    /// Integrity policy for persisted replay snapshots.
    pub integrity: ReplayIntegrityConfig,
}

/// Replay persistence backend.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ReplayBackend {
    /// Local JSON file at [`ReplayConfig::state_file`].
    FileJson,
    /// SQLite row storage (`k` -> `v` BLOB).
    Sqlite { path: PathBuf, table: String },
    /// Redis key/value storage (`key_prefix + storage_key`).
    Redis { url: String, key_prefix: String },
}

/// Replay snapshot integrity settings.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReplayIntegrityConfig {
    /// Validation mode.
    pub mode: ReplayIntegrityMode,
    /// Optional key used for `HmacSha256` mode.
    pub hmac_key: Option<Vec<u8>>,
    /// HMAC key source abstraction for operational key management.
    pub hmac_key_provider: ReplayHmacKeyProvider,
}

/// Replay snapshot integrity mode.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ReplayIntegrityMode {
    /// Disable integrity verification (legacy compatibility mode).
    None,
    /// Validate SHA-256 checksum of snapshot payload.
    ChecksumSha256,
    /// Validate HMAC-SHA256 signature of snapshot payload.
    HmacSha256,
}

/// HMAC key source for replay integrity verification.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ReplayHmacKeyProvider {
    /// Use [`ReplayIntegrityConfig::hmac_key`] bytes directly.
    StaticConfig,
    /// Read key material from an environment variable.
    EnvVar {
        name: String,
        encoding: ReplayKeyEncoding,
    },
    /// Read key material from a file.
    File {
        path: PathBuf,
        encoding: ReplayKeyEncoding,
    },
    /// External/custom provider registered by the application.
    ///
    /// Use this for integrating AWS KMS / Vault / HSM / any internal secret
    /// backend without embedding provider-specific dependencies in SRX core.
    Custom { provider: String },
}

/// Encoding for externally-provided HMAC key material.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum ReplayKeyEncoding {
    RawUtf8,
    Hex,
    Base64,
}

impl SrxConfig {
    /// Parallel AEAD pool sized from [`CryptoConfig::aead_worker_count`] (use key from handshake / KDF).
    pub fn build_aead_pipeline(
        &self,
        key: &[u8; 32],
    ) -> crate::error::Result<crate::crypto::AeadPipeline> {
        crate::crypto::AeadPipeline::new(self.crypto.aead, key, self.crypto.aead_worker_count)
    }
}

impl Default for SrxConfig {
    fn default() -> Self {
        Self {
            role: NodeRole::Client,
            transports: TransportConfig {
                tcp_enabled: true,
                udp_enabled: true,
                quic_enabled: true,
                websocket_enabled: true,
                grpc_enabled: false,
                http2_enabled: false,
                port_range: (10000, 60000),
            },
            crypto: CryptoConfig {
                aead: AeadCipher::ChaCha20Poly1305,
                pqc_enabled: true,
                rekey_interval: (1000, 5000),
                aead_worker_count: crate::crypto::DEFAULT_AEAD_WORKER_COUNT,
            },
            stochastic: StochasticConfig {
                seed_rotation_interval: Duration::from_secs(30),
                channel_hopping: true,
                nonlinear_retries: true,
            },
            masking: MaskingConfig {
                jitter_enabled: true,
                padding_enabled: true,
                cover_traffic_enabled: true,
                mimicry_mode: MimicryMode::Https,
            },
            channel: ChannelConfig {
                max_channels: 4,
                health_check_interval: Duration::from_secs(5),
                shadow_session_enabled: false,
            },
            replay: ReplayConfig {
                persist_enabled: true,
                backend: ReplayBackend::FileJson,
                state_file: PathBuf::from(".srx/replay_state.json"),
                storage_key: "default".to_string(),
                integrity: ReplayIntegrityConfig {
                    mode: ReplayIntegrityMode::ChecksumSha256,
                    hmac_key: None,
                    hmac_key_provider: ReplayHmacKeyProvider::StaticConfig,
                },
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_role_and_transport_flags() {
        let cfg = SrxConfig::default();
        assert_eq!(cfg.role, NodeRole::Client);
        assert!(cfg.transports.tcp_enabled);
        assert!(cfg.transports.udp_enabled);
        assert!(cfg.transports.quic_enabled);
        assert!(cfg.transports.websocket_enabled);
        assert!(!cfg.transports.grpc_enabled);
        assert!(!cfg.transports.http2_enabled);
        assert!(cfg.replay.persist_enabled);
        assert_eq!(cfg.replay.backend, ReplayBackend::FileJson);
        assert_eq!(cfg.replay.storage_key, "default");
        assert_eq!(
            cfg.replay.integrity.mode,
            ReplayIntegrityMode::ChecksumSha256
        );
        assert!(cfg.replay.integrity.hmac_key.is_none());
        assert_eq!(
            cfg.replay.integrity.hmac_key_provider,
            ReplayHmacKeyProvider::StaticConfig
        );
        assert!(!cfg.replay.state_file.as_os_str().is_empty());
    }

    #[test]
    fn default_ranges_and_intervals_are_sane() {
        let cfg = SrxConfig::default();
        let (port_lo, port_hi) = cfg.transports.port_range;
        let (rekey_min, rekey_max) = cfg.crypto.rekey_interval;

        assert!(port_lo < port_hi);
        assert!(rekey_min <= rekey_max);
        assert!(cfg.stochastic.seed_rotation_interval > Duration::from_secs(0));
        assert!(cfg.channel.health_check_interval > Duration::from_secs(0));
    }

    #[test]
    fn default_crypto_and_masking_values_match_expected() {
        let cfg = SrxConfig::default();
        assert_eq!(cfg.crypto.aead, AeadCipher::ChaCha20Poly1305);
        assert!(cfg.crypto.pqc_enabled);
        assert_eq!(
            cfg.crypto.aead_worker_count,
            crate::crypto::DEFAULT_AEAD_WORKER_COUNT
        );
        assert!(cfg.masking.jitter_enabled);
        assert!(cfg.masking.padding_enabled);
        assert!(cfg.masking.cover_traffic_enabled);
        assert_eq!(cfg.masking.mimicry_mode, MimicryMode::Https);
    }

    #[test]
    fn build_aead_pipeline_uses_explicit_worker_count() {
        let mut cfg = SrxConfig::default();
        cfg.crypto.aead_worker_count = 3;
        let key = [0x11u8; 32];

        let pipe = cfg.build_aead_pipeline(&key).unwrap();
        assert_eq!(pipe.worker_count(), 3);
    }

    #[test]
    fn build_aead_pipeline_zero_workers_uses_default() {
        let mut cfg = SrxConfig::default();
        cfg.crypto.aead_worker_count = 0;
        let key = [0x22u8; 32];

        let pipe = cfg.build_aead_pipeline(&key).unwrap();
        assert_eq!(
            pipe.worker_count(),
            crate::crypto::DEFAULT_AEAD_WORKER_COUNT
        );
    }
}