use crate::types::{
CryptoContext, PerformancePreference, SecurityLevel, UseCase,
config::CoreConfig,
error::{Result, TypeError},
traits::{DataCharacteristics, PatternType, SchemeSelector},
};
use crate::unified_api::crypto_types::{DecryptKey, EncryptKey, EncryptionScheme};
pub const CLASSICAL_FALLBACK_SIZE_THRESHOLD: usize = 4096;
pub struct CryptoPolicyEngine;
impl Default for CryptoPolicyEngine {
fn default() -> Self {
Self::new()
}
}
impl CryptoPolicyEngine {
#[must_use]
pub fn new() -> Self {
Self
}
#[must_use = "scheme recommendation should be used for algorithm selection"]
pub fn recommend_scheme(use_case: &UseCase, _config: &CoreConfig) -> Result<String> {
match *use_case {
UseCase::SecureMessaging => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
UseCase::EmailEncryption => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::VpnTunnel => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
UseCase::ApiSecurity => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
UseCase::FileStorage => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::DatabaseEncryption => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
UseCase::CloudStorage => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::BackupArchive => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::ConfigSecrets => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
UseCase::Authentication => Ok("hybrid-ml-dsa-87-ed25519".to_string()),
UseCase::SessionToken => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
UseCase::DigitalCertificate => Ok("hybrid-ml-dsa-87-ed25519".to_string()),
UseCase::KeyExchange => Ok("hybrid-ml-kem-1024-x25519".to_string()),
UseCase::FinancialTransactions => Ok("hybrid-ml-dsa-65-ed25519".to_string()),
UseCase::LegalDocuments => Ok("hybrid-ml-dsa-87-ed25519".to_string()),
UseCase::BlockchainTransaction => Ok("hybrid-ml-dsa-65-ed25519".to_string()),
UseCase::HealthcareRecords => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::GovernmentClassified => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::PaymentCard => Ok("hybrid-ml-kem-1024-aes-256-gcm".to_string()),
UseCase::IoTDevice => Ok("hybrid-ml-kem-512-aes-256-gcm".to_string()),
UseCase::FirmwareSigning => Ok("hybrid-ml-dsa-65-ed25519".to_string()),
UseCase::AuditLog => Ok("hybrid-ml-kem-768-aes-256-gcm".to_string()),
}
}
#[must_use]
pub fn force_scheme(scheme: &crate::types::CryptoScheme) -> String {
match *scheme {
crate::types::CryptoScheme::Hybrid => DEFAULT_ENCRYPTION_SCHEME.to_string(),
crate::types::CryptoScheme::Symmetric => "aes-256-gcm".to_string(),
crate::types::CryptoScheme::SymmetricChaCha20 => "chacha20-poly1305".to_string(),
crate::types::CryptoScheme::Asymmetric => "pq-ml-dsa-65".to_string(),
crate::types::CryptoScheme::PostQuantum => PQ_ENCRYPTION_768.to_string(),
}
}
#[must_use = "scheme selection should be used for algorithm configuration"]
pub fn select_pq_encryption_scheme(config: &CoreConfig) -> Result<String> {
match config.security_level {
SecurityLevel::Standard => Ok(PQ_ENCRYPTION_512.to_string()),
SecurityLevel::High => Ok(PQ_ENCRYPTION_768.to_string()),
SecurityLevel::Maximum => Ok(PQ_ENCRYPTION_1024.to_string()),
}
}
#[must_use = "scheme selection should be used for algorithm configuration"]
pub fn select_pq_signature_scheme(config: &CoreConfig) -> Result<String> {
match config.security_level {
SecurityLevel::Standard => Ok(PQ_SIGNATURE_44.to_string()),
SecurityLevel::High => Ok(PQ_SIGNATURE_65.to_string()),
SecurityLevel::Maximum => Ok(PQ_SIGNATURE_87.to_string()),
}
}
#[must_use]
pub fn analyze_data_characteristics(data: &[u8]) -> DataCharacteristics {
let size = data.len();
let entropy = calculate_entropy(data);
let pattern_type = detect_pattern_type(data);
DataCharacteristics { size, entropy, pattern_type }
}
pub fn select_encryption_scheme(
data: &[u8],
config: &CoreConfig,
use_case: Option<&UseCase>,
) -> Result<String> {
if let Some(use_case) = use_case {
return Self::recommend_scheme(use_case, config);
}
if !config.hardware_acceleration {
return Ok(CHACHA20_POLY1305.to_string());
}
if !data.is_empty() {
let characteristics = Self::analyze_data_characteristics(data);
match (&config.performance_preference, &characteristics.pattern_type) {
(PerformancePreference::Speed, PatternType::Random) => {
if matches!(config.security_level, SecurityLevel::High) {
return Ok(HYBRID_ENCRYPTION_512.to_string());
}
}
(PerformancePreference::Memory, _)
if data.len() < CLASSICAL_FALLBACK_SIZE_THRESHOLD =>
{
if matches!(config.security_level, SecurityLevel::High) {
return Ok(HYBRID_ENCRYPTION_512.to_string());
}
}
_ => {}
}
}
Ok(Self::select_for_security_level(config))
}
fn select_for_security_level(config: &CoreConfig) -> String {
match &config.security_level {
SecurityLevel::Maximum => HYBRID_ENCRYPTION_1024.to_string(),
SecurityLevel::High => HYBRID_ENCRYPTION_768.to_string(),
SecurityLevel::Standard => HYBRID_ENCRYPTION_512.to_string(),
}
}
#[must_use = "scheme selection should be used for algorithm configuration"]
pub fn select_signature_scheme(config: &CoreConfig) -> Result<String> {
match &config.security_level {
SecurityLevel::Maximum => Ok(HYBRID_SIGNATURE_87.to_string()),
SecurityLevel::High => Ok(HYBRID_SIGNATURE_65.to_string()),
SecurityLevel::Standard => Ok(HYBRID_SIGNATURE_44.to_string()),
}
}
pub fn adaptive_selection(
data: &[u8],
performance_metrics: &PerformanceMetrics,
config: &CoreConfig,
) -> Result<String> {
let characteristics = Self::analyze_data_characteristics(data);
let base_scheme = Self::select_encryption_scheme(data, config, None)?;
match (&config.performance_preference, performance_metrics) {
(PerformancePreference::Memory, metrics) if metrics.memory_usage_mb > 500.0 => {
Ok("hybrid-ml-kem-768-aes-256-gcm".to_string())
}
(PerformancePreference::Speed, metrics)
if metrics.encryption_speed_ms > 1000.0
&& matches!(characteristics.pattern_type, PatternType::Repetitive) =>
{
Ok("hybrid-ml-kem-512-aes-256-gcm".to_string())
}
_ => Ok(base_scheme),
}
}
#[must_use]
pub fn default_scheme() -> &'static str {
DEFAULT_ENCRYPTION_SCHEME
}
pub fn recommend_encryption_scheme(
use_case: &UseCase,
_config: &CoreConfig,
) -> Result<EncryptionScheme> {
match *use_case {
UseCase::SecureMessaging | UseCase::VpnTunnel | UseCase::ApiSecurity => {
Ok(EncryptionScheme::HybridMlKem768Aes256Gcm)
}
UseCase::EmailEncryption => Ok(EncryptionScheme::HybridMlKem1024Aes256Gcm),
UseCase::FileStorage | UseCase::CloudStorage | UseCase::BackupArchive => {
Ok(EncryptionScheme::HybridMlKem1024Aes256Gcm)
}
UseCase::DatabaseEncryption
| UseCase::ConfigSecrets
| UseCase::SessionToken
| UseCase::AuditLog => Ok(EncryptionScheme::HybridMlKem768Aes256Gcm),
UseCase::HealthcareRecords | UseCase::GovernmentClassified | UseCase::PaymentCard => {
Ok(EncryptionScheme::HybridMlKem1024Aes256Gcm)
}
UseCase::KeyExchange => Ok(EncryptionScheme::HybridMlKem1024Aes256Gcm),
UseCase::IoTDevice => Ok(EncryptionScheme::HybridMlKem512Aes256Gcm),
UseCase::Authentication
| UseCase::DigitalCertificate
| UseCase::FinancialTransactions
| UseCase::LegalDocuments
| UseCase::BlockchainTransaction
| UseCase::FirmwareSigning => Ok(EncryptionScheme::HybridMlKem768Aes256Gcm),
}
}
#[must_use]
pub fn select_encryption_scheme_typed(config: &CoreConfig) -> EncryptionScheme {
Self::select_encryption_scheme_typed_with_mode(
config,
crate::types::types::CryptoMode::Hybrid,
)
}
#[must_use]
pub fn select_encryption_scheme_typed_with_mode(
config: &CoreConfig,
mode: crate::types::types::CryptoMode,
) -> EncryptionScheme {
use crate::types::types::CryptoMode;
match mode {
CryptoMode::PqOnly => match &config.security_level {
SecurityLevel::Maximum => EncryptionScheme::PqMlKem1024Aes256Gcm,
SecurityLevel::High => EncryptionScheme::PqMlKem768Aes256Gcm,
SecurityLevel::Standard => EncryptionScheme::PqMlKem512Aes256Gcm,
},
CryptoMode::Hybrid => match &config.security_level {
SecurityLevel::Maximum => EncryptionScheme::HybridMlKem1024Aes256Gcm,
SecurityLevel::High => EncryptionScheme::HybridMlKem768Aes256Gcm,
SecurityLevel::Standard => EncryptionScheme::HybridMlKem512Aes256Gcm,
},
}
}
pub fn validate_key_matches_scheme(
key: &EncryptKey<'_>,
scheme: &EncryptionScheme,
) -> Result<()> {
match (key, scheme) {
(EncryptKey::Symmetric(_), s) if s.requires_symmetric_key() => Ok(()),
(EncryptKey::Hybrid(pk), s) if s.requires_hybrid_key() => {
if let Some(expected_level) = s.ml_kem_level()
&& pk.security_level() != expected_level
{
return Err(TypeError::ConfigurationError(format!(
"Scheme '{}' requires {} key, but the provided key \
was generated at {} level. Use \
generate_hybrid_keypair_with_level({:?}).",
scheme,
expected_level.name(),
pk.security_level().name(),
expected_level
)));
}
Ok(())
}
(EncryptKey::PqOnly(pk), s) if s.requires_pq_key() => {
if let Some(expected_level) = s.ml_kem_level()
&& pk.security_level() != expected_level
{
return Err(TypeError::ConfigurationError(format!(
"Scheme '{}' requires {} key, but the provided PQ-only key \
was generated at {} level. Use \
generate_pq_keypair_with_level({:?}).",
scheme,
expected_level.name(),
pk.security_level().name(),
expected_level
)));
}
Ok(())
}
(EncryptKey::Symmetric(_), _) => Err(TypeError::ConfigurationError(format!(
"Scheme '{}' requires a hybrid or PQ-only key, \
but a symmetric key was provided.",
scheme
))),
(EncryptKey::Hybrid(_), _) => Err(TypeError::ConfigurationError(format!(
"Scheme '{}' does not accept a hybrid key. Expected: {}.",
scheme,
if scheme.requires_pq_key() {
"EncryptKey::PqOnly"
} else {
"EncryptKey::Symmetric"
}
))),
(EncryptKey::PqOnly(_), _) => Err(TypeError::ConfigurationError(format!(
"Scheme '{}' does not accept a PQ-only key. Expected: {}.",
scheme,
if scheme.requires_hybrid_key() {
"EncryptKey::Hybrid"
} else {
"EncryptKey::Symmetric"
}
))),
}
}
pub fn validate_decrypt_key_matches_scheme(
key: &DecryptKey<'_>,
scheme: &EncryptionScheme,
) -> Result<()> {
match (key, scheme) {
(DecryptKey::Symmetric(_), s) if s.requires_symmetric_key() => Ok(()),
(DecryptKey::Hybrid(sk), s) if s.requires_hybrid_key() => {
if let Some(expected_level) = s.ml_kem_level()
&& sk.security_level() != expected_level
{
return Err(TypeError::ConfigurationError(format!(
"Scheme '{}' requires {} key, but the provided key \
was generated at {} level.",
scheme,
expected_level.name(),
sk.security_level().name(),
)));
}
Ok(())
}
(DecryptKey::PqOnly(sk), s) if s.requires_pq_key() => {
if let Some(expected_level) = s.ml_kem_level()
&& sk.security_level() != expected_level
{
return Err(TypeError::ConfigurationError(format!(
"Scheme '{}' requires {} key, but the provided PQ-only key \
was generated at {} level.",
scheme,
expected_level.name(),
sk.security_level().name(),
)));
}
Ok(())
}
(DecryptKey::Symmetric(_), _) => Err(TypeError::ConfigurationError(format!(
"Scheme '{}' requires a hybrid or PQ-only secret key, \
but a symmetric key was provided.",
scheme
))),
(DecryptKey::Hybrid(_), _) => Err(TypeError::ConfigurationError(format!(
"Scheme '{}' does not accept a hybrid key for decryption. Expected: {}.",
scheme,
if scheme.requires_pq_key() {
"DecryptKey::PqOnly"
} else {
"DecryptKey::Symmetric"
}
))),
(DecryptKey::PqOnly(_), _) => Err(TypeError::ConfigurationError(format!(
"Scheme '{}' does not accept a PQ-only key for decryption. Expected: {}.",
scheme,
if scheme.requires_hybrid_key() {
"DecryptKey::Hybrid"
} else {
"DecryptKey::Symmetric"
}
))),
}
}
}
impl SchemeSelector for CryptoPolicyEngine {
type Error = TypeError;
fn select_encryption_scheme(
&self,
data: &[u8],
ctx: &CryptoContext,
) -> std::result::Result<String, Self::Error> {
Self::select_encryption_scheme(
data,
&CoreConfig {
security_level: ctx.security_level,
performance_preference: ctx.performance_preference.clone(),
hardware_acceleration: ctx.hardware_acceleration,
..CoreConfig::default()
},
ctx.use_case.as_ref(),
)
}
fn select_signature_scheme(
&self,
ctx: &CryptoContext,
) -> std::result::Result<String, Self::Error> {
Self::select_signature_scheme(&CoreConfig {
security_level: ctx.security_level,
performance_preference: ctx.performance_preference.clone(),
hardware_acceleration: ctx.hardware_acceleration,
..CoreConfig::default()
})
}
fn analyze_data_characteristics(&self, data: &[u8]) -> DataCharacteristics {
Self::analyze_data_characteristics(data)
}
}
fn calculate_entropy(data: &[u8]) -> f64 {
if data.is_empty() {
return 0.0;
}
let mut frequency = [0u64; 256];
for &byte in data {
let index = usize::from(byte);
if let Some(count) = frequency.get_mut(index) {
*count = count.saturating_add(1);
}
}
#[allow(clippy::cast_precision_loss)]
let len = data.len() as f64;
let mut entropy = 0.0_f64;
for &count in &frequency {
if count > 0 {
#[allow(clippy::cast_precision_loss)]
let probability = count as f64 / len;
entropy -= probability * probability.log2();
}
}
entropy
}
fn detect_pattern_type(data: &[u8]) -> PatternType {
if data.is_empty() {
return PatternType::Random;
}
let entropy = calculate_entropy(data);
if entropy > 7.5 {
return PatternType::Random;
}
let mut is_text = true;
for &byte in data {
if !(byte.is_ascii_graphic() || byte.is_ascii_whitespace()) {
is_text = false;
break;
}
}
if is_text && entropy > 4.0 {
return PatternType::Text;
}
let mut repetitive = true;
if data.len() > 8 {
let chunk_size = std::cmp::min(8, data.len() / 4);
let first_chunk = data.get(..chunk_size);
if let Some(first) = first_chunk {
for chunk in data.chunks(chunk_size).skip(1) {
if chunk != first {
repetitive = false;
break;
}
}
} else {
repetitive = false;
}
} else {
repetitive = false;
}
if repetitive {
return PatternType::Repetitive;
}
let has_structure = data.windows(4).any(|window| {
let w0 = window.first().copied().unwrap_or(0);
let w1 = window.get(1).copied().unwrap_or(0);
let w2 = window.get(2).copied().unwrap_or(0);
let w3 = window.get(3).copied().unwrap_or(0);
w0.wrapping_add(1) == w1 && w1.wrapping_add(1) == w2 && w2.wrapping_add(1) == w3
});
if has_structure || entropy < 6.0 { PatternType::Structured } else { PatternType::Binary }
}
use crate::primitives::kem::ml_kem::MlKemSecurityLevel;
#[must_use]
pub fn ml_kem_level_to_security_level(level: MlKemSecurityLevel) -> SecurityLevel {
match level {
MlKemSecurityLevel::MlKem512 => SecurityLevel::Standard,
MlKemSecurityLevel::MlKem768 => SecurityLevel::High,
MlKemSecurityLevel::MlKem1024 => SecurityLevel::Maximum,
}
}
pub const DEFAULT_ENCRYPTION_SCHEME: &str = "hybrid-ml-kem-768-aes-256-gcm";
pub const DEFAULT_SIGNATURE_SCHEME: &str = "hybrid-ml-dsa-65-ed25519";
pub const HYBRID_ENCRYPTION_512: &str = "hybrid-ml-kem-512-aes-256-gcm";
pub const HYBRID_ENCRYPTION_768: &str = "hybrid-ml-kem-768-aes-256-gcm";
pub const HYBRID_ENCRYPTION_1024: &str = "hybrid-ml-kem-1024-aes-256-gcm";
pub const HYBRID_SIGNATURE_44: &str = "hybrid-ml-dsa-44-ed25519";
pub const HYBRID_SIGNATURE_65: &str = "hybrid-ml-dsa-65-ed25519";
pub const HYBRID_SIGNATURE_87: &str = "hybrid-ml-dsa-87-ed25519";
pub const PQ_ENCRYPTION_512: &str = "pq-ml-kem-512-aes-256-gcm";
pub const PQ_ENCRYPTION_768: &str = "pq-ml-kem-768-aes-256-gcm";
pub const PQ_ENCRYPTION_1024: &str = "pq-ml-kem-1024-aes-256-gcm";
pub const PQ_SIGNATURE_44: &str = "pq-ml-dsa-44";
pub const PQ_SIGNATURE_65: &str = "pq-ml-dsa-65";
pub const PQ_SIGNATURE_87: &str = "pq-ml-dsa-87";
pub const DEFAULT_PQ_ENCRYPTION_SCHEME: &str = PQ_ENCRYPTION_768;
pub const DEFAULT_PQ_SIGNATURE_SCHEME: &str = PQ_SIGNATURE_65;
pub const CHACHA20_POLY1305: &str = "chacha20-poly1305";
pub const CLASSICAL_AES_GCM: &str = "aes-256-gcm";
pub const CLASSICAL_ED25519: &str = "ed25519";
#[derive(Debug, Clone)]
pub struct PerformanceMetrics {
pub encryption_speed_ms: f64,
pub memory_usage_mb: f64,
}
impl Default for PerformanceMetrics {
fn default() -> Self {
Self { encryption_speed_ms: 100.0, memory_usage_mb: 100.0 }
}
}
#[cfg(kani)]
mod kani_proofs {
use super::*;
#[kani::proof]
fn force_scheme_covers_all_variants() {
let scheme: crate::types::CryptoScheme = kani::any();
let result = CryptoPolicyEngine::force_scheme(&scheme);
kani::assert(!result.is_empty(), "force_scheme must return a non-empty scheme string");
}
#[kani::proof]
fn select_pq_encryption_covers_all_levels() {
let level: SecurityLevel = kani::any();
let config = CoreConfig {
security_level: level,
performance_preference: PerformancePreference::Balanced,
hardware_acceleration: true,
fallback_enabled: true,
strict_validation: true,
};
let result = CryptoPolicyEngine::select_pq_encryption_scheme(&config);
kani::assert(
result.is_ok(),
"PQ encryption selection must succeed for all security levels",
);
}
#[kani::proof]
fn select_pq_signature_covers_all_levels() {
let level: SecurityLevel = kani::any();
let config = CoreConfig {
security_level: level,
performance_preference: PerformancePreference::Balanced,
hardware_acceleration: true,
fallback_enabled: true,
strict_validation: true,
};
let result = CryptoPolicyEngine::select_pq_signature_scheme(&config);
kani::assert(result.is_ok(), "PQ signature selection must succeed for all security levels");
}
#[kani::proof]
fn select_encryption_covers_all_levels() {
let level: SecurityLevel = kani::any();
let config = CoreConfig {
security_level: level,
performance_preference: PerformancePreference::Balanced,
hardware_acceleration: true,
fallback_enabled: true,
strict_validation: true,
};
let data = [0u8; 16];
let result = CryptoPolicyEngine::select_encryption_scheme(&data, &config, None);
kani::assert(result.is_ok(), "Encryption selection must succeed for all levels");
}
#[kani::proof]
fn select_signature_covers_all_levels() {
let level: SecurityLevel = kani::any();
let config = CoreConfig {
security_level: level,
performance_preference: PerformancePreference::Balanced,
hardware_acceleration: true,
fallback_enabled: true,
strict_validation: true,
};
let result = CryptoPolicyEngine::select_signature_scheme(&config);
kani::assert(result.is_ok(), "Signature selection must succeed for all levels");
}
}
#[cfg(test)]
#[allow(
clippy::panic,
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::arithmetic_side_effects,
clippy::panic_in_result_fn,
clippy::unnecessary_wraps,
clippy::redundant_clone,
clippy::useless_vec,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::clone_on_copy,
clippy::len_zero,
clippy::single_match,
clippy::unnested_or_patterns,
clippy::default_constructed_unit_structs,
clippy::redundant_closure_for_method_calls,
clippy::semicolon_if_nothing_returned,
clippy::unnecessary_unwrap,
clippy::redundant_pattern_matching,
clippy::missing_const_for_thread_local,
clippy::get_first,
clippy::float_cmp,
clippy::needless_borrows_for_generic_args,
unused_qualifications
)]
mod tests {
use super::*;
#[test]
fn test_scheme_constants_succeeds() {
assert_eq!(DEFAULT_ENCRYPTION_SCHEME, "hybrid-ml-kem-768-aes-256-gcm");
assert_eq!(DEFAULT_SIGNATURE_SCHEME, "hybrid-ml-dsa-65-ed25519");
}
#[test]
fn test_policy_engine_new_succeeds() {
let engine = CryptoPolicyEngine::new();
assert!(std::mem::size_of_val(&engine) == 0);
}
#[test]
fn test_default_scheme_succeeds() {
assert_eq!(CryptoPolicyEngine::default_scheme(), DEFAULT_ENCRYPTION_SCHEME);
}
#[test]
fn test_recommend_scheme_secure_messaging_returns_hybrid_scheme_succeeds() -> Result<()> {
let config = CoreConfig::default();
let scheme = CryptoPolicyEngine::recommend_scheme(&UseCase::SecureMessaging, &config)?;
assert_eq!(scheme, "hybrid-ml-kem-768-aes-256-gcm");
Ok(())
}
#[test]
fn test_select_encryption_scheme_maximum_security_succeeds() -> Result<()> {
let config = CoreConfig::new().with_security_level(SecurityLevel::Maximum);
let data = b"test data";
let scheme = CryptoPolicyEngine::select_encryption_scheme(data, &config, None)?;
assert_eq!(scheme, "hybrid-ml-kem-1024-aes-256-gcm");
Ok(())
}
#[test]
fn test_analyze_empty_data_succeeds() {
let data: &[u8] = &[];
let characteristics = CryptoPolicyEngine::analyze_data_characteristics(data);
assert_eq!(characteristics.size, 0);
assert_eq!(characteristics.entropy, 0.0);
}
#[test]
fn test_analyze_repetitive_data_returns_low_entropy_succeeds() {
let data = vec![0u8; 1000];
let characteristics = CryptoPolicyEngine::analyze_data_characteristics(&data);
assert!(characteristics.entropy < 1.0);
assert_eq!(characteristics.pattern_type, PatternType::Repetitive);
}
#[test]
fn test_hardware_acceleration_false_selects_chacha20_scheme_succeeds() {
let config = CoreConfig::new().with_hardware_acceleration(false);
let scheme =
CryptoPolicyEngine::select_encryption_scheme(b"test data", &config, None).unwrap();
assert_eq!(scheme, CHACHA20_POLY1305);
}
#[test]
fn test_hardware_acceleration_true_selects_hybrid_scheme_succeeds() {
let config = CoreConfig::new()
.with_hardware_acceleration(true)
.with_security_level(SecurityLevel::High);
let scheme =
CryptoPolicyEngine::select_encryption_scheme(b"test data", &config, None).unwrap();
assert_ne!(scheme, CHACHA20_POLY1305);
}
#[test]
fn test_data_aware_random_data_speed_selects_smaller_kem_scheme_succeeds() {
use rand::RngCore;
let mut data = vec![0u8; 8192];
rand::thread_rng().fill_bytes(&mut data);
let config = CoreConfig::new()
.with_performance_preference(PerformancePreference::Speed)
.with_security_level(SecurityLevel::High);
let scheme = CryptoPolicyEngine::select_encryption_scheme(&data, &config, None).unwrap();
assert_eq!(scheme, HYBRID_ENCRYPTION_512);
}
#[test]
fn test_data_aware_structured_data_balanced_no_downgrade_succeeds() {
let data = b"This is structured text data for encryption testing";
let config = CoreConfig::new()
.with_performance_preference(PerformancePreference::Balanced)
.with_security_level(SecurityLevel::High);
let scheme = CryptoPolicyEngine::select_encryption_scheme(data, &config, None).unwrap();
assert_eq!(scheme, HYBRID_ENCRYPTION_768);
}
#[test]
fn test_force_scheme_returns_forced_scheme_succeeds() {
use crate::types::CryptoScheme;
let result = CryptoPolicyEngine::force_scheme(&CryptoScheme::Symmetric);
assert_eq!(result, "aes-256-gcm");
let result = CryptoPolicyEngine::force_scheme(&CryptoScheme::PostQuantum);
assert_eq!(result, DEFAULT_PQ_ENCRYPTION_SCHEME);
}
#[test]
fn test_security_level_influences_encryption_scheme_succeeds() {
let data = b"test data for scheme selection";
let config_a = CoreConfig::new()
.with_security_level(SecurityLevel::Standard)
.with_hardware_acceleration(true);
let result_a = CryptoPolicyEngine::select_encryption_scheme(data, &config_a, None).unwrap();
let config_b = CoreConfig::new()
.with_security_level(SecurityLevel::Maximum)
.with_hardware_acceleration(true);
let result_b = CryptoPolicyEngine::select_encryption_scheme(data, &config_b, None).unwrap();
assert_ne!(result_a, result_b, "security_level must influence encryption scheme selection");
}
#[test]
fn test_security_level_influences_signature_scheme_succeeds() {
let config_a = CoreConfig::new().with_security_level(SecurityLevel::Standard);
let result_a = CryptoPolicyEngine::select_signature_scheme(&config_a).unwrap();
let config_b = CoreConfig::new().with_security_level(SecurityLevel::Maximum);
let result_b = CryptoPolicyEngine::select_signature_scheme(&config_b).unwrap();
assert_ne!(result_a, result_b, "security_level must influence signature scheme selection");
}
#[test]
fn test_security_level_influences_pq_encryption_scheme_succeeds() {
let config_a = CoreConfig::new().with_security_level(SecurityLevel::Standard);
let result_a = CryptoPolicyEngine::select_pq_encryption_scheme(&config_a).unwrap();
let config_b = CoreConfig::new().with_security_level(SecurityLevel::Maximum);
let result_b = CryptoPolicyEngine::select_pq_encryption_scheme(&config_b).unwrap();
assert_ne!(
result_a, result_b,
"security_level must influence PQ encryption scheme selection"
);
}
#[test]
fn test_performance_preference_influences_encryption_scheme_succeeds() {
use rand::RngCore;
let mut data = vec![0u8; 8192];
rand::thread_rng().fill_bytes(&mut data);
let config_a = CoreConfig::new()
.with_performance_preference(PerformancePreference::Speed)
.with_security_level(SecurityLevel::High)
.with_hardware_acceleration(true);
let result_a =
CryptoPolicyEngine::select_encryption_scheme(&data, &config_a, None).unwrap();
let config_b = CoreConfig::new()
.with_performance_preference(PerformancePreference::Balanced)
.with_security_level(SecurityLevel::High)
.with_hardware_acceleration(true);
let result_b =
CryptoPolicyEngine::select_encryption_scheme(&data, &config_b, None).unwrap();
assert_ne!(
result_a, result_b,
"performance_preference must influence encryption scheme for random data"
);
}
#[test]
fn test_hardware_acceleration_influences_encryption_scheme_succeeds() {
let data = b"test data for hardware influence";
let config_a = CoreConfig::new()
.with_hardware_acceleration(false)
.with_security_level(SecurityLevel::High);
let result_a = CryptoPolicyEngine::select_encryption_scheme(data, &config_a, None).unwrap();
let config_b = CoreConfig::new()
.with_hardware_acceleration(true)
.with_security_level(SecurityLevel::High);
let result_b = CryptoPolicyEngine::select_encryption_scheme(data, &config_b, None).unwrap();
assert_ne!(
result_a, result_b,
"hardware_acceleration must influence encryption scheme selection"
);
}
#[test]
fn test_fallback_enabled_does_not_influence_scheme_selection_succeeds() {
let data = b"test data for fallback influence check";
let config_a =
CoreConfig::new().with_security_level(SecurityLevel::High).with_fallback(true);
let result_a = CryptoPolicyEngine::select_encryption_scheme(data, &config_a, None).unwrap();
let config_b =
CoreConfig::new().with_security_level(SecurityLevel::High).with_fallback(false);
let result_b = CryptoPolicyEngine::select_encryption_scheme(data, &config_b, None).unwrap();
assert_eq!(
result_a, result_b,
"fallback_enabled does not influence scheme selection (consumed by validate() only)"
);
}
#[test]
fn test_fallback_enabled_influences_validate_returns_error_without_fallback_fails() {
let config_with_fallback = CoreConfig::new()
.with_performance_preference(PerformancePreference::Speed)
.with_fallback(true);
let config_without_fallback = CoreConfig::new()
.with_performance_preference(PerformancePreference::Speed)
.with_fallback(false);
assert!(
config_with_fallback.validate().is_ok(),
"Speed + fallback_enabled=true must pass validation"
);
assert!(
config_without_fallback.validate().is_err(),
"Speed + fallback_enabled=false must fail validation"
);
}
#[test]
fn test_strict_validation_influences_validate_rejects_standard_level_fails() {
let config_strict = CoreConfig::new()
.with_security_level(SecurityLevel::Standard)
.with_strict_validation(true);
let config_relaxed = CoreConfig::new()
.with_security_level(SecurityLevel::Standard)
.with_strict_validation(false);
assert!(
config_strict.validate().is_err(),
"strict_validation=true with Standard level must fail validation"
);
assert!(
config_relaxed.validate().is_ok(),
"strict_validation=false with Standard level must pass validation"
);
}
#[test]
fn test_strict_validation_does_not_influence_scheme_selection_succeeds() {
let data = b"test data for strict validation influence check";
let config_a =
CoreConfig::new().with_security_level(SecurityLevel::High).with_strict_validation(true);
let result_a = CryptoPolicyEngine::select_encryption_scheme(data, &config_a, None).unwrap();
let config_b = CoreConfig::new()
.with_security_level(SecurityLevel::High)
.with_strict_validation(false);
let result_b = CryptoPolicyEngine::select_encryption_scheme(data, &config_b, None).unwrap();
assert_eq!(
result_a, result_b,
"strict_validation does not influence scheme selection (consumed by validate() only)"
);
}
#[test]
fn test_security_level_influences_pq_signature_scheme_succeeds() {
let config_a = CoreConfig::new().with_security_level(SecurityLevel::Standard);
let result_a = CryptoPolicyEngine::select_pq_signature_scheme(&config_a).unwrap();
let config_b = CoreConfig::new().with_security_level(SecurityLevel::Maximum);
let result_b = CryptoPolicyEngine::select_pq_signature_scheme(&config_b).unwrap();
assert_ne!(
result_a, result_b,
"security_level must influence PQ signature scheme selection"
);
}
#[test]
fn test_security_level_influences_encryption_scheme_typed_succeeds() {
let config_a = CoreConfig::new().with_security_level(SecurityLevel::Standard);
let result_a = CryptoPolicyEngine::select_encryption_scheme_typed(&config_a);
let config_b = CoreConfig::new().with_security_level(SecurityLevel::Maximum);
let result_b = CryptoPolicyEngine::select_encryption_scheme_typed(&config_b);
assert_ne!(
result_a, result_b,
"security_level must influence typed encryption scheme selection"
);
}
#[test]
fn test_selector_pq_only_standard_returns_pq512() {
let config = CoreConfig::new().with_security_level(SecurityLevel::Standard);
let scheme = CryptoPolicyEngine::select_encryption_scheme_typed_with_mode(
&config,
crate::types::types::CryptoMode::PqOnly,
);
assert_eq!(scheme, EncryptionScheme::PqMlKem512Aes256Gcm);
}
#[test]
fn test_selector_pq_only_high_returns_pq768() {
let config = CoreConfig::new().with_security_level(SecurityLevel::High);
let scheme = CryptoPolicyEngine::select_encryption_scheme_typed_with_mode(
&config,
crate::types::types::CryptoMode::PqOnly,
);
assert_eq!(scheme, EncryptionScheme::PqMlKem768Aes256Gcm);
}
#[test]
fn test_selector_pq_only_maximum_returns_pq1024() {
let config = CoreConfig::new().with_security_level(SecurityLevel::Maximum);
let scheme = CryptoPolicyEngine::select_encryption_scheme_typed_with_mode(
&config,
crate::types::types::CryptoMode::PqOnly,
);
assert_eq!(scheme, EncryptionScheme::PqMlKem1024Aes256Gcm);
}
#[test]
fn test_selector_hybrid_mode_unchanged_by_refactor() {
let config = CoreConfig::new().with_security_level(SecurityLevel::Standard);
let scheme = CryptoPolicyEngine::select_encryption_scheme_typed_with_mode(
&config,
crate::types::types::CryptoMode::Hybrid,
);
assert_eq!(scheme, EncryptionScheme::HybridMlKem512Aes256Gcm);
}
#[test]
fn test_pq_key_level_mismatch_encrypt_rejected() {
use crate::hybrid::pq_only::generate_pq_keypair_with_level;
use crate::primitives::kem::ml_kem::MlKemSecurityLevel;
let (pk_512, _) = generate_pq_keypair_with_level(MlKemSecurityLevel::MlKem512).unwrap();
let result = CryptoPolicyEngine::validate_key_matches_scheme(
&EncryptKey::PqOnly(&pk_512),
&EncryptionScheme::PqMlKem768Aes256Gcm,
);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(err.contains("ML-KEM"));
}
#[test]
fn test_pq_key_level_mismatch_decrypt_rejected() {
use crate::hybrid::pq_only::generate_pq_keypair_with_level;
use crate::primitives::kem::ml_kem::MlKemSecurityLevel;
let (_, sk_512) = generate_pq_keypair_with_level(MlKemSecurityLevel::MlKem512).unwrap();
let result = CryptoPolicyEngine::validate_decrypt_key_matches_scheme(
&DecryptKey::PqOnly(&sk_512),
&EncryptionScheme::PqMlKem1024Aes256Gcm,
);
assert!(result.is_err());
let err = format!("{}", result.unwrap_err());
assert!(err.contains("ML-KEM"));
}
#[test]
fn test_hardware_acceleration_does_not_influence_signature_scheme_succeeds() {
let config_a = CoreConfig::new()
.with_security_level(SecurityLevel::High)
.with_hardware_acceleration(false);
let result_a = CryptoPolicyEngine::select_signature_scheme(&config_a).unwrap();
let config_b = CoreConfig::new()
.with_security_level(SecurityLevel::High)
.with_hardware_acceleration(true);
let result_b = CryptoPolicyEngine::select_signature_scheme(&config_b).unwrap();
assert_eq!(
result_a, result_b,
"hardware_acceleration must NOT influence signature scheme selection"
);
}
#[test]
fn test_performance_preference_influences_adaptive_selection_succeeds() {
let data = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
let config_speed = CoreConfig::new()
.with_performance_preference(PerformancePreference::Speed)
.with_security_level(SecurityLevel::High);
let config_memory = CoreConfig::new()
.with_performance_preference(PerformancePreference::Memory)
.with_security_level(SecurityLevel::High);
let metrics_slow = PerformanceMetrics { encryption_speed_ms: 1500.0, memory_usage_mb: 0.0 };
let result_speed =
CryptoPolicyEngine::adaptive_selection(data, &metrics_slow, &config_speed).unwrap();
let result_memory =
CryptoPolicyEngine::adaptive_selection(data, &metrics_slow, &config_memory).unwrap();
assert_ne!(
result_speed, result_memory,
"performance_preference must influence adaptive scheme selection when metrics cross thresholds"
);
}
}