use std::sync::Arc;
use rustls::crypto::CryptoProvider;
use crate::crypto::pqc::types::PqcError;
#[derive(Debug, Clone)]
pub struct RustlsPqcConfig {
pub enable_ml_kem: bool,
pub enable_ml_dsa: bool,
pub prefer_pqc: bool,
pub allow_downgrade: bool,
}
impl Default for RustlsPqcConfig {
fn default() -> Self {
Self {
enable_ml_kem: true,
enable_ml_dsa: true,
prefer_pqc: true,
allow_downgrade: false,
}
}
}
pub struct PqcCryptoProvider {
#[allow(dead_code)]
base_provider: Arc<CryptoProvider>,
#[allow(dead_code)]
config: RustlsPqcConfig,
#[allow(dead_code)]
cipher_suites: Vec<rustls::CipherSuite>,
}
impl PqcCryptoProvider {
fn normalize_config(mut config: RustlsPqcConfig) -> RustlsPqcConfig {
config.enable_ml_kem = true;
config.enable_ml_dsa = true;
config.prefer_pqc = true;
config.allow_downgrade = false;
config
}
pub fn new() -> Result<Self, PqcError> {
Self::with_config(Some(RustlsPqcConfig::default()))
}
pub fn with_config(config: Option<RustlsPqcConfig>) -> Result<Self, PqcError> {
let config = config
.ok_or_else(|| PqcError::CryptoError("PQC config is required".to_string()))
.map(Self::normalize_config)?;
validate_config(&config)?;
let base_provider = crate::crypto::rustls::configured_provider();
let cipher_suites = create_hybrid_cipher_suites(&base_provider)?;
Ok(Self {
base_provider,
config,
cipher_suites,
})
}
pub fn cipher_suites(&self) -> Vec<rustls::CipherSuite> {
vec![
rustls::CipherSuite::TLS13_AES_128_GCM_SHA256,
rustls::CipherSuite::TLS13_AES_256_GCM_SHA384,
rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
]
}
pub fn validate_cipher_suites(suites: &[rustls::CipherSuite]) -> Result<(), PqcError> {
if suites.is_empty() {
return Err(PqcError::CryptoError(
"No cipher suites provided".to_string(),
));
}
Ok(())
}
}
pub fn validate_config(config: &RustlsPqcConfig) -> Result<(), PqcError> {
if !config.enable_ml_kem || !config.enable_ml_dsa {
return Err(PqcError::CryptoError(
"Pure PQC requires ML-KEM and ML-DSA to be enabled".to_string(),
));
}
if config.allow_downgrade {
return Err(PqcError::CryptoError(
"PQC downgrade is not supported in symmetric P2P mode".to_string(),
));
}
Ok(())
}
fn create_hybrid_cipher_suites(
_base_provider: &Arc<CryptoProvider>,
) -> Result<Vec<rustls::CipherSuite>, PqcError> {
Ok(vec![
rustls::CipherSuite::TLS13_AES_128_GCM_SHA256,
rustls::CipherSuite::TLS13_AES_256_GCM_SHA384,
rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
])
}
pub trait PqcConfigExt {
fn has_pqc_support(&self) -> bool;
fn crypto_config(&self) -> CryptoInfo;
}
pub struct CryptoInfo {
has_pqc: bool,
pqc_kex: bool,
#[allow(dead_code)]
pqc_sig: bool,
}
impl CryptoInfo {
pub fn has_pqc_support(&self) -> bool {
self.has_pqc
}
pub fn used_pqc_kex(&self) -> bool {
self.pqc_kex
}
pub fn used_classical_kex(&self) -> bool {
!self.pqc_kex
}
}
pub fn with_pqc_support(config: crate::ClientConfig) -> Result<crate::ClientConfig, PqcError> {
Ok(config)
}
pub fn with_pqc_support_server(
config: crate::ServerConfig,
) -> Result<crate::ServerConfig, PqcError> {
Ok(config)
}
impl PqcConfigExt for crate::ClientConfig {
fn has_pqc_support(&self) -> bool {
true }
fn crypto_config(&self) -> CryptoInfo {
CryptoInfo {
has_pqc: true,
pqc_kex: true, pqc_sig: true,
}
}
}
impl PqcConfigExt for crate::ServerConfig {
fn has_pqc_support(&self) -> bool {
true
}
fn crypto_config(&self) -> CryptoInfo {
CryptoInfo {
has_pqc: true,
pqc_kex: true, pqc_sig: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pqc_config_default() {
let config = RustlsPqcConfig::default();
assert!(config.enable_ml_kem);
assert!(config.enable_ml_dsa);
assert!(config.prefer_pqc);
assert!(!config.allow_downgrade);
}
#[test]
fn test_config_validation() {
let valid = RustlsPqcConfig::default();
assert!(validate_config(&valid).is_ok());
let invalid = RustlsPqcConfig {
enable_ml_kem: false,
enable_ml_dsa: false,
prefer_pqc: false,
allow_downgrade: false,
};
assert!(validate_config(&invalid).is_err());
let invalid = RustlsPqcConfig {
enable_ml_kem: true,
enable_ml_dsa: true,
prefer_pqc: true,
allow_downgrade: true,
};
assert!(validate_config(&invalid).is_err());
}
#[test]
fn test_provider_creation() {
let provider = PqcCryptoProvider::new();
assert!(provider.is_ok());
let provider = provider.unwrap();
assert_eq!(provider.cipher_suites().len(), 3);
assert!(
provider
.cipher_suites()
.contains(&rustls::CipherSuite::TLS13_AES_128_GCM_SHA256)
);
}
}