1use super::*;
24use anyhow::Result;
25use serde::{Deserialize, Serialize};
26use sha2::{Digest, Sha256};
27use std::{
28 collections::{HashMap, HashSet, VecDeque},
29 net::IpAddr,
30 sync::Arc,
31 time::{Duration, Instant, SystemTime},
32};
33use tokio::sync::{Mutex, RwLock};
34
35#[derive(Debug, Clone, Default)]
37pub struct SecurityConfig {
38 pub rate_limit: RateLimitConfig,
40
41 pub blacklist: BlacklistConfig,
43
44 pub eclipse_detection: EclipseDetectionConfig,
46
47 pub integrity: IntegrityConfig,
49
50 pub audit: AuditConfig,
52}
53
54#[derive(Debug, Clone)]
56pub struct RateLimitConfig {
57 pub node_requests_per_window: u32,
59
60 pub ip_requests_per_window: u32,
62
63 pub window_duration: Duration,
65
66 pub max_connections_per_node: u32,
68
69 pub max_joins_per_hour: u32,
71}
72
73impl Default for RateLimitConfig {
74 fn default() -> Self {
75 Self {
76 node_requests_per_window: 100,
77 ip_requests_per_window: 500,
78 window_duration: Duration::from_secs(60),
79 max_connections_per_node: 10,
80 max_joins_per_hour: 20,
81 }
82 }
83}
84
85#[derive(Debug, Clone)]
87pub struct BlacklistConfig {
88 pub entry_ttl: Duration,
90
91 pub max_entries: usize,
93
94 pub violation_threshold: u32,
96}
97
98impl Default for BlacklistConfig {
99 fn default() -> Self {
100 Self {
101 entry_ttl: Duration::from_secs(86400), max_entries: 10000,
103 violation_threshold: 3,
104 }
105 }
106}
107
108#[derive(Debug, Clone)]
110pub struct EclipseDetectionConfig {
111 pub min_diversity_score: f64,
113
114 pub max_subnet_ratio: f64,
116
117 pub pattern_threshold: f64,
119}
120
121impl Default for EclipseDetectionConfig {
122 fn default() -> Self {
123 Self {
124 min_diversity_score: 0.5,
125 max_subnet_ratio: 0.2,
126 pattern_threshold: 0.7,
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
133pub struct IntegrityConfig {
134 pub verify_content_hash: bool,
136
137 pub require_signatures: bool,
139
140 pub max_message_size: usize,
142}
143
144impl Default for IntegrityConfig {
145 fn default() -> Self {
146 Self {
147 verify_content_hash: true,
148 require_signatures: true,
149 max_message_size: 10 * 1024 * 1024, }
151 }
152}
153
154#[derive(Debug, Clone)]
156pub struct AuditConfig {
157 pub enabled: bool,
159
160 pub log_security_events: bool,
162
163 pub log_rate_limits: bool,
165
166 pub retention_days: u32,
168}
169
170impl Default for AuditConfig {
171 fn default() -> Self {
172 Self {
173 enabled: true,
174 log_security_events: true,
175 log_rate_limits: true,
176 retention_days: 30,
177 }
178 }
179}
180
181pub struct SecurityManager {
183 config: SecurityConfig,
185
186 rate_limiter: Arc<RateLimiter>,
188
189 blacklist: Arc<BlacklistManager>,
191
192 eclipse_detector: Arc<EclipseDetector>,
194
195 integrity_verifier: Arc<IntegrityVerifier>,
197
198 auditor: Arc<SecurityAuditor>,
200
201 _identity: NodeIdentity,
203}
204
205pub struct RateLimiter {
207 config: RateLimitConfig,
209
210 node_requests: Arc<RwLock<HashMap<NodeId, RequestWindow>>>,
212
213 ip_requests: Arc<RwLock<HashMap<IpAddr, RequestWindow>>>,
215
216 _connections: Arc<RwLock<HashMap<NodeId, u32>>>,
218
219 join_requests: Arc<RwLock<VecDeque<Instant>>>,
221}
222
223#[derive(Debug, Clone)]
225struct RequestWindow {
226 count: u32,
228
229 window_start: Instant,
231}
232
233pub struct BlacklistManager {
235 config: BlacklistConfig,
237
238 blacklist: Arc<RwLock<HashMap<NodeId, BlacklistEntry>>>,
240
241 violations: Arc<RwLock<HashMap<NodeId, u32>>>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct BlacklistEntry {
248 pub node_id: NodeId,
250
251 pub reason: BlacklistReason,
253
254 pub timestamp: SystemTime,
256
257 pub reporter: Option<NodeId>,
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
263pub enum BlacklistReason {
264 RateLimitViolation,
266
267 MaliciousBehavior(String),
269
270 EclipseAttack,
272
273 DataCorruption,
275
276 InvalidCrypto,
278
279 Manual(String),
281}
282
283pub struct EclipseDetector {
285 config: EclipseDetectionConfig,
287
288 patterns: Arc<RwLock<AnomalyPatterns>>,
290}
291
292#[derive(Debug, Default)]
294struct AnomalyPatterns {
295 _rapid_connections: HashMap<NodeId, Vec<Instant>>,
297
298 subnet_distribution: HashMap<String, u32>,
300
301 routing_anomalies: Vec<RoutingAnomaly>,
303}
304
305#[derive(Debug, Clone)]
307struct RoutingAnomaly {
308 _node_id: NodeId,
310
311 _anomaly_type: AnomalyType,
313
314 timestamp: Instant,
316}
317
318#[derive(Debug, Clone)]
320pub enum AnomalyType {
321 SubnetConcentration,
323
324 RapidChurn,
326
327 ConnectionPattern,
329
330 CoordinatedActivity,
332}
333
334pub struct IntegrityVerifier {
336 _config: IntegrityConfig,
338
339 stats: Arc<RwLock<VerificationStats>>,
341}
342
343#[derive(Debug, Default)]
345struct VerificationStats {
346 total_verified: u64,
348
349 failed_verifications: u64,
351
352 invalid_hashes: u64,
354
355 _invalid_signatures: u64,
357}
358
359pub struct SecurityAuditor {
361 config: AuditConfig,
363
364 audit_log: Arc<Mutex<VecDeque<AuditEntry>>>,
366
367 event_counts: Arc<RwLock<HashMap<String, u64>>>,
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct AuditEntry {
374 pub timestamp: SystemTime,
376
377 pub event_type: SecurityEvent,
379
380 pub node_id: Option<NodeId>,
382
383 pub details: String,
385
386 pub severity: Severity,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
392pub enum SecurityEvent {
393 RateLimitExceeded,
395
396 NodeBlacklisted,
398
399 EclipseAttackDetected,
401
402 IntegrityFailure,
404
405 AuthenticationFailure,
407
408 SuspiciousActivity,
410
411 ConfigurationChange,
413}
414
415#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
417pub enum Severity {
418 Debug,
419 Info,
420 Warning,
421 Error,
422 Critical,
423}
424
425#[derive(Debug, thiserror::Error)]
427pub enum SecurityError {
428 #[error("Rate limit exceeded")]
429 RateLimitExceeded,
430
431 #[error("Node is blacklisted")]
432 Blacklisted,
433
434
435 #[error("Invalid cryptographic identity")]
436 InvalidIdentity,
437
438 #[error("Data integrity check failed")]
439 IntegrityCheckFailed,
440
441 #[error("Eclipse attack detected")]
442 EclipseAttackDetected,
443
444 #[error("Message too large")]
445 MessageTooLarge,
446
447 #[error("Invalid signature")]
448 InvalidSignature,
449}
450
451impl SecurityManager {
452 pub fn new(config: SecurityConfig, identity: NodeIdentity) -> Self {
454 let rate_limiter = Arc::new(RateLimiter::new(config.rate_limit.clone()));
455 let blacklist = Arc::new(BlacklistManager::new(config.blacklist.clone()));
456 let eclipse_detector = Arc::new(EclipseDetector::new(config.eclipse_detection.clone()));
457 let integrity_verifier = Arc::new(IntegrityVerifier::new(config.integrity.clone()));
458 let auditor = Arc::new(SecurityAuditor::new(config.audit.clone()));
459
460 Self {
461 config,
462 rate_limiter,
463 blacklist,
464 eclipse_detector,
465 integrity_verifier,
466 auditor,
467 _identity: identity,
468 }
469 }
470
471 pub async fn validate_node_join(&self, node: &NodeDescriptor) -> Result<(), SecurityError> {
473 if self.blacklist.is_blacklisted(&node.id).await {
475 self.auditor
476 .log_event(
477 SecurityEvent::NodeBlacklisted,
478 Some(node.id.clone()),
479 "Node attempted to join while blacklisted".to_string(),
480 Severity::Warning,
481 )
482 .await;
483 return Err(SecurityError::Blacklisted);
484 }
485
486 if !self.rate_limiter.check_join_rate().await {
488 self.auditor
489 .log_event(
490 SecurityEvent::RateLimitExceeded,
491 Some(node.id.clone()),
492 "Join rate limit exceeded".to_string(),
493 Severity::Warning,
494 )
495 .await;
496 return Err(SecurityError::RateLimitExceeded);
497 }
498
499 if !self.verify_identity(node).await {
501 return Err(SecurityError::InvalidIdentity);
502 }
503
504 Ok(())
505 }
506
507 pub async fn check_rate_limit(
509 &self,
510 node_id: &NodeId,
511 ip: Option<IpAddr>,
512 ) -> Result<(), SecurityError> {
513 if !self.rate_limiter.check_node_rate(node_id).await {
515 self.blacklist
516 .record_violation(node_id, BlacklistReason::RateLimitViolation)
517 .await;
518 self.auditor
519 .log_event(
520 SecurityEvent::RateLimitExceeded,
521 Some(node_id.clone()),
522 "Node request rate limit exceeded".to_string(),
523 Severity::Warning,
524 )
525 .await;
526 return Err(SecurityError::RateLimitExceeded);
527 }
528
529 if let Some(ip_addr) = ip
531 && !self.rate_limiter.check_ip_rate(&ip_addr).await
532 {
533 self.auditor
534 .log_event(
535 SecurityEvent::RateLimitExceeded,
536 None,
537 format!("IP {ip_addr} rate limit exceeded"),
538 Severity::Warning,
539 )
540 .await;
541 return Err(SecurityError::RateLimitExceeded);
542 }
543
544 Ok(())
545 }
546
547 pub async fn detect_eclipse_attack(
549 &self,
550 routing_table: &[NodeId],
551 ) -> Result<(), SecurityError> {
552 let diversity_score = self
553 .eclipse_detector
554 .calculate_diversity_score(routing_table)
555 .await;
556
557 if diversity_score < self.config.eclipse_detection.min_diversity_score {
558 self.auditor
559 .log_event(
560 SecurityEvent::EclipseAttackDetected,
561 None,
562 format!("Low routing table diversity: {diversity_score:.2}"),
563 Severity::Critical,
564 )
565 .await;
566 return Err(SecurityError::EclipseAttackDetected);
567 }
568
569 if self
570 .eclipse_detector
571 .detect_suspicious_patterns(routing_table)
572 .await
573 {
574 self.auditor
575 .log_event(
576 SecurityEvent::EclipseAttackDetected,
577 None,
578 "Suspicious routing patterns detected".to_string(),
579 Severity::Critical,
580 )
581 .await;
582 return Err(SecurityError::EclipseAttackDetected);
583 }
584
585 Ok(())
586 }
587
588 pub async fn verify_message_integrity(
590 &self,
591 message: &[u8],
592 hash: &[u8],
593 signature: Option<&[u8]>,
594 ) -> Result<(), SecurityError> {
595 if message.len() > self.config.integrity.max_message_size {
597 return Err(SecurityError::MessageTooLarge);
598 }
599
600 if self.config.integrity.verify_content_hash
602 && !self.integrity_verifier.verify_hash(message, hash).await
603 {
604 self.auditor
605 .log_event(
606 SecurityEvent::IntegrityFailure,
607 None,
608 "Content hash verification failed".to_string(),
609 Severity::Error,
610 )
611 .await;
612 return Err(SecurityError::IntegrityCheckFailed);
613 }
614
615 if self.config.integrity.require_signatures {
617 if let Some(sig) = signature {
618 if !self.integrity_verifier.verify_signature(message, sig).await {
619 self.auditor
620 .log_event(
621 SecurityEvent::IntegrityFailure,
622 None,
623 "Message signature verification failed".to_string(),
624 Severity::Error,
625 )
626 .await;
627 return Err(SecurityError::InvalidSignature);
628 }
629 } else {
630 return Err(SecurityError::InvalidSignature);
631 }
632 }
633
634 Ok(())
635 }
636
637 pub async fn blacklist_node(&self, node_id: NodeId, reason: BlacklistReason) {
639 self.blacklist
640 .add_entry(node_id.clone(), reason.clone())
641 .await;
642
643 self.auditor
644 .log_event(
645 SecurityEvent::NodeBlacklisted,
646 Some(node_id),
647 format!("Node blacklisted: {reason:?}"),
648 Severity::Warning,
649 )
650 .await;
651 }
652
653 pub async fn get_metrics(&self) -> SecurityMetrics {
655 SecurityMetrics {
656 rate_limit_violations: self.rate_limiter.get_violation_count().await,
657 blacklisted_nodes: self.blacklist.get_blacklist_size().await,
658 verification_failures: self.integrity_verifier.get_failure_count().await,
659 eclipse_detections: self.eclipse_detector.get_detection_count().await,
660 audit_entries: self.auditor.get_entry_count().await,
661 }
662 }
663
664 async fn verify_identity(&self, _node: &NodeDescriptor) -> bool {
666 true
669 }
670}
671
672impl RateLimiter {
673 pub fn new(config: RateLimitConfig) -> Self {
675 Self {
676 config,
677 node_requests: Arc::new(RwLock::new(HashMap::new())),
678 ip_requests: Arc::new(RwLock::new(HashMap::new())),
679 _connections: Arc::new(RwLock::new(HashMap::new())),
680 join_requests: Arc::new(RwLock::new(VecDeque::new())),
681 }
682 }
683
684 pub async fn check_node_rate(&self, node_id: &NodeId) -> bool {
686 let mut requests = self.node_requests.write().await;
687 let now = Instant::now();
688
689 let window = requests.entry(node_id.clone()).or_insert(RequestWindow {
690 count: 0,
691 window_start: now,
692 });
693
694 if now.duration_since(window.window_start) > self.config.window_duration {
696 window.count = 0;
697 window.window_start = now;
698 }
699
700 if window.count < self.config.node_requests_per_window {
702 window.count += 1;
703 true
704 } else {
705 false
706 }
707 }
708
709 pub async fn check_ip_rate(&self, ip: &IpAddr) -> bool {
711 let mut requests = self.ip_requests.write().await;
712 let now = Instant::now();
713
714 let window = requests.entry(*ip).or_insert(RequestWindow {
715 count: 0,
716 window_start: now,
717 });
718
719 if now.duration_since(window.window_start) > self.config.window_duration {
721 window.count = 0;
722 window.window_start = now;
723 }
724
725 if window.count < self.config.ip_requests_per_window {
727 window.count += 1;
728 true
729 } else {
730 false
731 }
732 }
733
734 pub async fn check_join_rate(&self) -> bool {
736 let mut join_requests = self.join_requests.write().await;
737 let now = Instant::now();
738 let hour_ago = now - Duration::from_secs(3600);
739
740 while let Some(front) = join_requests.front() {
742 if *front < hour_ago {
743 join_requests.pop_front();
744 } else {
745 break;
746 }
747 }
748
749 if join_requests.len() < self.config.max_joins_per_hour as usize {
751 join_requests.push_back(now);
752 true
753 } else {
754 false
755 }
756 }
757
758 pub async fn get_violation_count(&self) -> u64 {
760 0
762 }
763}
764
765impl BlacklistManager {
766 pub fn new(config: BlacklistConfig) -> Self {
768 Self {
769 config,
770 blacklist: Arc::new(RwLock::new(HashMap::new())),
771 violations: Arc::new(RwLock::new(HashMap::new())),
772 }
773 }
774
775 pub async fn is_blacklisted(&self, node_id: &NodeId) -> bool {
777 let blacklist = self.blacklist.read().await;
778
779 if let Some(entry) = blacklist.get(node_id) {
780 let now = SystemTime::now();
782 let elapsed = now
783 .duration_since(entry.timestamp)
784 .unwrap_or(Duration::ZERO);
785
786 elapsed < self.config.entry_ttl
787 } else {
788 false
789 }
790 }
791
792 pub async fn add_entry(&self, node_id: NodeId, reason: BlacklistReason) {
794 let mut blacklist = self.blacklist.write().await;
795
796 if blacklist.len() >= self.config.max_entries {
798 if let Some(oldest) = blacklist
800 .iter()
801 .min_by_key(|(_, entry)| entry.timestamp)
802 .map(|(id, _)| id.clone())
803 {
804 blacklist.remove(&oldest);
805 }
806 }
807
808 blacklist.insert(
809 node_id.clone(),
810 BlacklistEntry {
811 node_id,
812 reason,
813 timestamp: SystemTime::now(),
814 reporter: None,
815 },
816 );
817 }
818
819 pub async fn record_violation(&self, node_id: &NodeId, reason: BlacklistReason) {
821 let mut violations = self.violations.write().await;
822 let count = violations.entry(node_id.clone()).or_insert(0);
823 *count += 1;
824
825 if *count >= self.config.violation_threshold {
827 drop(violations);
828 self.add_entry(node_id.clone(), reason).await;
829 }
830 }
831
832 pub async fn get_blacklist_size(&self) -> usize {
834 self.blacklist.read().await.len()
835 }
836
837 pub async fn export_blacklist(&self) -> Vec<BlacklistEntry> {
839 let blacklist = self.blacklist.read().await;
840 let now = SystemTime::now();
841
842 blacklist
843 .values()
844 .filter(|entry| {
845 let elapsed = now
846 .duration_since(entry.timestamp)
847 .unwrap_or(Duration::ZERO);
848 elapsed < self.config.entry_ttl
849 })
850 .cloned()
851 .collect()
852 }
853
854 pub async fn import_blacklist(&self, entries: Vec<BlacklistEntry>) {
856 let mut blacklist = self.blacklist.write().await;
857
858 for entry in entries {
859 match blacklist.get(&entry.node_id) {
861 Some(existing) if existing.timestamp >= entry.timestamp => continue,
862 _ => {
863 blacklist.insert(entry.node_id.clone(), entry);
864 }
865 }
866 }
867 }
868}
869
870impl EclipseDetector {
871 pub fn new(config: EclipseDetectionConfig) -> Self {
873 Self {
874 config,
875 patterns: Arc::new(RwLock::new(AnomalyPatterns::default())),
876 }
877 }
878
879 pub async fn calculate_diversity_score(&self, routing_table: &[NodeId]) -> f64 {
881 if routing_table.is_empty() {
882 return 0.0;
883 }
884
885 let mut prefixes = HashSet::new();
887 for node_id in routing_table {
888 let prefix = &node_id.hash[..4];
890 prefixes.insert(prefix.to_vec());
891 }
892
893 prefixes.len() as f64 / routing_table.len() as f64
895 }
896
897 pub async fn detect_suspicious_patterns(&self, routing_table: &[NodeId]) -> bool {
899 let mut patterns = self.patterns.write().await;
900
901 patterns.subnet_distribution.clear();
905
906 for node_id in routing_table {
907 let subnet = format!("{:02x}{:02x}", node_id.hash[0], node_id.hash[1]);
908 *patterns.subnet_distribution.entry(subnet).or_insert(0) += 1;
909 }
910
911 let max_allowed = (routing_table.len() as f64 * self.config.max_subnet_ratio) as u32;
913 for count in patterns.subnet_distribution.values() {
914 if *count > max_allowed {
915 return true;
916 }
917 }
918
919 false
920 }
921
922 pub async fn record_anomaly(&self, node_id: NodeId, anomaly_type: AnomalyType) {
924 let mut patterns = self.patterns.write().await;
925
926 patterns.routing_anomalies.push(RoutingAnomaly {
927 _node_id: node_id,
928 _anomaly_type: anomaly_type,
929 timestamp: Instant::now(),
930 });
931
932 let cutoff = Instant::now() - Duration::from_secs(3600);
934 patterns.routing_anomalies.retain(|a| a.timestamp > cutoff);
935 }
936
937 pub async fn get_detection_count(&self) -> u64 {
939 self.patterns.read().await.routing_anomalies.len() as u64
940 }
941}
942
943impl IntegrityVerifier {
944 pub fn new(config: IntegrityConfig) -> Self {
946 Self {
947 _config: config,
948 stats: Arc::new(RwLock::new(VerificationStats::default())),
949 }
950 }
951
952 pub async fn verify_hash(&self, content: &[u8], expected_hash: &[u8]) -> bool {
954 let mut stats = self.stats.write().await;
955 stats.total_verified += 1;
956
957 let mut hasher = Sha256::new();
958 hasher.update(content);
959 let computed_hash = hasher.finalize();
960
961 if computed_hash.as_slice() == expected_hash {
962 true
963 } else {
964 stats.failed_verifications += 1;
965 stats.invalid_hashes += 1;
966 false
967 }
968 }
969
970 pub async fn verify_signature(&self, _message: &[u8], _signature: &[u8]) -> bool {
972 true
975 }
976
977 pub async fn get_failure_count(&self) -> u64 {
979 self.stats.read().await.failed_verifications
980 }
981}
982
983impl SecurityAuditor {
984 pub fn new(config: AuditConfig) -> Self {
986 Self {
987 config,
988 audit_log: Arc::new(Mutex::new(VecDeque::new())),
989 event_counts: Arc::new(RwLock::new(HashMap::new())),
990 }
991 }
992
993 pub async fn log_event(
995 &self,
996 event_type: SecurityEvent,
997 node_id: Option<NodeId>,
998 details: String,
999 severity: Severity,
1000 ) {
1001 if !self.config.enabled {
1002 return;
1003 }
1004
1005 let entry = AuditEntry {
1006 timestamp: SystemTime::now(),
1007 event_type: event_type.clone(),
1008 node_id,
1009 details,
1010 severity,
1011 };
1012
1013 let mut log = self.audit_log.lock().await;
1015 log.push_back(entry);
1016
1017 let retention_duration = Duration::from_secs(self.config.retention_days as u64 * 86400);
1019 let cutoff = SystemTime::now() - retention_duration;
1020
1021 while let Some(front) = log.front() {
1022 if front.timestamp < cutoff {
1023 log.pop_front();
1024 } else {
1025 break;
1026 }
1027 }
1028
1029 let event_name = format!("{event_type:?}");
1031 let mut counts = self.event_counts.write().await;
1032 *counts.entry(event_name).or_insert(0) += 1;
1033 }
1034
1035 pub async fn get_entries(
1037 &self,
1038 since: Option<SystemTime>,
1039 severity_filter: Option<Severity>,
1040 ) -> Vec<AuditEntry> {
1041 let log = self.audit_log.lock().await;
1042
1043 log.iter()
1044 .filter(|entry| {
1045 if let Some(min_time) = since
1046 && entry.timestamp < min_time
1047 {
1048 return false;
1049 }
1050 if let Some(min_severity) = severity_filter
1051 && (entry.severity as u8) < (min_severity as u8)
1052 {
1053 return false;
1054 }
1055 true
1056 })
1057 .cloned()
1058 .collect()
1059 }
1060
1061 pub async fn get_entry_count(&self) -> u64 {
1063 self.audit_log.lock().await.len() as u64
1064 }
1065
1066 pub async fn export_report(&self) -> AuditReport {
1068 let entries = self.get_entries(None, None).await;
1069 let event_counts = self.event_counts.read().await.clone();
1070
1071 AuditReport {
1072 generated_at: SystemTime::now(),
1073 total_entries: entries.len(),
1074 event_counts,
1075 severity_breakdown: self.calculate_severity_breakdown(&entries),
1076 recent_critical_events: entries
1077 .iter()
1078 .filter(|e| e.severity == Severity::Critical)
1079 .take(10)
1080 .cloned()
1081 .collect(),
1082 }
1083 }
1084
1085 fn calculate_severity_breakdown(&self, entries: &[AuditEntry]) -> HashMap<Severity, u64> {
1087 let mut breakdown = HashMap::new();
1088
1089 for entry in entries {
1090 *breakdown.entry(entry.severity).or_insert(0) += 1;
1091 }
1092
1093 breakdown
1094 }
1095}
1096
1097#[derive(Debug, Clone, Default)]
1099pub struct SecurityMetrics {
1100 pub rate_limit_violations: u64,
1101 pub blacklisted_nodes: usize,
1102 pub verification_failures: u64,
1103 pub eclipse_detections: u64,
1104 pub audit_entries: u64,
1105}
1106
1107#[derive(Debug, Clone, Serialize, Deserialize)]
1109pub struct AuditReport {
1110 pub generated_at: SystemTime,
1111 pub total_entries: usize,
1112 pub event_counts: HashMap<String, u64>,
1113 pub severity_breakdown: HashMap<Severity, u64>,
1114 pub recent_critical_events: Vec<AuditEntry>,
1115}
1116
1117#[cfg(test)]
1118mod tests {
1119 use super::*;
1120
1121 #[tokio::test]
1122 async fn test_rate_limiter_node_limits() {
1123 let config = RateLimitConfig {
1124 node_requests_per_window: 5,
1125 window_duration: Duration::from_secs(1),
1126 ..Default::default()
1127 };
1128
1129 let limiter = RateLimiter::new(config);
1130 let node_id = NodeId { hash: [1u8; 32] };
1131
1132 for _ in 0..5 {
1134 assert!(limiter.check_node_rate(&node_id).await);
1135 }
1136
1137 assert!(!limiter.check_node_rate(&node_id).await);
1139
1140 tokio::time::sleep(Duration::from_secs(2)).await;
1142
1143 assert!(limiter.check_node_rate(&node_id).await);
1145 }
1146
1147 #[tokio::test]
1148 async fn test_blacklist_management() {
1149 let config = BlacklistConfig::default();
1150 let blacklist = BlacklistManager::new(config);
1151
1152 let node_id = NodeId { hash: [2u8; 32] };
1153
1154 assert!(!blacklist.is_blacklisted(&node_id).await);
1156
1157 blacklist
1159 .add_entry(
1160 node_id.clone(),
1161 BlacklistReason::MaliciousBehavior("Test".to_string()),
1162 )
1163 .await;
1164
1165 assert!(blacklist.is_blacklisted(&node_id).await);
1167
1168 assert_eq!(blacklist.get_blacklist_size().await, 1);
1170 }
1171
1172 #[tokio::test]
1173 async fn test_eclipse_detection() {
1174 let config = EclipseDetectionConfig {
1175 min_diversity_score: 0.5,
1176 max_subnet_ratio: 0.3,
1177 pattern_threshold: 0.7,
1178 };
1179
1180 let detector = EclipseDetector::new(config);
1181
1182 let mut routing_table = vec![];
1184 for i in 0..10 {
1185 let mut hash = [0u8; 32];
1186 hash[0] = 1; hash[31] = i;
1188 routing_table.push(NodeId { hash });
1189 }
1190
1191 let score = detector.calculate_diversity_score(&routing_table).await;
1193 assert!(score < 0.5);
1194
1195 let mut diverse_table = vec![];
1197 for i in 0..10 {
1198 let mut hash = [0u8; 32];
1199 hash[0] = i * 25; diverse_table.push(NodeId { hash });
1201 }
1202
1203 let diverse_score = detector.calculate_diversity_score(&diverse_table).await;
1205 assert!(diverse_score > 0.8);
1206 }
1207
1208 #[tokio::test]
1209 async fn test_integrity_verification() {
1210 let config = IntegrityConfig::default();
1211 let verifier = IntegrityVerifier::new(config);
1212
1213 let content = b"Test content";
1214 let mut hasher = Sha256::new();
1215 hasher.update(content);
1216 let correct_hash = hasher.finalize();
1217
1218 assert!(verifier.verify_hash(content, &correct_hash).await);
1220
1221 let wrong_hash = [0u8; 32];
1223 assert!(!verifier.verify_hash(content, &wrong_hash).await);
1224
1225 assert_eq!(verifier.get_failure_count().await, 1);
1227 }
1228
1229 #[tokio::test]
1230 async fn test_security_auditor() {
1231 let config = AuditConfig::default();
1232 let auditor = SecurityAuditor::new(config);
1233
1234 auditor
1236 .log_event(
1237 SecurityEvent::RateLimitExceeded,
1238 None,
1239 "Test rate limit".to_string(),
1240 Severity::Warning,
1241 )
1242 .await;
1243
1244 auditor
1245 .log_event(
1246 SecurityEvent::EclipseAttackDetected,
1247 None,
1248 "Test eclipse attack".to_string(),
1249 Severity::Critical,
1250 )
1251 .await;
1252
1253 assert_eq!(auditor.get_entry_count().await, 2);
1255
1256 let entries = auditor.get_entries(None, Some(Severity::Critical)).await;
1258 assert_eq!(entries.len(), 1);
1259 assert_eq!(entries[0].severity, Severity::Critical);
1260
1261 let report = auditor.export_report().await;
1263 assert_eq!(report.total_entries, 2);
1264 assert_eq!(report.recent_critical_events.len(), 1);
1265 }
1266
1267 #[tokio::test]
1268 async fn test_security_manager_integration() {
1269 let config = SecurityConfig::default();
1270 let identity = NodeIdentity::generate().unwrap();
1271 let manager = SecurityManager::new(config, identity);
1272
1273 let node = NodeDescriptor {
1275 id: NodeId { hash: [3u8; 32] },
1276 public_key: ed25519_dalek::VerifyingKey::from_bytes(&[0u8; 32]).unwrap(),
1277 addresses: vec![], hyperbolic: None,
1279 som_position: None,
1280 trust: 0.5,
1281 capabilities: NodeCapabilities {
1282 storage: 100,
1283 compute: 50,
1284 bandwidth: 10,
1285 },
1286 };
1287
1288 assert!(manager.validate_node_join(&node).await.is_ok());
1290
1291 manager
1293 .blacklist_node(node.id.clone(), BlacklistReason::Manual("Test".to_string()))
1294 .await;
1295
1296 assert!(matches!(
1298 manager.validate_node_join(&node).await,
1299 Err(SecurityError::Blacklisted)
1300 ));
1301
1302 let metrics = manager.get_metrics().await;
1304 assert_eq!(metrics.blacklisted_nodes, 1);
1305 assert!(metrics.audit_entries > 0);
1306 }
1307}