pub mod basic_features;
pub mod context;
pub mod error;
pub mod pq_key_exchange;
pub mod recovery;
pub mod selector;
pub mod session_store;
pub mod tls13;
pub mod tracing;
#[cfg(any(feature = "formal-verification", feature = "kani", feature = "saw"))]
pub mod formal_verification;
pub use basic_features::{
create_client_connector, create_server_acceptor, get_config_info, load_certs, load_private_key,
tls_accept, tls_connect,
};
pub use context::{
DiagnosticInfo, ErrorChain, ErrorLink, SystemInfo, TlsContext as TlsDiagnosticContext,
};
pub use error::{ErrorCode, ErrorContext, ErrorSeverity, OperationPhase, RecoveryHint, TlsError};
pub use pq_key_exchange::{
KexInfo, PqKexMode, get_kex_info, get_kex_provider, is_custom_hybrid_available, is_pq_available,
};
pub use recovery::{
CircuitBreaker, DegradationConfig, FallbackStrategy, RetryPolicy, execute_with_circuit_breaker,
execute_with_fallback, retry_with_policy,
};
pub use selector::{
CLASSICAL_TLS_KEX, CLASSICAL_TLS_SCHEME, DEFAULT_PQ_TLS_KEX, DEFAULT_PQ_TLS_SCHEME,
DEFAULT_TLS_KEX, DEFAULT_TLS_SCHEME, HYBRID_TLS_512, HYBRID_TLS_768, HYBRID_TLS_1024,
PQ_TLS_512, PQ_TLS_768, PQ_TLS_1024, TlsConstraints, TlsContext, TlsPolicyEngine, TlsUseCase,
};
pub use session_store::{
ConfigurableSessionStore, PersistentSessionStore, create_resumption_config,
create_session_store,
};
pub use tls13::{
HandshakeState, HandshakeStats, Tls13Config, create_client_config, create_server_config,
get_cipher_suites, verify_config,
};
pub use tracing::{TlsMetrics, TlsSpan, TracingConfig, init_tracing};
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum TlsMode {
Classic,
#[default]
Hybrid,
Pq,
}
#[derive(Debug, Clone)]
pub struct ClientAuthConfig {
pub cert_path: String,
pub key_path: String,
}
impl ClientAuthConfig {
#[must_use]
pub fn new(cert_path: impl Into<String>, key_path: impl Into<String>) -> Self {
Self { cert_path: cert_path.into(), key_path: key_path.into() }
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ClientVerificationMode {
#[default]
None,
Optional,
Required,
}
#[derive(Debug, Clone)]
pub struct SessionPersistenceConfig {
pub path: std::path::PathBuf,
pub max_sessions: usize,
}
impl SessionPersistenceConfig {
#[must_use]
pub fn new(path: impl Into<std::path::PathBuf>, max_sessions: usize) -> Self {
Self { path: path.into(), max_sessions }
}
}
#[derive(Debug, Clone)]
pub struct TlsConfig {
pub mode: TlsMode,
pub enable_tracing: bool,
pub retry_policy: Option<RetryPolicy>,
pub enable_fallback: bool,
pub alpn_protocols: Vec<Vec<u8>>,
pub max_fragment_size: Option<usize>,
pub enable_early_data: bool,
pub max_early_data_size: u32,
pub enable_resumption: bool,
pub session_lifetime: u32,
pub enable_key_logging: bool,
pub cipher_suites: Option<Vec<rustls::SupportedCipherSuite>>,
pub min_protocol_version: Option<rustls::ProtocolVersion>,
pub max_protocol_version: Option<rustls::ProtocolVersion>,
pub client_auth: Option<ClientAuthConfig>,
pub client_verification: ClientVerificationMode,
pub client_ca_certs: Option<String>,
pub session_persistence: Option<SessionPersistenceConfig>,
}
impl Default for TlsConfig {
fn default() -> Self {
Self {
mode: TlsMode::default(),
enable_tracing: false,
retry_policy: None,
enable_fallback: true,
alpn_protocols: vec![],
max_fragment_size: None,
enable_early_data: false,
max_early_data_size: 0,
enable_resumption: true,
session_lifetime: 7200, enable_key_logging: false,
cipher_suites: None,
min_protocol_version: Some(rustls::ProtocolVersion::TLSv1_3),
max_protocol_version: Some(rustls::ProtocolVersion::TLSv1_3),
client_auth: None,
client_verification: ClientVerificationMode::default(),
client_ca_certs: None,
session_persistence: None,
}
}
}
impl TlsConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn use_case(mut self, use_case: TlsUseCase) -> Self {
self.mode = TlsPolicyEngine::recommend_mode(use_case);
self
}
#[must_use]
pub fn security_level(mut self, level: crate::unified_api::SecurityLevel) -> Self {
self.mode = TlsPolicyEngine::select_by_security_level(level);
self
}
#[must_use]
pub fn with_tracing(mut self) -> Self {
self.enable_tracing = true;
self
}
#[must_use]
pub fn with_retry_policy(mut self, policy: RetryPolicy) -> Self {
self.retry_policy = Some(policy);
self
}
#[must_use]
pub fn with_fallback(mut self, enable: bool) -> Self {
self.enable_fallback = enable;
self
}
#[must_use]
pub fn with_alpn_protocols(mut self, protocols: Vec<&'static str>) -> Self {
self.alpn_protocols = protocols.into_iter().map(|p| p.as_bytes().to_vec()).collect();
self
}
#[must_use]
pub fn with_max_fragment_size(mut self, size: usize) -> Self {
self.max_fragment_size = Some(size);
self
}
#[must_use]
pub fn with_early_data(mut self, max_size: u32) -> Self {
self.enable_early_data = true;
self.max_early_data_size = max_size;
self
}
#[must_use]
pub fn with_session_lifetime(mut self, seconds: u32) -> Self {
self.session_lifetime = seconds;
self
}
#[must_use]
pub fn with_resumption(mut self, enable: bool) -> Self {
self.enable_resumption = enable;
self
}
#[must_use]
pub fn with_key_logging(mut self) -> Self {
self.enable_key_logging = true;
self
}
#[must_use]
pub fn with_cipher_suites(mut self, suites: Vec<rustls::SupportedCipherSuite>) -> Self {
self.cipher_suites = Some(suites);
self
}
#[must_use]
pub fn with_min_protocol_version(mut self, version: rustls::ProtocolVersion) -> Self {
self.min_protocol_version = Some(version);
self
}
#[must_use]
pub fn with_max_protocol_version(mut self, version: rustls::ProtocolVersion) -> Self {
self.max_protocol_version = Some(version);
self
}
#[must_use]
pub fn with_client_auth(
mut self,
cert_path: impl Into<String>,
key_path: impl Into<String>,
) -> Self {
self.client_auth = Some(ClientAuthConfig::new(cert_path, key_path));
self
}
#[must_use]
pub fn with_client_verification(mut self, mode: ClientVerificationMode) -> Self {
self.client_verification = mode;
self
}
#[must_use]
pub fn with_client_ca_certs(mut self, ca_certs_path: impl Into<String>) -> Self {
self.client_ca_certs = Some(ca_certs_path.into());
self
}
#[must_use]
pub fn with_session_persistence(
mut self,
path: impl Into<std::path::PathBuf>,
max_sessions: usize,
) -> Self {
self.session_persistence = Some(SessionPersistenceConfig::new(path, max_sessions));
self
}
pub fn validate(&self) -> Result<(), TlsError> {
if let Some(min_version) = self.min_protocol_version {
let versions: Vec<&'static rustls::SupportedProtocolVersion> =
if let Some(max_version) = self.max_protocol_version {
rustls::ALL_VERSIONS
.iter()
.filter(|v| {
let v_num: u16 = v.version.into();
let min_num: u16 = min_version.into();
let max_num: u16 = max_version.into();
v_num >= min_num && v_num <= max_num
})
.copied()
.collect()
} else {
rustls::ALL_VERSIONS
.iter()
.filter(|v| {
let v_num: u16 = v.version.into();
let min_num: u16 = min_version.into();
v_num >= min_num
})
.copied()
.collect()
};
if versions.is_empty() {
return Err(TlsError::Config {
message: format!(
"Protocol version range ({:?} - {:?}) produces no valid versions. \
Supported versions are TLS 1.2 and TLS 1.3.",
min_version, self.max_protocol_version
),
field: Some("protocol_version".to_string()),
code: ErrorCode::InvalidProtocolVersion,
context: Box::new(ErrorContext {
code: ErrorCode::InvalidProtocolVersion,
phase: OperationPhase::Initialization,
..Default::default()
}),
recovery: Box::new(RecoveryHint::Reconfigure {
field: "min_protocol_version / max_protocol_version".to_string(),
suggestion: "Use TLSv1_2 or TLSv1_3 as version bounds".to_string(),
}),
});
}
}
Ok(())
}
}
impl From<&TlsConfig> for Tls13Config {
fn from(config: &TlsConfig) -> Self {
let TlsConfig {
ref mode,
enable_tracing,
retry_policy: _, enable_fallback: _, ref alpn_protocols,
max_fragment_size,
enable_early_data,
max_early_data_size,
enable_resumption,
session_lifetime: _, enable_key_logging,
ref cipher_suites,
min_protocol_version,
max_protocol_version,
client_auth: _, client_verification,
client_ca_certs: _, ref session_persistence,
} = *config;
let mut tls13_config = match mode {
TlsMode::Classic => Tls13Config::classic(),
TlsMode::Hybrid => Tls13Config::hybrid(),
TlsMode::Pq => Tls13Config::pq(),
};
if !alpn_protocols.is_empty() {
tls13_config.alpn_protocols = alpn_protocols.clone();
}
if let Some(size) = max_fragment_size {
tls13_config.max_fragment_size = Some(size);
}
if enable_early_data {
tls13_config.enable_early_data = true;
tls13_config.max_early_data_size = max_early_data_size;
}
if let Some(min_version) = min_protocol_version {
let versions: Vec<&'static rustls::SupportedProtocolVersion> =
if let Some(max_ver) = max_protocol_version {
rustls::ALL_VERSIONS
.iter()
.filter(|v| {
let v_num: u16 = v.version.into();
let min_num: u16 = min_version.into();
let max_num: u16 = max_ver.into();
v_num >= min_num && v_num <= max_num
})
.copied()
.collect()
} else {
rustls::ALL_VERSIONS
.iter()
.filter(|v| {
let v_num: u16 = v.version.into();
let min_num: u16 = min_version.into();
v_num >= min_num
})
.copied()
.collect()
};
if versions.is_empty() {
::tracing::warn!(
min_version = ?min_version,
max_version = ?max_protocol_version,
"Requested protocol version range produced empty list. \
Defaulting to TLS 1.3. Consider using TLS 1.2 or TLS 1.3."
);
tls13_config.protocol_versions = vec![&rustls::version::TLS13];
} else {
tls13_config.protocol_versions = versions;
}
}
if let Some(suites) = cipher_suites {
tls13_config.cipher_suites = Some(suites.clone());
}
if enable_key_logging {
tls13_config.key_log = Some(std::sync::Arc::new(rustls::KeyLogFile::new()));
}
tls13_config.client_verification = client_verification;
if !enable_resumption {
tls13_config.resumption = rustls::client::Resumption::disabled();
} else if session_persistence.is_some() {
tls13_config.resumption = create_resumption_config(session_persistence.as_ref());
}
if enable_tracing {
use std::sync::Once;
static TRACING_INIT: Once = Once::new();
TRACING_INIT.call_once(|| {
init_tracing(&TracingConfig::default());
});
}
tls13_config
}
}
#[must_use]
pub const fn pq_enabled() -> bool {
true
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_tls_mode_default_is_hybrid_succeeds() {
assert_eq!(TlsMode::default(), TlsMode::Hybrid);
}
#[test]
fn test_tls_config_new_sets_expected_fields_succeeds() {
let config = TlsConfig::new();
assert_eq!(config.mode, TlsMode::Hybrid);
}
#[test]
fn test_tls_config_with_use_case_webserver_sets_use_case_succeeds() {
let config = TlsConfig::new().use_case(TlsUseCase::WebServer);
assert_eq!(config.mode, TlsMode::Hybrid);
}
#[test]
fn test_tls_config_with_use_case_iot_sets_use_case_succeeds() {
let config = TlsConfig::new().use_case(TlsUseCase::IoT);
assert_eq!(config.mode, TlsMode::Classic);
}
#[test]
fn test_tls_config_with_use_case_government_sets_use_case_succeeds() {
let config = TlsConfig::new().use_case(TlsUseCase::Government);
assert_eq!(config.mode, TlsMode::Pq);
}
#[test]
fn test_tls_config_with_security_level_maximum_sets_security_level_succeeds() {
use crate::unified_api::SecurityLevel;
let config = TlsConfig::new().security_level(SecurityLevel::Maximum);
assert_eq!(config.mode, TlsMode::Hybrid);
}
#[test]
fn test_tls_config_with_security_level_quantum_sets_security_level_succeeds() {
use crate::unified_api::SecurityLevel;
let config = TlsConfig::new().security_level(SecurityLevel::Quantum);
assert_eq!(config.mode, TlsMode::Pq);
}
#[test]
fn test_tls_config_with_security_level_standard_sets_security_level_succeeds() {
use crate::unified_api::SecurityLevel;
let config = TlsConfig::new().security_level(SecurityLevel::Standard);
assert_eq!(config.mode, TlsMode::Hybrid);
}
#[test]
fn test_tls_config_with_tracing_sets_tracing_config_succeeds() {
let config = TlsConfig::new().with_tracing();
assert!(config.enable_tracing);
}
#[test]
fn test_tls_config_with_retry_sets_retry_count_succeeds() {
let policy = RetryPolicy::default();
let config = TlsConfig::new().with_retry_policy(policy);
assert!(config.retry_policy.is_some());
}
#[test]
fn test_tls_config_with_fallback_sets_fallback_flag_succeeds() {
let config = TlsConfig::new().with_fallback(false);
assert!(!config.enable_fallback);
}
#[test]
fn test_tls_config_builder_chain_sets_all_fields_succeeds() {
let config = TlsConfig::new()
.use_case(TlsUseCase::FinancialServices)
.with_tracing()
.with_fallback(true);
assert_eq!(config.mode, TlsMode::Hybrid);
assert!(config.enable_tracing);
assert!(config.enable_fallback);
}
#[test]
fn test_tls13_config_from_tls_config_succeeds() {
let tls_config = TlsConfig::new();
let tls13_config = Tls13Config::from(&tls_config);
assert_eq!(tls13_config.mode, TlsMode::Hybrid);
}
#[test]
fn test_version_is_nonempty_succeeds() {
assert!(!crate::unified_api::VERSION.is_empty());
}
#[test]
fn test_pq_enabled_returns_bool_succeeds() {
assert!(pq_enabled());
}
#[test]
fn test_tls_config_with_alpn_protocols_sets_alpn_list_succeeds() {
let config = TlsConfig::new().with_alpn_protocols(vec!["h2", "http/1.1"]);
assert_eq!(config.alpn_protocols.len(), 2);
assert_eq!(config.alpn_protocols[0], b"h2");
assert_eq!(config.alpn_protocols[1], b"http/1.1");
}
#[test]
fn test_tls_config_with_max_fragment_size_sets_fragment_size_has_correct_size() {
let config = TlsConfig::new().with_max_fragment_size(4096);
assert_eq!(config.max_fragment_size, Some(4096));
}
#[test]
fn test_tls_config_with_early_data_sets_early_data_flag_succeeds() {
let config = TlsConfig::new().with_early_data(16384);
assert!(config.enable_early_data);
assert_eq!(config.max_early_data_size, 16384);
}
#[test]
fn test_tls_config_with_session_lifetime_sets_lifetime_succeeds() {
let config = TlsConfig::new().with_session_lifetime(3600);
assert_eq!(config.session_lifetime, 3600);
}
#[test]
fn test_tls_config_with_resumption_sets_resumption_flag_succeeds() {
let config = TlsConfig::new().with_resumption(false);
assert!(!config.enable_resumption);
}
#[test]
fn test_tls_config_with_key_logging_sets_key_log_flag_succeeds() {
let config = TlsConfig::new().with_key_logging();
assert!(config.enable_key_logging);
}
#[test]
fn test_tls_config_with_cipher_suites_sets_suite_list_succeeds() {
let suites = vec![rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384];
let config = TlsConfig::new().with_cipher_suites(suites);
assert!(config.cipher_suites.is_some());
}
#[test]
fn test_tls_config_with_min_protocol_version_sets_min_version_succeeds() {
let config = TlsConfig::new().with_min_protocol_version(rustls::ProtocolVersion::TLSv1_2);
assert_eq!(config.min_protocol_version, Some(rustls::ProtocolVersion::TLSv1_2));
}
#[test]
fn test_tls_config_with_max_protocol_version_sets_max_version_succeeds() {
let config = TlsConfig::new().with_max_protocol_version(rustls::ProtocolVersion::TLSv1_3);
assert_eq!(config.max_protocol_version, Some(rustls::ProtocolVersion::TLSv1_3));
}
#[test]
fn test_tls_config_with_client_auth_sets_client_auth_config_succeeds() {
let config = TlsConfig::new().with_client_auth("client.crt", "client.key");
assert!(config.client_auth.is_some());
let auth = config.client_auth.as_ref().expect("should have client auth");
assert_eq!(auth.cert_path, "client.crt");
assert_eq!(auth.key_path, "client.key");
}
#[test]
fn test_tls_config_with_client_verification_sets_verification_mode_succeeds() {
let config = TlsConfig::new().with_client_verification(ClientVerificationMode::Required);
assert_eq!(config.client_verification, ClientVerificationMode::Required);
}
#[test]
fn test_tls_config_with_client_ca_certs_sets_ca_certs_succeeds() {
let config = TlsConfig::new().with_client_ca_certs("ca-bundle.crt");
assert_eq!(config.client_ca_certs.as_deref(), Some("ca-bundle.crt"));
}
#[test]
fn test_tls_config_with_session_persistence_sets_persistence_config_succeeds() {
let config = TlsConfig::new()
.with_session_persistence(std::env::temp_dir().join("sessions.bin"), 500);
assert!(config.session_persistence.is_some());
let sp = config.session_persistence.as_ref().expect("should have persistence");
assert_eq!(sp.max_sessions, 500);
}
#[test]
fn test_tls_config_validate_default_succeeds() {
let config = TlsConfig::new();
assert!(config.validate().is_ok());
}
#[test]
fn test_tls_config_validate_valid_range_succeeds() {
let config = TlsConfig::new()
.with_min_protocol_version(rustls::ProtocolVersion::TLSv1_2)
.with_max_protocol_version(rustls::ProtocolVersion::TLSv1_3);
assert!(config.validate().is_ok());
}
#[test]
fn test_tls_config_validate_min_only_succeeds() {
let mut config = TlsConfig::new();
config.min_protocol_version = Some(rustls::ProtocolVersion::TLSv1_2);
config.max_protocol_version = None;
assert!(config.validate().is_ok());
}
#[test]
fn test_tls_config_validate_no_min_fails() {
let mut config = TlsConfig::new();
config.min_protocol_version = None;
assert!(config.validate().is_ok());
}
#[test]
fn test_client_auth_config_new_stores_fields_succeeds() {
let auth = ClientAuthConfig::new("cert.pem", "key.pem");
assert_eq!(auth.cert_path, "cert.pem");
assert_eq!(auth.key_path, "key.pem");
}
#[test]
fn test_client_verification_mode_default_is_none_succeeds() {
assert_eq!(ClientVerificationMode::default(), ClientVerificationMode::None);
}
#[test]
fn test_client_verification_mode_variants_are_distinct_are_unique() {
assert_ne!(ClientVerificationMode::None, ClientVerificationMode::Optional);
assert_ne!(ClientVerificationMode::Optional, ClientVerificationMode::Required);
}
#[test]
fn test_session_persistence_config_new_stores_fields_succeeds() {
let sp = SessionPersistenceConfig::new(std::env::temp_dir().join("sess.bin"), 100);
assert_eq!(sp.path, std::env::temp_dir().join("sess.bin"));
assert_eq!(sp.max_sessions, 100);
}
#[test]
fn test_tls13_config_from_classic_tls_config_sets_non_pq_mode_succeeds() {
let tls_config = TlsConfig { mode: TlsMode::Classic, ..TlsConfig::default() };
let tls13 = Tls13Config::from(&tls_config);
assert_eq!(tls13.mode, TlsMode::Classic);
}
#[test]
fn test_tls13_config_from_pq_tls_config_sets_pq_mode_succeeds() {
let tls_config = TlsConfig { mode: TlsMode::Pq, ..TlsConfig::default() };
let tls13 = Tls13Config::from(&tls_config);
assert_eq!(tls13.mode, TlsMode::Pq);
}
#[test]
fn test_tls13_config_from_tls_config_alpn_sets_alpn_protocols_succeeds() {
let tls_config = TlsConfig::new().with_alpn_protocols(vec!["h2"]);
let tls13 = Tls13Config::from(&tls_config);
assert_eq!(tls13.alpn_protocols.len(), 1);
assert_eq!(tls13.alpn_protocols[0], b"h2");
}
#[test]
fn test_tls13_config_from_tls_config_fragment_size_sets_fragment_size_has_correct_size() {
let tls_config = TlsConfig::new().with_max_fragment_size(8192);
let tls13 = Tls13Config::from(&tls_config);
assert_eq!(tls13.max_fragment_size, Some(8192));
}
#[test]
fn test_tls13_config_from_tls_config_early_data_sets_early_data_succeeds() {
let tls_config = TlsConfig::new().with_early_data(32768);
let tls13 = Tls13Config::from(&tls_config);
assert!(tls13.enable_early_data);
assert_eq!(tls13.max_early_data_size, 32768);
}
#[test]
fn test_tls13_config_from_tls_config_cipher_suites_sets_suite_list_succeeds() {
let suites = vec![rustls::crypto::aws_lc_rs::cipher_suite::TLS13_AES_256_GCM_SHA384];
let tls_config = TlsConfig::new().with_cipher_suites(suites);
let tls13 = Tls13Config::from(&tls_config);
assert!(tls13.cipher_suites.is_some());
}
#[test]
fn test_tls13_config_from_tls_config_key_logging_sets_key_log_flag_succeeds() {
let tls_config = TlsConfig::new().with_key_logging();
let tls13 = Tls13Config::from(&tls_config);
assert!(tls13.key_log.is_some());
}
#[test]
fn test_tls13_config_from_tls_config_client_verification_sets_verification_mode_succeeds() {
let tls_config =
TlsConfig::new().with_client_verification(ClientVerificationMode::Required);
let tls13 = Tls13Config::from(&tls_config);
assert_eq!(tls13.client_verification, ClientVerificationMode::Required);
}
#[test]
fn test_tls13_config_from_tls_config_protocol_versions_sets_version_range_succeeds() {
let tls_config = TlsConfig::new()
.with_min_protocol_version(rustls::ProtocolVersion::TLSv1_3)
.with_max_protocol_version(rustls::ProtocolVersion::TLSv1_3);
let tls13 = Tls13Config::from(&tls_config);
assert!(!tls13.protocol_versions.is_empty());
}
#[test]
fn test_tls13_config_from_tls_config_protocol_versions_min_only_sets_min_version_succeeds() {
let mut tls_config = TlsConfig::new();
tls_config.min_protocol_version = Some(rustls::ProtocolVersion::TLSv1_3);
tls_config.max_protocol_version = None;
let tls13 = Tls13Config::from(&tls_config);
assert!(!tls13.protocol_versions.is_empty());
}
#[test]
fn test_tls_mode_clone_copy_eq_debug_work_correctly_succeeds() {
let mode = TlsMode::Hybrid;
let cloned = mode;
let copied = mode;
assert_eq!(mode, cloned);
assert_eq!(mode, copied);
let debug = format!("{:?}", mode);
assert!(debug.contains("Hybrid"));
}
#[test]
fn test_tls_mode_all_variants_are_distinct_are_unique() {
assert_ne!(TlsMode::Classic, TlsMode::Hybrid);
assert_ne!(TlsMode::Hybrid, TlsMode::Pq);
assert_ne!(TlsMode::Classic, TlsMode::Pq);
}
#[test]
fn test_tls_config_full_builder_chain_sets_all_fields_succeeds() {
let config = TlsConfig::new()
.use_case(TlsUseCase::FinancialServices)
.with_tracing()
.with_fallback(true)
.with_alpn_protocols(vec!["h2"])
.with_max_fragment_size(4096)
.with_session_lifetime(1800)
.with_resumption(true)
.with_min_protocol_version(rustls::ProtocolVersion::TLSv1_3)
.with_max_protocol_version(rustls::ProtocolVersion::TLSv1_3)
.with_client_verification(ClientVerificationMode::Optional)
.with_client_ca_certs("ca.crt");
assert_eq!(config.mode, TlsMode::Hybrid);
assert!(config.enable_tracing);
assert!(config.enable_fallback);
assert_eq!(config.alpn_protocols.len(), 1);
assert_eq!(config.max_fragment_size, Some(4096));
assert_eq!(config.session_lifetime, 1800);
assert!(config.enable_resumption);
assert_eq!(config.client_verification, ClientVerificationMode::Optional);
assert_eq!(config.client_ca_certs.as_deref(), Some("ca.crt"));
}
#[test]
fn test_tls_config_default_values_are_expected_succeeds() {
let config = TlsConfig::default();
assert_eq!(config.mode, TlsMode::Hybrid);
assert!(!config.enable_tracing);
assert!(config.retry_policy.is_none());
assert!(config.enable_fallback);
assert!(config.alpn_protocols.is_empty());
assert!(config.max_fragment_size.is_none());
assert!(!config.enable_early_data);
assert_eq!(config.max_early_data_size, 0);
assert!(config.enable_resumption);
assert_eq!(config.session_lifetime, 7200);
assert!(!config.enable_key_logging);
assert!(config.cipher_suites.is_none());
assert_eq!(config.min_protocol_version, Some(rustls::ProtocolVersion::TLSv1_3));
assert_eq!(config.max_protocol_version, Some(rustls::ProtocolVersion::TLSv1_3));
assert!(config.client_auth.is_none());
assert_eq!(config.client_verification, ClientVerificationMode::None);
assert!(config.client_ca_certs.is_none());
assert!(config.session_persistence.is_none());
}
#[test]
fn test_enable_resumption_false_disables_sessions_succeeds() {
let tls_config = TlsConfig::new().with_resumption(false);
let tls13 = Tls13Config::from(&tls_config);
let default_tls13 = Tls13Config::from(&TlsConfig::new());
let _ = tls13;
let _ = default_tls13;
}
#[test]
fn test_enable_resumption_true_keeps_default_succeeds() {
let tls_config = TlsConfig::new().with_resumption(true);
let _tls13 = Tls13Config::from(&tls_config);
}
#[test]
fn test_session_persistence_wired_to_tls13_succeeds() {
let tls_config = TlsConfig::new()
.with_session_persistence(std::env::temp_dir().join("test_sessions.bin"), 256);
let _tls13 = Tls13Config::from(&tls_config);
}
#[test]
fn test_enable_early_data_influences_tls13_config_succeeds() {
let config_a = TlsConfig::new(); let tls13_a = Tls13Config::from(&config_a);
let config_b = TlsConfig::new().with_early_data(16384);
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(
tls13_a.enable_early_data, tls13_b.enable_early_data,
"enable_early_data must influence Tls13Config"
);
}
#[test]
fn test_max_early_data_size_influences_tls13_config_has_correct_size() {
let config_a = TlsConfig::new().with_early_data(1024);
let tls13_a = Tls13Config::from(&config_a);
let config_b = TlsConfig::new().with_early_data(16384);
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(
tls13_a.max_early_data_size, tls13_b.max_early_data_size,
"max_early_data_size must influence Tls13Config"
);
}
#[test]
fn test_max_fragment_size_influences_tls13_config_has_correct_size() {
let config_a = TlsConfig::new(); let tls13_a = Tls13Config::from(&config_a);
let config_b = TlsConfig::new().with_max_fragment_size(1024);
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(
tls13_a.max_fragment_size, tls13_b.max_fragment_size,
"max_fragment_size must influence Tls13Config"
);
}
#[test]
fn test_alpn_protocols_influences_tls13_config_succeeds() {
let config_a = TlsConfig::new(); let tls13_a = Tls13Config::from(&config_a);
let config_b = TlsConfig::new().with_alpn_protocols(vec!["h2"]);
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(
tls13_a.alpn_protocols, tls13_b.alpn_protocols,
"alpn_protocols must influence Tls13Config"
);
}
#[test]
fn test_enable_key_logging_influences_tls13_config_succeeds() {
let config_a = TlsConfig::new(); let tls13_a = Tls13Config::from(&config_a);
let config_b = TlsConfig::new().with_key_logging();
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(
tls13_a.key_log.is_some(),
tls13_b.key_log.is_some(),
"enable_key_logging must influence Tls13Config key_log"
);
}
#[test]
fn test_client_verification_influences_tls13_config_succeeds() {
let config_a = TlsConfig::new(); let tls13_a = Tls13Config::from(&config_a);
let config_b = TlsConfig::new().with_client_verification(ClientVerificationMode::Required);
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(
tls13_a.client_verification, tls13_b.client_verification,
"client_verification must influence Tls13Config"
);
}
#[test]
fn test_mode_influences_tls13_config_succeeds() {
let mut config_a = TlsConfig::new();
config_a.mode = TlsMode::Hybrid;
let tls13_a = Tls13Config::from(&config_a);
let mut config_b = TlsConfig::new();
config_b.mode = TlsMode::Pq;
let tls13_b = Tls13Config::from(&config_b);
assert_ne!(tls13_a.mode, tls13_b.mode, "mode must influence Tls13Config");
}
}