1use anyhow::Result;
7use chrono::{Duration as ChronoDuration, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::sync::Arc;
11use std::time::Instant;
12use tokio::sync::RwLock;
13
14pub mod post_quantum;
15pub mod traits;
16
17pub use post_quantum::{PQCryptoMetrics, PostQuantumCryptoEngine, PostQuantumKeyPair};
18pub use traits::{
19 AuditFilter, AuditLogEntry, AuditLogger, AuthenticationProvider, AuthorizationProvider,
20 Credentials, RateLimiter, SecurityContext, SecurityMetrics, ThreatAlert, ThreatContext,
21 ThreatDetector,
22};
23
24#[derive(Debug, Clone, Serialize, Deserialize, Default)]
26pub struct SecurityConfig {
27 pub authentication: AuthConfig,
29 pub authorization: AuthzConfig,
31 pub encryption: EncryptionConfig,
33 pub post_quantum: PostQuantumConfig,
35 pub quantum_resistant_certs: QuantumResistantCerts,
37 pub audit: AuditConfig,
39 pub threat_detection: ThreatDetectionConfig,
41 pub rate_limiting: RateLimitConfig,
43 pub session: SessionConfig,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct AuthConfig {
50 pub methods: Vec<AuthMethod>,
52 pub mfa: MfaConfig,
54 pub token: TokenConfig,
56 pub password_policy: PasswordPolicy,
58}
59
60impl Default for AuthConfig {
61 fn default() -> Self {
62 Self {
63 methods: vec![AuthMethod::ApiKey, AuthMethod::JWT],
64 mfa: MfaConfig::default(),
65 token: TokenConfig::default(),
66 password_policy: PasswordPolicy::default(),
67 }
68 }
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
73pub enum AuthMethod {
74 ApiKey,
75 JWT,
76 OAuth2,
77 SAML,
78 Certificate,
79 Basic,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct MfaConfig {
85 pub enabled: bool,
86 pub required_for_admin: bool,
87 pub methods: Vec<MfaMethod>,
88 pub backup_codes: bool,
89}
90
91impl Default for MfaConfig {
92 fn default() -> Self {
93 Self {
94 enabled: false,
95 required_for_admin: true,
96 methods: vec![MfaMethod::TOTP],
97 backup_codes: true,
98 }
99 }
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
104pub enum MfaMethod {
105 TOTP, SMS, Email, Hardware, }
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct TokenConfig {
114 pub jwt_secret: String,
115 pub access_token_ttl: ChronoDuration,
116 pub refresh_token_ttl: ChronoDuration,
117 pub issuer: String,
118 pub audience: String,
119}
120
121impl Default for TokenConfig {
122 fn default() -> Self {
123 Self {
124 jwt_secret: "change-this-secret".to_string(),
125 access_token_ttl: ChronoDuration::hours(1),
126 refresh_token_ttl: ChronoDuration::days(30),
127 issuer: "oxirs-stream".to_string(),
128 audience: "oxirs-api".to_string(),
129 }
130 }
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct PasswordPolicy {
136 pub min_length: usize,
137 pub require_uppercase: bool,
138 pub require_lowercase: bool,
139 pub require_numbers: bool,
140 pub require_symbols: bool,
141 pub max_age_days: Option<u32>,
142 pub history_count: usize,
143}
144
145impl Default for PasswordPolicy {
146 fn default() -> Self {
147 Self {
148 min_length: 12,
149 require_uppercase: true,
150 require_lowercase: true,
151 require_numbers: true,
152 require_symbols: true,
153 max_age_days: Some(90),
154 history_count: 5,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct AuthzConfig {
162 pub model: AuthzModel,
164 pub default_permissions: Vec<Permission>,
166 pub rbac: RbacConfig,
168 pub abac: AbacConfig,
170}
171
172impl Default for AuthzConfig {
173 fn default() -> Self {
174 Self {
175 model: AuthzModel::RBAC,
176 default_permissions: vec![Permission::Read],
177 rbac: RbacConfig::default(),
178 abac: AbacConfig::default(),
179 }
180 }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub enum AuthzModel {
186 RBAC, ABAC, MAC, DAC, }
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
194pub enum Permission {
195 Read,
196 Write,
197 Delete,
198 Admin,
199 Execute,
200 Stream,
201 Query,
202 Configure,
203}
204
205impl std::fmt::Display for Permission {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 match self {
208 Permission::Read => write!(f, "read"),
209 Permission::Write => write!(f, "write"),
210 Permission::Delete => write!(f, "delete"),
211 Permission::Admin => write!(f, "admin"),
212 Permission::Execute => write!(f, "execute"),
213 Permission::Stream => write!(f, "stream"),
214 Permission::Query => write!(f, "query"),
215 Permission::Configure => write!(f, "configure"),
216 }
217 }
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct RbacConfig {
223 pub enabled: bool,
224 pub default_role: String,
225 pub role_hierarchy: HashMap<String, Vec<String>>,
226 pub role_permissions: HashMap<String, Vec<Permission>>,
227}
228
229impl Default for RbacConfig {
230 fn default() -> Self {
231 let mut role_permissions = HashMap::new();
232 role_permissions.insert(
233 "viewer".to_string(),
234 vec![Permission::Read, Permission::Query],
235 );
236 role_permissions.insert(
237 "user".to_string(),
238 vec![
239 Permission::Read,
240 Permission::Write,
241 Permission::Stream,
242 Permission::Query,
243 ],
244 );
245 role_permissions.insert(
246 "admin".to_string(),
247 vec![
248 Permission::Read,
249 Permission::Write,
250 Permission::Delete,
251 Permission::Admin,
252 Permission::Execute,
253 Permission::Stream,
254 Permission::Query,
255 Permission::Configure,
256 ],
257 );
258
259 let mut role_hierarchy = HashMap::new();
260 role_hierarchy.insert(
261 "admin".to_string(),
262 vec!["user".to_string(), "viewer".to_string()],
263 );
264 role_hierarchy.insert("user".to_string(), vec!["viewer".to_string()]);
265
266 Self {
267 enabled: true,
268 default_role: "viewer".to_string(),
269 role_hierarchy,
270 role_permissions,
271 }
272 }
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct AbacConfig {
278 pub enabled: bool,
279 pub policy_engine: PolicyEngine,
280 pub attributes: Vec<AttributeDefinition>,
281}
282
283impl Default for AbacConfig {
284 fn default() -> Self {
285 Self {
286 enabled: false,
287 policy_engine: PolicyEngine::default(),
288 attributes: vec![
289 AttributeDefinition {
290 name: "department".to_string(),
291 attribute_type: AttributeType::String,
292 required: false,
293 },
294 AttributeDefinition {
295 name: "security_level".to_string(),
296 attribute_type: AttributeType::Integer,
297 required: true,
298 },
299 ],
300 }
301 }
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct PolicyEngine {
307 pub language: PolicyLanguage,
308 pub cache_policies: bool,
309 pub policy_files: Vec<String>,
310}
311
312impl Default for PolicyEngine {
313 fn default() -> Self {
314 Self {
315 language: PolicyLanguage::OPA,
316 cache_policies: true,
317 policy_files: vec!["/etc/oxirs/policies/".to_string()],
318 }
319 }
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
324pub enum PolicyLanguage {
325 OPA, XACML, Cedar, Custom, }
330
331#[derive(Debug, Clone, Serialize, Deserialize)]
333pub struct AttributeDefinition {
334 pub name: String,
335 pub attribute_type: AttributeType,
336 pub required: bool,
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub enum AttributeType {
342 String,
343 Integer,
344 Boolean,
345 Float,
346 List,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, Default)]
351pub struct EncryptionConfig {
352 pub at_rest: EncryptionAtRest,
354 pub in_transit: EncryptionInTransit,
356 pub field_level: FieldLevelEncryption,
358}
359
360#[derive(Debug, Clone, Serialize, Deserialize)]
362pub struct EncryptionAtRest {
363 pub enabled: bool,
364 pub algorithm: EncryptionAlgorithm,
365 pub key_management: KeyManagement,
366}
367
368impl Default for EncryptionAtRest {
369 fn default() -> Self {
370 Self {
371 enabled: true,
372 algorithm: EncryptionAlgorithm::AES256GCM,
373 key_management: KeyManagement::default(),
374 }
375 }
376}
377
378#[derive(Debug, Clone, Serialize, Deserialize)]
380pub struct EncryptionInTransit {
381 pub enabled: bool,
382 pub tls_version: TlsVersion,
383 pub cipher_suites: Vec<String>,
384 pub certificate_validation: bool,
385}
386
387impl Default for EncryptionInTransit {
388 fn default() -> Self {
389 Self {
390 enabled: true,
391 tls_version: TlsVersion::V1_3,
392 cipher_suites: vec![
393 "TLS_AES_256_GCM_SHA384".to_string(),
394 "TLS_CHACHA20_POLY1305_SHA256".to_string(),
395 ],
396 certificate_validation: true,
397 }
398 }
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
403pub struct FieldLevelEncryption {
404 pub enabled: bool,
405 pub fields: Vec<String>,
406 pub algorithm: EncryptionAlgorithm,
407}
408
409impl Default for FieldLevelEncryption {
410 fn default() -> Self {
411 Self {
412 enabled: false,
413 fields: vec!["password".to_string(), "ssn".to_string()],
414 algorithm: EncryptionAlgorithm::AES256GCM,
415 }
416 }
417}
418
419#[derive(Debug, Clone, Serialize, Deserialize)]
421pub enum EncryptionAlgorithm {
422 AES256GCM,
424 AES256CBC,
425 ChaCha20Poly1305,
426
427 Kyber512,
429 Kyber768,
430 Kyber1024,
431
432 NewHope1024,
434 FrodoKEM640,
435 FrodoKEM976,
436 FrodoKEM1344,
437
438 Rainbow1,
440 Rainbow3,
441 Rainbow5,
442
443 SphincsPlus128s,
445 SphincsPlus256s,
446
447 SikeP434,
449 SikeP503,
450 SikeP751,
451
452 McEliece348864,
454 McEliece460896,
455 McEliece6688128,
456
457 HybridAesKyber768,
459 HybridChaCha20NewHope,
460}
461
462#[derive(Debug, Clone, Serialize, Deserialize)]
464pub enum TlsVersion {
465 V1_2,
466 V1_3,
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize)]
471pub struct KeyManagement {
472 pub provider: KeyProvider,
473 pub rotation_interval: ChronoDuration,
474 pub key_derivation: KeyDerivation,
475}
476
477impl Default for KeyManagement {
478 fn default() -> Self {
479 Self {
480 provider: KeyProvider::Local,
481 rotation_interval: ChronoDuration::days(90),
482 key_derivation: KeyDerivation::PBKDF2,
483 }
484 }
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
489pub enum KeyProvider {
490 Local,
491 HSM,
492 AwsKms,
493 AzureKeyVault,
494 HashiCorpVault,
495}
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
499pub enum KeyDerivation {
500 PBKDF2,
502 Scrypt,
503 Argon2,
504
505 LatticeBasedKDF,
507 HashBasedKDF,
508 CodeBasedKDF,
509
510 HybridArgon2Lattice,
512 HybridScryptHash,
513}
514
515#[derive(Debug, Clone, Serialize, Deserialize)]
517pub enum PostQuantumSignature {
518 Dilithium2,
520 Dilithium3,
521 Dilithium5,
522
523 SphincsPlusSha2128s,
525 SphincsPlusSha2128f,
526 SphincsPlusSha2192s,
527 SphincsPlusSha2192f,
528 SphincsPlusSha2256s,
529 SphincsPlusSha2256f,
530 SphincsPlusShake128s,
531 SphincsPlusShake128f,
532 SphincsPlusShake192s,
533 SphincsPlusShake192f,
534 SphincsPlusShake256s,
535 SphincsPlusShake256f,
536
537 RainbowIClassic,
539 RainbowICircumzenithal,
540 RainbowICompressed,
541 RainbowIiiClassic,
542 RainbowIiiCircumzenithal,
543 RainbowIiiCompressed,
544 RainbowVClassic,
545 RainbowVCircumzenithal,
546 RainbowVCompressed,
547
548 Falcon512,
550 Falcon1024,
551
552 PicnicL1Fs,
554 PicnicL1Ur,
555 PicnicL3Fs,
556 PicnicL3Ur,
557 PicnicL5Fs,
558 PicnicL5Ur,
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct PostQuantumConfig {
564 pub enabled: bool,
565 pub primary_kem: Option<EncryptionAlgorithm>,
566 pub signature_algorithm: Option<PostQuantumSignature>,
567 pub hybrid_mode: bool,
568 pub classical_fallback: bool,
569 pub quantum_security_level: QuantumSecurityLevel,
570 pub key_size_preferences: KeySizePreferences,
571}
572
573impl Default for PostQuantumConfig {
574 fn default() -> Self {
575 Self {
576 enabled: true,
577 primary_kem: Some(EncryptionAlgorithm::Kyber768),
578 signature_algorithm: Some(PostQuantumSignature::Dilithium3),
579 hybrid_mode: true,
580 classical_fallback: true,
581 quantum_security_level: QuantumSecurityLevel::Level3,
582 key_size_preferences: KeySizePreferences::default(),
583 }
584 }
585}
586
587#[derive(Debug, Clone, Serialize, Deserialize)]
589pub enum QuantumSecurityLevel {
590 Level1, Level2, Level3, Level4, Level5, }
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct KeySizePreferences {
600 pub prefer_smaller_keys: bool,
601 pub prefer_faster_signing: bool,
602 pub prefer_faster_verification: bool,
603 pub max_signature_size_kb: Option<u32>,
604 pub max_public_key_size_kb: Option<u32>,
605}
606
607impl Default for KeySizePreferences {
608 fn default() -> Self {
609 Self {
610 prefer_smaller_keys: true,
611 prefer_faster_signing: false,
612 prefer_faster_verification: true,
613 max_signature_size_kb: Some(50),
614 max_public_key_size_kb: Some(10),
615 }
616 }
617}
618
619#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct QuantumResistantCerts {
622 pub enabled: bool,
623 pub use_hybrid_certificates: bool,
624 pub pq_signature_algorithm: PostQuantumSignature,
625 pub classical_signature_fallback: bool,
626 pub certificate_chain_validation: PQCertValidation,
627}
628
629impl Default for QuantumResistantCerts {
630 fn default() -> Self {
631 Self {
632 enabled: false,
633 use_hybrid_certificates: true,
634 pq_signature_algorithm: PostQuantumSignature::Dilithium3,
635 classical_signature_fallback: true,
636 certificate_chain_validation: PQCertValidation::default(),
637 }
638 }
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize)]
643pub struct PQCertValidation {
644 pub require_pq_signatures: bool,
645 pub allow_mixed_chain: bool,
646 pub minimum_security_level: QuantumSecurityLevel,
647 pub validate_quantum_resistance: bool,
648}
649
650impl Default for PQCertValidation {
651 fn default() -> Self {
652 Self {
653 require_pq_signatures: false,
654 allow_mixed_chain: true,
655 minimum_security_level: QuantumSecurityLevel::Level3,
656 validate_quantum_resistance: true,
657 }
658 }
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
663pub struct AuditConfig {
664 pub enabled: bool,
665 pub events: Vec<AuditEvent>,
666 pub retention_days: u32,
667 pub log_format: AuditLogFormat,
668 pub output: AuditOutput,
669}
670
671impl Default for AuditConfig {
672 fn default() -> Self {
673 Self {
674 enabled: true,
675 events: vec![
676 AuditEvent::Authentication,
677 AuditEvent::Authorization,
678 AuditEvent::DataAccess,
679 AuditEvent::ConfigChange,
680 ],
681 retention_days: 365,
682 log_format: AuditLogFormat::JSON,
683 output: AuditOutput::File("/var/log/oxirs/audit.log".to_string()),
684 }
685 }
686}
687
688#[derive(Debug, Clone, Serialize, Deserialize)]
690pub enum AuditEvent {
691 Authentication,
692 Authorization,
693 DataAccess,
694 ConfigChange,
695 UserManagement,
696 SecurityAlert,
697}
698
699#[derive(Debug, Clone, Serialize, Deserialize)]
701pub enum AuditLogFormat {
702 JSON,
703 CEF, LEEF, Syslog,
706}
707
708#[derive(Debug, Clone, Serialize, Deserialize)]
710pub enum AuditOutput {
711 File(String),
712 Syslog,
713 Database,
714 SIEM(String), }
716
717#[derive(Debug, Clone, Serialize, Deserialize)]
719pub struct ThreatDetectionConfig {
720 pub enabled: bool,
721 pub rules: Vec<ThreatRule>,
722 pub anomaly_detection: AnomalyDetectionConfig,
723 pub response: ThreatResponseConfig,
724}
725
726impl Default for ThreatDetectionConfig {
727 fn default() -> Self {
728 Self {
729 enabled: true,
730 rules: vec![
731 ThreatRule {
732 name: "Multiple Failed Logins".to_string(),
733 description: "Detect multiple failed login attempts".to_string(),
734 condition: "failed_logins > 5 in 5m".to_string(),
735 severity: ThreatSeverity::High,
736 enabled: true,
737 },
738 ThreatRule {
739 name: "Unusual Access Pattern".to_string(),
740 description: "Detect unusual access patterns".to_string(),
741 condition: "requests > 1000 in 1m".to_string(),
742 severity: ThreatSeverity::Medium,
743 enabled: true,
744 },
745 ],
746 anomaly_detection: AnomalyDetectionConfig::default(),
747 response: ThreatResponseConfig::default(),
748 }
749 }
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct ThreatRule {
755 pub name: String,
756 pub description: String,
757 pub condition: String,
758 pub severity: ThreatSeverity,
759 pub enabled: bool,
760}
761
762#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
764pub enum ThreatSeverity {
765 Low,
766 Medium,
767 High,
768 Critical,
769}
770
771#[derive(Debug, Clone, Serialize, Deserialize)]
773pub struct AnomalyDetectionConfig {
774 pub enabled: bool,
775 pub algorithms: Vec<AnomalyAlgorithm>,
776 pub sensitivity: f64,
777 pub learning_period_days: u32,
778}
779
780impl Default for AnomalyDetectionConfig {
781 fn default() -> Self {
782 Self {
783 enabled: true,
784 algorithms: vec![
785 AnomalyAlgorithm::IsolationForest,
786 AnomalyAlgorithm::StatisticalOutlier,
787 ],
788 sensitivity: 0.8,
789 learning_period_days: 30,
790 }
791 }
792}
793
794#[derive(Debug, Clone, Serialize, Deserialize)]
796pub enum AnomalyAlgorithm {
797 IsolationForest,
798 StatisticalOutlier,
799 LSTM,
800 KMeans,
801}
802
803#[derive(Debug, Clone, Serialize, Deserialize)]
805pub struct ThreatResponseConfig {
806 pub auto_response: bool,
807 pub actions: Vec<ResponseAction>,
808 pub escalation: EscalationConfig,
809}
810
811impl Default for ThreatResponseConfig {
812 fn default() -> Self {
813 Self {
814 auto_response: true,
815 actions: vec![
816 ResponseAction::Block,
817 ResponseAction::RateLimit,
818 ResponseAction::Alert,
819 ],
820 escalation: EscalationConfig::default(),
821 }
822 }
823}
824
825#[derive(Debug, Clone, Serialize, Deserialize)]
827pub enum ResponseAction {
828 Block,
829 RateLimit,
830 Alert,
831 Quarantine,
832 Notify,
833}
834
835#[derive(Debug, Clone, Serialize, Deserialize)]
837pub struct EscalationConfig {
838 pub enabled: bool,
839 pub thresholds: HashMap<ThreatSeverity, u32>,
840 pub notifications: Vec<String>, }
842
843impl Default for EscalationConfig {
844 fn default() -> Self {
845 let mut thresholds = HashMap::new();
846 thresholds.insert(ThreatSeverity::Low, 10);
847 thresholds.insert(ThreatSeverity::Medium, 5);
848 thresholds.insert(ThreatSeverity::High, 1);
849 thresholds.insert(ThreatSeverity::Critical, 1);
850
851 Self {
852 enabled: true,
853 thresholds,
854 notifications: vec!["security@example.com".to_string()],
855 }
856 }
857}
858
859#[derive(Debug, Clone, Serialize, Deserialize)]
861pub struct RateLimitConfig {
862 pub enabled: bool,
863 pub global_limit: RateLimit,
864 pub per_user_limit: RateLimit,
865 pub per_ip_limit: RateLimit,
866 pub burst_limit: RateLimit,
867}
868
869impl Default for RateLimitConfig {
870 fn default() -> Self {
871 Self {
872 enabled: true,
873 global_limit: RateLimit {
874 requests: 10000,
875 window: ChronoDuration::minutes(1),
876 },
877 per_user_limit: RateLimit {
878 requests: 100,
879 window: ChronoDuration::minutes(1),
880 },
881 per_ip_limit: RateLimit {
882 requests: 1000,
883 window: ChronoDuration::minutes(1),
884 },
885 burst_limit: RateLimit {
886 requests: 50,
887 window: ChronoDuration::seconds(10),
888 },
889 }
890 }
891}
892
893#[derive(Debug, Clone, Serialize, Deserialize)]
895pub struct RateLimit {
896 pub requests: u32,
897 pub window: ChronoDuration,
898}
899
900#[derive(Debug, Clone, Serialize, Deserialize)]
902pub struct SessionConfig {
903 pub timeout: ChronoDuration,
904 pub max_concurrent: u32,
905 pub secure_cookies: bool,
906 pub same_site: SameSite,
907}
908
909impl Default for SessionConfig {
910 fn default() -> Self {
911 Self {
912 timeout: ChronoDuration::hours(8),
913 max_concurrent: 5,
914 secure_cookies: true,
915 same_site: SameSite::Strict,
916 }
917 }
918}
919
920#[derive(Debug, Clone, Serialize, Deserialize)]
922pub enum SameSite {
923 Strict,
924 Lax,
925 None,
926}
927
928pub struct SecurityManager {
930 config: SecurityConfig,
931 auth_provider: Arc<dyn AuthenticationProvider>,
932 authz_provider: Arc<dyn AuthorizationProvider>,
933 audit_logger: Arc<dyn AuditLogger>,
934 threat_detector: Arc<dyn ThreatDetector>,
935 rate_limiter: Arc<dyn RateLimiter>,
936 metrics: Arc<RwLock<SecurityMetrics>>,
937}
938
939impl SecurityManager {
940 pub fn new(
942 config: SecurityConfig,
943 auth_provider: Arc<dyn AuthenticationProvider>,
944 authz_provider: Arc<dyn AuthorizationProvider>,
945 audit_logger: Arc<dyn AuditLogger>,
946 threat_detector: Arc<dyn ThreatDetector>,
947 rate_limiter: Arc<dyn RateLimiter>,
948 ) -> Self {
949 Self {
950 config,
951 auth_provider,
952 authz_provider,
953 audit_logger,
954 threat_detector,
955 rate_limiter,
956 metrics: Arc::new(RwLock::new(SecurityMetrics::default())),
957 }
958 }
959
960 pub async fn authenticate(&self, credentials: &Credentials) -> Result<SecurityContext> {
962 let start_time = Instant::now();
963
964 if self.config.rate_limiting.enabled {
966 self.rate_limiter
967 .check_limit(&credentials.identifier())
968 .await?;
969 }
970
971 let result = self.auth_provider.authenticate(credentials).await;
973
974 let audit_event = AuditLogEntry {
976 event_type: AuditEvent::Authentication,
977 timestamp: Utc::now(),
978 user_id: credentials.user_id().map(|s| s.to_string()),
979 ip_address: credentials.ip_address().map(|s| s.to_string()),
980 success: result.is_ok(),
981 details: format!("Authentication attempt for {}", credentials.identifier()),
982 };
983
984 let _ = self.audit_logger.log(audit_event).await;
985
986 {
988 let mut metrics = self.metrics.write().await;
989 metrics.authentication_attempts += 1;
990 if result.is_ok() {
991 metrics.authentication_successes += 1;
992 } else {
993 metrics.authentication_failures += 1;
994 }
995 metrics.authentication_latency_ms = start_time.elapsed().as_millis() as f64;
996 }
997
998 if result.is_err() {
1000 let _ = self
1001 .threat_detector
1002 .detect_threat(&ThreatContext {
1003 event_type: "authentication_failure".to_string(),
1004 user_id: credentials.user_id().map(|s| s.to_string()),
1005 ip_address: credentials.ip_address().map(|s| s.to_string()),
1006 timestamp: Utc::now(),
1007 details: HashMap::new(),
1008 })
1009 .await;
1010 }
1011
1012 result
1013 }
1014
1015 pub async fn authorize(
1017 &self,
1018 context: &SecurityContext,
1019 resource: &str,
1020 action: &Permission,
1021 ) -> Result<bool> {
1022 let start_time = Instant::now();
1023
1024 let result = self
1025 .authz_provider
1026 .authorize(context, resource, action)
1027 .await;
1028 let success = result.as_ref().map(|&b| b).unwrap_or(false);
1029
1030 let audit_event = AuditLogEntry {
1032 event_type: AuditEvent::Authorization,
1033 timestamp: Utc::now(),
1034 user_id: context.user_id.clone(),
1035 ip_address: context.ip_address.clone(),
1036 success,
1037 details: format!("Authorization check for {action} on {resource}"),
1038 };
1039
1040 let _ = self.audit_logger.log(audit_event).await;
1041
1042 {
1044 let mut metrics = self.metrics.write().await;
1045 metrics.authorization_checks += 1;
1046 if success {
1047 metrics.authorization_successes += 1;
1048 } else {
1049 metrics.authorization_failures += 1;
1050 }
1051 metrics.authorization_latency_ms = start_time.elapsed().as_millis() as f64;
1052 }
1053
1054 result
1055 }
1056
1057 pub async fn log_data_access(
1059 &self,
1060 context: &SecurityContext,
1061 resource: &str,
1062 operation: &str,
1063 ) -> Result<()> {
1064 let audit_event = AuditLogEntry {
1065 event_type: AuditEvent::DataAccess,
1066 timestamp: Utc::now(),
1067 user_id: context.user_id.clone(),
1068 ip_address: context.ip_address.clone(),
1069 success: true,
1070 details: format!("Data access: {operation} on {resource}"),
1071 };
1072
1073 self.audit_logger.log(audit_event).await
1074 }
1075
1076 pub async fn get_metrics(&self) -> SecurityMetrics {
1078 self.metrics.read().await.clone()
1079 }
1080}
1081
1082#[cfg(test)]
1083mod tests {
1084 use super::*;
1085 use std::collections::HashSet;
1086
1087 #[test]
1088 fn test_security_config_defaults() {
1089 let config = SecurityConfig::default();
1090 assert!(config.authentication.methods.contains(&AuthMethod::ApiKey));
1091 assert!(config.authorization.rbac.enabled);
1092 assert!(config.encryption.at_rest.enabled);
1093 assert!(config.audit.enabled);
1094 }
1095
1096 #[test]
1097 fn test_security_context_permissions() {
1098 let mut context = SecurityContext {
1099 user_id: Some("test_user".to_string()),
1100 session_id: None,
1101 roles: HashSet::new(),
1102 permissions: HashSet::new(),
1103 attributes: HashMap::new(),
1104 ip_address: None,
1105 user_agent: None,
1106 authentication_method: None,
1107 authenticated_at: None,
1108 };
1109
1110 context.permissions.insert(Permission::Read);
1111 assert!(context.has_permission(&Permission::Read));
1112 assert!(!context.has_permission(&Permission::Write));
1113 }
1114
1115 #[test]
1116 fn test_credentials_identifier() {
1117 let creds = Credentials::ApiKey {
1118 key: "test_key".to_string(),
1119 ip_address: None,
1120 };
1121
1122 assert_eq!(creds.identifier(), "api_key:test_key");
1123 }
1124
1125 #[test]
1126 fn test_rbac_config_default_roles() {
1127 let rbac = RbacConfig::default();
1128 assert!(rbac.role_permissions.contains_key("admin"));
1129 assert!(rbac.role_permissions.contains_key("user"));
1130 assert!(rbac.role_permissions.contains_key("viewer"));
1131
1132 let admin_perms = rbac.role_permissions.get("admin").unwrap();
1133 assert!(admin_perms.contains(&Permission::Admin));
1134 assert!(admin_perms.contains(&Permission::Read));
1135 assert!(admin_perms.contains(&Permission::Write));
1136 }
1137
1138 #[cfg(not(feature = "post-quantum"))]
1145 #[tokio::test]
1146 async fn test_pq_dilithium_requires_feature_flag() {
1147 let config = PostQuantumConfig::default();
1148 let engine = PostQuantumCryptoEngine::new(config);
1149 let result = engine
1150 .generate_keypair(&PostQuantumSignature::Dilithium3)
1151 .await;
1152 assert!(result.is_err());
1153 let msg = result.unwrap_err().to_string();
1154 assert!(
1155 msg.contains("post-quantum"),
1156 "error should mention the feature flag, got: {msg}"
1157 );
1158 }
1159
1160 #[cfg(not(feature = "post-quantum"))]
1161 #[tokio::test]
1162 async fn test_pq_kyber_encapsulate_requires_feature_flag() {
1163 let config = PostQuantumConfig::default();
1164 let engine = PostQuantumCryptoEngine::new(config);
1165 let dummy_pk = vec![0u8; 32];
1166 let result = engine
1167 .encapsulate(&dummy_pk, &EncryptionAlgorithm::Kyber768)
1168 .await;
1169 assert!(result.is_err());
1170 let msg = result.unwrap_err().to_string();
1171 assert!(
1172 msg.contains("post-quantum"),
1173 "error should mention the feature flag, got: {msg}"
1174 );
1175 }
1176
1177 #[cfg(feature = "post-quantum")]
1179 #[tokio::test]
1180 async fn test_pq_dilithium3_round_trip() {
1181 let config = PostQuantumConfig::default();
1182 let engine = PostQuantumCryptoEngine::new(config);
1183
1184 let keypair = engine
1185 .generate_keypair(&PostQuantumSignature::Dilithium3)
1186 .await
1187 .expect("Dilithium3 key generation failed");
1188
1189 let data = b"OxiRS post-quantum signing test vector";
1190 let sig_bytes = engine
1191 .sign(&keypair.id, data)
1192 .await
1193 .expect("Dilithium3 signing failed");
1194
1195 let valid = engine
1196 .verify(
1197 &keypair.public_key,
1198 data,
1199 &sig_bytes,
1200 &PostQuantumSignature::Dilithium3,
1201 )
1202 .await
1203 .expect("Dilithium3 verification failed");
1204 assert!(valid, "valid Dilithium3 signature must verify as true");
1205
1206 let tampered = b"OxiRS post-quantum signing test vector TAMPERED";
1207 let invalid = engine
1208 .verify(
1209 &keypair.public_key,
1210 tampered,
1211 &sig_bytes,
1212 &PostQuantumSignature::Dilithium3,
1213 )
1214 .await
1215 .expect("Dilithium3 tampered-data verification call failed");
1216 assert!(
1217 !invalid,
1218 "Dilithium3 signature over tampered data must not verify"
1219 );
1220 }
1221
1222 #[cfg(feature = "post-quantum")]
1224 #[tokio::test]
1225 async fn test_pq_kyber768_kem_round_trip() {
1226 let config = PostQuantumConfig::default();
1227 let engine = PostQuantumCryptoEngine::new(config);
1228
1229 use pqcrypto_traits::kem::{PublicKey as _, SecretKey as _};
1231 let (pk, sk) = pqcrypto_kyber::kyber768::keypair();
1232 let pk_bytes = pk.as_bytes().to_vec();
1233 let sk_bytes = sk.as_bytes().to_vec();
1234
1235 let (ct, ss_enc) = engine
1237 .encapsulate(&pk_bytes, &EncryptionAlgorithm::Kyber768)
1238 .await
1239 .expect("Kyber768 encapsulation failed");
1240
1241 let ss_dec = engine
1243 .decapsulate(&sk_bytes, &ct, &EncryptionAlgorithm::Kyber768)
1244 .await
1245 .expect("Kyber768 decapsulation failed");
1246
1247 assert_eq!(
1248 ss_enc, ss_dec,
1249 "encapsulated and decapsulated shared secrets must match"
1250 );
1251 assert_eq!(ss_enc.len(), 32, "Kyber768 shared secret must be 32 bytes");
1252 }
1253
1254 #[cfg(feature = "post-quantum")]
1256 #[tokio::test]
1257 async fn test_pq_sphincsplus_sha2_256s_round_trip() {
1258 let config = PostQuantumConfig::default();
1259 let engine = PostQuantumCryptoEngine::new(config);
1260
1261 let keypair = engine
1262 .generate_keypair(&PostQuantumSignature::SphincsPlusSha2256s)
1263 .await
1264 .expect("SPHINCS+ SHA2-256s key generation failed");
1265
1266 let data = b"SPHINCS+ signing test vector";
1267 let sig_bytes = engine
1268 .sign(&keypair.id, data)
1269 .await
1270 .expect("SPHINCS+ signing failed");
1271
1272 let valid = engine
1273 .verify(
1274 &keypair.public_key,
1275 data,
1276 &sig_bytes,
1277 &PostQuantumSignature::SphincsPlusSha2256s,
1278 )
1279 .await
1280 .expect("SPHINCS+ verification failed");
1281 assert!(valid, "valid SPHINCS+ signature must verify");
1282 }
1283
1284 #[cfg(not(feature = "post-quantum"))]
1286 #[tokio::test]
1287 async fn test_pq_falcon_requires_feature_flag() {
1288 let config = PostQuantumConfig::default();
1289 let engine = PostQuantumCryptoEngine::new(config);
1290 let result = engine
1291 .generate_keypair(&PostQuantumSignature::Falcon1024)
1292 .await;
1293 assert!(result.is_err());
1294 let msg = result.unwrap_err().to_string();
1295 assert!(
1296 msg.contains("post-quantum"),
1297 "error should mention the feature flag, got: {msg}"
1298 );
1299 }
1300
1301 #[cfg(feature = "post-quantum")]
1303 #[tokio::test]
1304 async fn test_pq_falcon512_round_trip() {
1305 let config = PostQuantumConfig::default();
1306 let engine = PostQuantumCryptoEngine::new(config);
1307
1308 let keypair = engine
1309 .generate_keypair(&PostQuantumSignature::Falcon512)
1310 .await
1311 .expect("Falcon512 key generation failed");
1312
1313 let data = b"OxiRS Falcon512 signing test vector";
1314
1315 let sig_bytes = engine
1316 .sign(&keypair.id, data)
1317 .await
1318 .expect("Falcon512 signing failed");
1319 assert!(
1320 !sig_bytes.is_empty(),
1321 "Falcon512 signature must not be empty"
1322 );
1323
1324 let valid = engine
1326 .verify(
1327 &keypair.public_key,
1328 data,
1329 &sig_bytes,
1330 &PostQuantumSignature::Falcon512,
1331 )
1332 .await
1333 .expect("Falcon512 verification failed");
1334 assert!(valid, "valid Falcon512 signature must verify as true");
1335
1336 let tampered = b"OxiRS Falcon512 signing test vector TAMPERED";
1338 let invalid = engine
1339 .verify(
1340 &keypair.public_key,
1341 tampered,
1342 &sig_bytes,
1343 &PostQuantumSignature::Falcon512,
1344 )
1345 .await
1346 .expect("Falcon512 tampered-data verification call failed");
1347 assert!(
1348 !invalid,
1349 "Falcon512 signature over tampered data must not verify"
1350 );
1351 }
1352
1353 #[cfg(feature = "post-quantum")]
1355 #[tokio::test]
1356 async fn test_pq_falcon1024_round_trip() {
1357 let config = PostQuantumConfig::default();
1358 let engine = PostQuantumCryptoEngine::new(config);
1359
1360 let keypair = engine
1361 .generate_keypair(&PostQuantumSignature::Falcon1024)
1362 .await
1363 .expect("Falcon1024 key generation failed");
1364
1365 let data = b"OxiRS Falcon1024 signing test vector";
1366
1367 let sig_bytes = engine
1368 .sign(&keypair.id, data)
1369 .await
1370 .expect("Falcon1024 signing failed");
1371 assert!(
1372 !sig_bytes.is_empty(),
1373 "Falcon1024 signature must not be empty"
1374 );
1375
1376 let valid = engine
1378 .verify(
1379 &keypair.public_key,
1380 data,
1381 &sig_bytes,
1382 &PostQuantumSignature::Falcon1024,
1383 )
1384 .await
1385 .expect("Falcon1024 verification failed");
1386 assert!(valid, "valid Falcon1024 signature must verify as true");
1387
1388 let tampered = b"OxiRS Falcon1024 signing test vector TAMPERED";
1390 let invalid = engine
1391 .verify(
1392 &keypair.public_key,
1393 tampered,
1394 &sig_bytes,
1395 &PostQuantumSignature::Falcon1024,
1396 )
1397 .await
1398 .expect("Falcon1024 tampered-data verification call failed");
1399 assert!(
1400 !invalid,
1401 "Falcon1024 signature over tampered data must not verify"
1402 );
1403 }
1404
1405 #[tokio::test]
1407 async fn test_pq_newhope_not_yet_supported() {
1408 let config = PostQuantumConfig::default();
1409 let engine = PostQuantumCryptoEngine::new(config);
1410 let dummy_pk = vec![0u8; 32];
1411 let result = engine
1412 .encapsulate(&dummy_pk, &EncryptionAlgorithm::NewHope1024)
1413 .await;
1414 assert!(result.is_err());
1415 let msg = result.unwrap_err().to_string();
1416 assert!(
1417 msg.contains("NewHope"),
1418 "error should mention NewHope, got: {msg}"
1419 );
1420 }
1421
1422 #[tokio::test]
1423 async fn test_pq_frodokem_not_yet_supported() {
1424 let config = PostQuantumConfig::default();
1425 let engine = PostQuantumCryptoEngine::new(config);
1426 let dummy_pk = vec![0u8; 32];
1427 let result = engine
1428 .encapsulate(&dummy_pk, &EncryptionAlgorithm::FrodoKEM976)
1429 .await;
1430 assert!(result.is_err());
1431 let msg = result.unwrap_err().to_string();
1432 assert!(
1433 msg.contains("FrodoKEM"),
1434 "error should mention FrodoKEM, got: {msg}"
1435 );
1436 }
1437}