qssh 0.0.2-alpha

Experimental quantum-safe SSH using post-quantum crypto. Research project - NOT for production. See LIMITATIONS.md
Documentation
//! Configuration constants for QSSH
//!
//! Centralizes all hardcoded values for easy configuration

use once_cell::sync::Lazy;
use std::env;

/// Network configuration
pub struct NetworkConfig {
    pub default_port: u16,
    pub default_qssh_port: u16,
    pub connect_timeout_secs: u64,
    pub read_timeout_secs: u64,
    pub keepalive_interval_secs: u64,
    pub max_packet_size: usize,
}

impl Default for NetworkConfig {
    fn default() -> Self {
        Self {
            default_port: env::var("QSSH_DEFAULT_PORT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(22),
            default_qssh_port: env::var("QSSH_PORT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(22222),
            connect_timeout_secs: env::var("QSSH_CONNECT_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(30),
            read_timeout_secs: env::var("QSSH_READ_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(120),
            keepalive_interval_secs: env::var("QSSH_KEEPALIVE_INTERVAL")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(60),
            max_packet_size: env::var("QSSH_MAX_PACKET_SIZE")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(65536),
        }
    }
}

/// Buffer sizes
pub struct BufferConfig {
    pub default_buffer_size: usize,
    pub small_buffer_size: usize,
    pub large_buffer_size: usize,
    pub channel_window_size: u32,
    pub channel_max_packet: u32,
}

impl Default for BufferConfig {
    fn default() -> Self {
        Self {
            default_buffer_size: env::var("QSSH_BUFFER_SIZE")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(8192),
            small_buffer_size: env::var("QSSH_SMALL_BUFFER")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(1024),
            large_buffer_size: env::var("QSSH_LARGE_BUFFER")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(65536),
            channel_window_size: env::var("QSSH_CHANNEL_WINDOW")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(1024 * 1024),
            channel_max_packet: env::var("QSSH_CHANNEL_MAX_PACKET")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(32768),
        }
    }
}

/// Security configuration
pub struct SecurityConfig {
    pub max_auth_attempts: u32,
    pub auth_timeout_secs: u64,
    pub key_rotation_interval_secs: u64,
    pub session_timeout_secs: u64,
    pub max_connections: usize,
    pub rate_limit_per_minute: u32,
}

impl Default for SecurityConfig {
    fn default() -> Self {
        Self {
            max_auth_attempts: env::var("QSSH_MAX_AUTH_ATTEMPTS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(3),
            auth_timeout_secs: env::var("QSSH_AUTH_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(60),
            key_rotation_interval_secs: env::var("QSSH_KEY_ROTATION")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(3600),
            session_timeout_secs: env::var("QSSH_SESSION_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(86400), // 24 hours
            max_connections: env::var("QSSH_MAX_CONNECTIONS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(100),
            rate_limit_per_minute: env::var("QSSH_RATE_LIMIT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(60),
        }
    }
}

/// Crypto configuration
pub struct CryptoConfig {
    pub default_algorithm: &'static str,
    pub kdf_iterations: u32,
    pub salt_length: usize,
    pub nonce_length: usize,
    pub tag_length: usize,
}

impl Default for CryptoConfig {
    fn default() -> Self {
        Self {
            default_algorithm: "falcon512",
            kdf_iterations: env::var("QSSH_KDF_ITERATIONS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(100_000),
            salt_length: 16,
            nonce_length: 12,
            tag_length: 16,
        }
    }
}

/// X11 configuration
pub struct X11Config {
    pub base_display_port: u16,
    pub max_displays: u32,
    pub default_display: u32,
    pub auth_timeout_secs: u64,
}

impl Default for X11Config {
    fn default() -> Self {
        Self {
            base_display_port: env::var("QSSH_X11_BASE_PORT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(6000),
            max_displays: env::var("QSSH_X11_MAX_DISPLAYS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(100),
            default_display: env::var("DISPLAY")
                .ok()
                .and_then(|s| {
                    s.split(':')
                        .nth(1)
                        .and_then(|part| part.split('.').next())
                        .and_then(|num| num.parse().ok())
                })
                .unwrap_or(0),
            auth_timeout_secs: 60,
        }
    }
}

/// Terminal configuration
pub struct TerminalConfig {
    pub default_term: &'static str,
    pub default_rows: u16,
    pub default_cols: u16,
    pub default_pixel_width: u16,
    pub default_pixel_height: u16,
}

impl Default for TerminalConfig {
    fn default() -> Self {
        Self {
            default_term: "xterm-256color",
            default_rows: env::var("LINES")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(24),
            default_cols: env::var("COLUMNS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(80),
            default_pixel_width: 640,
            default_pixel_height: 480,
        }
    }
}

/// File paths configuration
pub struct PathConfig {
    pub config_dir: String,
    pub known_hosts_file: String,
    pub authorized_keys_file: String,
    pub identity_file: String,
    pub temp_dir: String,
    pub log_dir: String,
}

impl Default for PathConfig {
    fn default() -> Self {
        let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
        let config_dir = env::var("QSSH_CONFIG_DIR")
            .unwrap_or_else(|_| format!("{}/.qssh", home));

        Self {
            config_dir: config_dir.clone(),
            known_hosts_file: env::var("QSSH_KNOWN_HOSTS")
                .unwrap_or_else(|_| format!("{}/known_hosts", config_dir)),
            authorized_keys_file: env::var("QSSH_AUTHORIZED_KEYS")
                .unwrap_or_else(|_| format!("{}/authorized_keys", config_dir)),
            identity_file: env::var("QSSH_IDENTITY")
                .unwrap_or_else(|_| format!("{}/id_qssh", config_dir)),
            temp_dir: env::var("TMPDIR")
                .or_else(|_| env::var("TEMP"))
                .or_else(|_| env::var("TMP"))
                .unwrap_or_else(|_| "/tmp".to_string()),
            log_dir: env::var("QSSH_LOG_DIR")
                .unwrap_or_else(|_| format!("{}/logs", config_dir)),
        }
    }
}

/// Global configuration instance
pub static NETWORK: Lazy<NetworkConfig> = Lazy::new(NetworkConfig::default);
pub static BUFFER: Lazy<BufferConfig> = Lazy::new(BufferConfig::default);
pub static SECURITY: Lazy<SecurityConfig> = Lazy::new(SecurityConfig::default);
pub static CRYPTO: Lazy<CryptoConfig> = Lazy::new(CryptoConfig::default);
pub static X11: Lazy<X11Config> = Lazy::new(X11Config::default);
pub static TERMINAL: Lazy<TerminalConfig> = Lazy::new(TerminalConfig::default);
pub static PATHS: Lazy<PathConfig> = Lazy::new(PathConfig::default);

/// Protocol constants (should not change)
pub mod protocol {
    pub const VERSION: &str = "QSSH-2.0";
    pub const MIN_PACKET_SIZE: usize = 16;
    pub const MAX_PACKET_SIZE: usize = 35000;
    pub const PADDING_MULTIPLE: usize = 8;
}

/// Message type codes
pub mod msg_codes {
    pub const DISCONNECT: u8 = 1;
    pub const IGNORE: u8 = 2;
    pub const UNIMPLEMENTED: u8 = 3;
    pub const DEBUG: u8 = 4;
    pub const SERVICE_REQUEST: u8 = 5;
    pub const SERVICE_ACCEPT: u8 = 6;

    pub const KEXINIT: u8 = 20;
    pub const NEWKEYS: u8 = 21;

    pub const USERAUTH_REQUEST: u8 = 50;
    pub const USERAUTH_FAILURE: u8 = 51;
    pub const USERAUTH_SUCCESS: u8 = 52;
    pub const USERAUTH_BANNER: u8 = 53;

    pub const CHANNEL_OPEN: u8 = 90;
    pub const CHANNEL_OPEN_CONFIRMATION: u8 = 91;
    pub const CHANNEL_OPEN_FAILURE: u8 = 92;
    pub const CHANNEL_WINDOW_ADJUST: u8 = 93;
    pub const CHANNEL_DATA: u8 = 94;
    pub const CHANNEL_EXTENDED_DATA: u8 = 95;
    pub const CHANNEL_EOF: u8 = 96;
    pub const CHANNEL_CLOSE: u8 = 97;
    pub const CHANNEL_REQUEST: u8 = 98;
    pub const CHANNEL_SUCCESS: u8 = 99;
    pub const CHANNEL_FAILURE: u8 = 100;
}

/// Get configuration value with fallback
pub fn get_config_value<T: std::str::FromStr>(key: &str, default: T) -> T {
    env::var(key)
        .ok()
        .and_then(|s| s.parse().ok())
        .unwrap_or(default)
}

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

    #[test]
    fn test_network_config() {
        assert_eq!(NETWORK.default_qssh_port, 22222);
        assert!(NETWORK.connect_timeout_secs > 0);
    }

    #[test]
    fn test_buffer_config() {
        assert!(BUFFER.default_buffer_size > 0);
        assert!(BUFFER.large_buffer_size > BUFFER.default_buffer_size);
    }

    #[test]
    fn test_security_config() {
        assert!(SECURITY.max_auth_attempts > 0);
        assert!(SECURITY.key_rotation_interval_secs > 0);
    }
}