saorsa_core/adaptive/
security.rs

1// Copyright 2024 Saorsa Labs Limited
2//
3// This software is dual-licensed under:
4// - GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later)
5// - Commercial License
6//
7// For AGPL-3.0 license, see LICENSE-AGPL-3.0
8// For commercial licensing, contact: saorsalabs@gmail.com
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under these licenses is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
14//! Security hardening for the adaptive P2P network
15//!
16//! This module implements comprehensive security measures including:
17//! - Rate limiting to prevent DoS attacks
18//! - Blacklist management for malicious nodes
19//! - Eclipse attack detection
20//! - Data integrity verification
21//! - Security audit tools
22
23use 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/// Security configuration
36#[derive(Debug, Clone, Default)]
37pub struct SecurityConfig {
38    /// Rate limiting configuration
39    pub rate_limit: RateLimitConfig,
40
41    /// Blacklist configuration
42    pub blacklist: BlacklistConfig,
43
44    /// Eclipse detection configuration
45    pub eclipse_detection: EclipseDetectionConfig,
46
47    /// Data integrity configuration
48    pub integrity: IntegrityConfig,
49
50    /// Audit configuration
51    pub audit: AuditConfig,
52}
53
54/// Rate limiting configuration
55#[derive(Debug, Clone)]
56pub struct RateLimitConfig {
57    /// Max requests per node per window
58    pub node_requests_per_window: u32,
59
60    /// Max requests per IP per window
61    pub ip_requests_per_window: u32,
62
63    /// Time window for rate limiting
64    pub window_duration: Duration,
65
66    /// Max concurrent connections per node
67    pub max_connections_per_node: u32,
68
69    /// Max join requests per hour
70    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/// Blacklist configuration
86#[derive(Debug, Clone)]
87pub struct BlacklistConfig {
88    /// Blacklist entry expiration
89    pub entry_ttl: Duration,
90
91    /// Max blacklist size
92    pub max_entries: usize,
93
94    /// Auto-blacklist threshold
95    pub violation_threshold: u32,
96}
97
98impl Default for BlacklistConfig {
99    fn default() -> Self {
100        Self {
101            entry_ttl: Duration::from_secs(86400), // 24 hours
102            max_entries: 10000,
103            violation_threshold: 3,
104        }
105    }
106}
107
108/// Eclipse detection configuration
109#[derive(Debug, Clone)]
110pub struct EclipseDetectionConfig {
111    /// Minimum routing table diversity score
112    pub min_diversity_score: f64,
113
114    /// Maximum allowed nodes from same subnet
115    pub max_subnet_ratio: f64,
116
117    /// Suspicious pattern threshold
118    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/// Data integrity configuration
132#[derive(Debug, Clone)]
133pub struct IntegrityConfig {
134    /// Enable content hash verification
135    pub verify_content_hash: bool,
136
137    /// Enable message signatures
138    pub require_signatures: bool,
139
140    /// Maximum message size
141    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, // 10MB
150        }
151    }
152}
153
154/// Audit configuration
155#[derive(Debug, Clone)]
156pub struct AuditConfig {
157    /// Enable audit logging
158    pub enabled: bool,
159
160    /// Log security events
161    pub log_security_events: bool,
162
163    /// Log rate limit violations
164    pub log_rate_limits: bool,
165
166    /// Audit log retention
167    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
181/// Security manager
182pub struct SecurityManager {
183    /// Configuration
184    config: SecurityConfig,
185
186    /// Rate limiter
187    rate_limiter: Arc<RateLimiter>,
188
189    /// Blacklist manager
190    blacklist: Arc<BlacklistManager>,
191
192    /// Eclipse detector
193    eclipse_detector: Arc<EclipseDetector>,
194
195    /// Integrity verifier
196    integrity_verifier: Arc<IntegrityVerifier>,
197
198    /// Security auditor
199    auditor: Arc<SecurityAuditor>,
200
201    /// Node identity for signing
202    _identity: crate::peer_record::UserId,
203}
204
205/// Rate limiter
206pub struct RateLimiter {
207    /// Configuration
208    config: RateLimitConfig,
209
210    /// Node request counts
211    node_requests: Arc<RwLock<HashMap<NodeId, RequestWindow>>>,
212
213    /// IP request counts
214    ip_requests: Arc<RwLock<HashMap<IpAddr, RequestWindow>>>,
215
216    /// Connection counts
217    _connections: Arc<RwLock<HashMap<NodeId, u32>>>,
218
219    /// Join request tracking
220    join_requests: Arc<RwLock<VecDeque<Instant>>>,
221}
222
223/// Request tracking window
224#[derive(Debug, Clone)]
225struct RequestWindow {
226    /// Request count
227    count: u32,
228
229    /// Window start time
230    window_start: Instant,
231}
232
233/// Blacklist manager
234pub struct BlacklistManager {
235    /// Configuration
236    config: BlacklistConfig,
237
238    /// Blacklisted nodes
239    blacklist: Arc<RwLock<HashMap<NodeId, BlacklistEntry>>>,
240
241    /// Violation counts
242    violations: Arc<RwLock<HashMap<NodeId, u32>>>,
243}
244
245/// Blacklist entry
246#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct BlacklistEntry {
248    /// Node ID
249    pub node_id: NodeId,
250
251    /// Reason for blacklisting
252    pub reason: BlacklistReason,
253
254    /// Timestamp when blacklisted
255    pub timestamp: SystemTime,
256
257    /// Reporter node
258    pub reporter: Option<NodeId>,
259}
260
261/// Blacklist reason
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub enum BlacklistReason {
264    /// Exceeded rate limits
265    RateLimitViolation,
266
267    /// Malicious behavior detected
268    MaliciousBehavior(String),
269
270    /// Eclipse attack attempt
271    EclipseAttack,
272
273    /// Data corruption
274    DataCorruption,
275
276    /// Invalid cryptographic proofs
277    InvalidCrypto,
278
279    /// Manual blacklist
280    Manual(String),
281}
282
283/// Eclipse attack detector
284pub struct EclipseDetector {
285    /// Configuration
286    config: EclipseDetectionConfig,
287
288    /// Anomaly patterns
289    patterns: Arc<RwLock<AnomalyPatterns>>,
290}
291
292/// Anomaly patterns for detection
293#[derive(Debug, Default)]
294struct AnomalyPatterns {
295    /// Rapid connection attempts
296    _rapid_connections: HashMap<NodeId, Vec<Instant>>,
297
298    /// Subnet distribution
299    subnet_distribution: HashMap<String, u32>,
300
301    /// Suspicious routing updates
302    routing_anomalies: Vec<RoutingAnomaly>,
303}
304
305/// Routing anomaly
306#[derive(Debug, Clone)]
307struct RoutingAnomaly {
308    /// Node exhibiting anomaly
309    _node_id: NodeId,
310
311    /// Type of anomaly
312    _anomaly_type: AnomalyType,
313
314    /// Detection time
315    timestamp: Instant,
316}
317
318/// Anomaly types
319#[derive(Debug, Clone)]
320pub enum AnomalyType {
321    /// Too many nodes from same subnet
322    SubnetConcentration,
323
324    /// Rapid routing table changes
325    RapidChurn,
326
327    /// Suspicious connection patterns
328    ConnectionPattern,
329
330    /// Coordinated behavior
331    CoordinatedActivity,
332}
333
334/// Data integrity verifier
335pub struct IntegrityVerifier {
336    /// Configuration
337    _config: IntegrityConfig,
338
339    /// Message verification stats
340    stats: Arc<RwLock<VerificationStats>>,
341}
342
343/// Verification statistics
344#[derive(Debug, Default)]
345struct VerificationStats {
346    /// Total messages verified
347    total_verified: u64,
348
349    /// Failed verifications
350    failed_verifications: u64,
351
352    /// Invalid hashes
353    invalid_hashes: u64,
354
355    /// Invalid signatures
356    _invalid_signatures: u64,
357}
358
359/// Security auditor
360pub struct SecurityAuditor {
361    /// Configuration
362    config: AuditConfig,
363
364    /// Audit log
365    audit_log: Arc<Mutex<VecDeque<AuditEntry>>>,
366
367    /// Event counts
368    event_counts: Arc<RwLock<HashMap<String, u64>>>,
369}
370
371/// Audit log entry
372#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct AuditEntry {
374    /// Entry timestamp
375    pub timestamp: SystemTime,
376
377    /// Event type
378    pub event_type: SecurityEvent,
379
380    /// Associated node
381    pub node_id: Option<NodeId>,
382
383    /// Event details
384    pub details: String,
385
386    /// Severity level
387    pub severity: Severity,
388}
389
390/// Security event types
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub enum SecurityEvent {
393    /// Rate limit exceeded
394    RateLimitExceeded,
395
396    /// Node blacklisted
397    NodeBlacklisted,
398
399    /// Eclipse attack detected
400    EclipseAttackDetected,
401
402    /// Data integrity failure
403    IntegrityFailure,
404
405    /// Authentication failure
406    AuthenticationFailure,
407
408    /// Suspicious activity
409    SuspiciousActivity,
410
411    /// Security configuration change
412    ConfigurationChange,
413}
414
415/// Event severity
416#[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/// Security errors
426#[derive(Debug, thiserror::Error)]
427pub enum SecurityError {
428    #[error("Rate limit exceeded")]
429    RateLimitExceeded,
430
431    #[error("Node is blacklisted")]
432    Blacklisted,
433
434    #[error("Invalid cryptographic identity")]
435    InvalidIdentity,
436
437    #[error("Data integrity check failed")]
438    IntegrityCheckFailed,
439
440    #[error("Eclipse attack detected")]
441    EclipseAttackDetected,
442
443    #[error("Message too large")]
444    MessageTooLarge,
445
446    #[error("Invalid signature")]
447    InvalidSignature,
448}
449
450impl SecurityManager {
451    /// Create new security manager
452    pub fn new(config: SecurityConfig, identity: &NodeIdentity) -> Self {
453        let rate_limiter = Arc::new(RateLimiter::new(config.rate_limit.clone()));
454        let blacklist = Arc::new(BlacklistManager::new(config.blacklist.clone()));
455        let eclipse_detector = Arc::new(EclipseDetector::new(config.eclipse_detection.clone()));
456        let integrity_verifier = Arc::new(IntegrityVerifier::new(config.integrity.clone()));
457        let auditor = Arc::new(SecurityAuditor::new(config.audit.clone()));
458
459        Self {
460            config,
461            rate_limiter,
462            blacklist,
463            eclipse_detector,
464            integrity_verifier,
465            auditor,
466            _identity: identity.to_user_id(),
467        }
468    }
469
470    /// Validate node join request
471    pub async fn validate_node_join(&self, node: &NodeDescriptor) -> Result<(), SecurityError> {
472        // Check blacklist
473        if self.blacklist.is_blacklisted(&node.id).await {
474            self.auditor
475                .log_event(
476                    SecurityEvent::NodeBlacklisted,
477                    Some(node.id.clone()),
478                    "Node attempted to join while blacklisted".to_string(),
479                    Severity::Warning,
480                )
481                .await;
482            return Err(SecurityError::Blacklisted);
483        }
484
485        // Check rate limits for joins
486        if !self.rate_limiter.check_join_rate().await {
487            self.auditor
488                .log_event(
489                    SecurityEvent::RateLimitExceeded,
490                    Some(node.id.clone()),
491                    "Join rate limit exceeded".to_string(),
492                    Severity::Warning,
493                )
494                .await;
495            return Err(SecurityError::RateLimitExceeded);
496        }
497
498        // Verify cryptographic identity
499        if !self.verify_identity(node).await {
500            return Err(SecurityError::InvalidIdentity);
501        }
502
503        Ok(())
504    }
505
506    /// Check if request should be rate limited
507    pub async fn check_rate_limit(
508        &self,
509        node_id: &NodeId,
510        ip: Option<IpAddr>,
511    ) -> Result<(), SecurityError> {
512        // Check node rate limit
513        if !self.rate_limiter.check_node_rate(node_id).await {
514            self.blacklist
515                .record_violation(node_id, BlacklistReason::RateLimitViolation)
516                .await;
517            self.auditor
518                .log_event(
519                    SecurityEvent::RateLimitExceeded,
520                    Some(node_id.clone()),
521                    "Node request rate limit exceeded".to_string(),
522                    Severity::Warning,
523                )
524                .await;
525            return Err(SecurityError::RateLimitExceeded);
526        }
527
528        // Check IP rate limit if provided
529        if let Some(ip_addr) = ip
530            && !self.rate_limiter.check_ip_rate(&ip_addr).await
531        {
532            self.auditor
533                .log_event(
534                    SecurityEvent::RateLimitExceeded,
535                    None,
536                    format!("IP {ip_addr} rate limit exceeded"),
537                    Severity::Warning,
538                )
539                .await;
540            return Err(SecurityError::RateLimitExceeded);
541        }
542
543        Ok(())
544    }
545
546    /// Detect eclipse attack
547    pub async fn detect_eclipse_attack(
548        &self,
549        routing_table: &[NodeId],
550    ) -> Result<(), SecurityError> {
551        let diversity_score = self
552            .eclipse_detector
553            .calculate_diversity_score(routing_table)
554            .await;
555
556        if diversity_score < self.config.eclipse_detection.min_diversity_score {
557            self.auditor
558                .log_event(
559                    SecurityEvent::EclipseAttackDetected,
560                    None,
561                    format!("Low routing table diversity: {diversity_score:.2}"),
562                    Severity::Critical,
563                )
564                .await;
565            return Err(SecurityError::EclipseAttackDetected);
566        }
567
568        if self
569            .eclipse_detector
570            .detect_suspicious_patterns(routing_table)
571            .await
572        {
573            self.auditor
574                .log_event(
575                    SecurityEvent::EclipseAttackDetected,
576                    None,
577                    "Suspicious routing patterns detected".to_string(),
578                    Severity::Critical,
579                )
580                .await;
581            return Err(SecurityError::EclipseAttackDetected);
582        }
583
584        Ok(())
585    }
586
587    /// Verify message integrity
588    pub async fn verify_message_integrity(
589        &self,
590        message: &[u8],
591        hash: &[u8],
592        signature: Option<&[u8]>,
593    ) -> Result<(), SecurityError> {
594        // Basic anti-replay check using timestamp prefix if present
595        if message.len() >= 8 {
596            let mut ts_bytes = [0u8; 8];
597            ts_bytes.copy_from_slice(&message[..8]);
598            let msg_ts = u64::from_be_bytes(ts_bytes);
599            let now = SystemTime::now()
600                .duration_since(SystemTime::UNIX_EPOCH)
601                .unwrap_or_default()
602                .as_secs();
603            // 5-minute future tolerance window
604            if msg_ts > now + 300 {
605                return Err(SecurityError::IntegrityCheckFailed);
606            }
607        }
608        // Check message size
609        if message.len() > self.config.integrity.max_message_size {
610            return Err(SecurityError::MessageTooLarge);
611        }
612
613        // Verify content hash
614        if self.config.integrity.verify_content_hash
615            && !self.integrity_verifier.verify_hash(message, hash).await
616        {
617            self.auditor
618                .log_event(
619                    SecurityEvent::IntegrityFailure,
620                    None,
621                    "Content hash verification failed".to_string(),
622                    Severity::Error,
623                )
624                .await;
625            return Err(SecurityError::IntegrityCheckFailed);
626        }
627
628        // Verify signature if required
629        if self.config.integrity.require_signatures {
630            if let Some(sig) = signature {
631                if !self.integrity_verifier.verify_signature(message, sig).await {
632                    self.auditor
633                        .log_event(
634                            SecurityEvent::IntegrityFailure,
635                            None,
636                            "Message signature verification failed".to_string(),
637                            Severity::Error,
638                        )
639                        .await;
640                    return Err(SecurityError::InvalidSignature);
641                }
642            } else {
643                return Err(SecurityError::InvalidSignature);
644            }
645        }
646
647        Ok(())
648    }
649
650    /// Blacklist a node
651    pub async fn blacklist_node(&self, node_id: NodeId, reason: BlacklistReason) {
652        self.blacklist
653            .add_entry(node_id.clone(), reason.clone())
654            .await;
655
656        self.auditor
657            .log_event(
658                SecurityEvent::NodeBlacklisted,
659                Some(node_id),
660                format!("Node blacklisted: {reason:?}"),
661                Severity::Warning,
662            )
663            .await;
664    }
665
666    /// Get security metrics
667    pub async fn get_metrics(&self) -> SecurityMetrics {
668        SecurityMetrics {
669            rate_limit_violations: self.rate_limiter.get_violation_count().await,
670            blacklisted_nodes: self.blacklist.get_blacklist_size().await,
671            verification_failures: self.integrity_verifier.get_failure_count().await,
672            eclipse_detections: self.eclipse_detector.get_detection_count().await,
673            audit_entries: self.auditor.get_entry_count().await,
674        }
675    }
676
677    /// Verify node identity by binding NodeId to the advertised ML-DSA public key
678    async fn verify_identity(&self, node: &NodeDescriptor) -> bool {
679        // Derive BLAKE3 hash of ML-DSA public key bytes to match UserId
680        let bytes = node.public_key.as_bytes();
681        let hash = blake3::hash(bytes);
682        node.id.as_bytes() == hash.as_bytes()
683    }
684}
685
686impl RateLimiter {
687    /// Create new rate limiter
688    pub fn new(config: RateLimitConfig) -> Self {
689        Self {
690            config,
691            node_requests: Arc::new(RwLock::new(HashMap::new())),
692            ip_requests: Arc::new(RwLock::new(HashMap::new())),
693            _connections: Arc::new(RwLock::new(HashMap::new())),
694            join_requests: Arc::new(RwLock::new(VecDeque::new())),
695        }
696    }
697
698    /// Check node rate limit
699    pub async fn check_node_rate(&self, node_id: &NodeId) -> bool {
700        let mut requests = self.node_requests.write().await;
701        let now = Instant::now();
702
703        let window = requests.entry(node_id.clone()).or_insert(RequestWindow {
704            count: 0,
705            window_start: now,
706        });
707
708        // Reset window if expired
709        if now.duration_since(window.window_start) > self.config.window_duration {
710            window.count = 0;
711            window.window_start = now;
712        }
713
714        // Check if under limit
715        if window.count < self.config.node_requests_per_window {
716            window.count += 1;
717            true
718        } else {
719            false
720        }
721    }
722
723    /// Check IP rate limit
724    pub async fn check_ip_rate(&self, ip: &IpAddr) -> bool {
725        let mut requests = self.ip_requests.write().await;
726        let now = Instant::now();
727
728        let window = requests.entry(*ip).or_insert(RequestWindow {
729            count: 0,
730            window_start: now,
731        });
732
733        // Reset window if expired
734        if now.duration_since(window.window_start) > self.config.window_duration {
735            window.count = 0;
736            window.window_start = now;
737        }
738
739        // Check if under limit
740        if window.count < self.config.ip_requests_per_window {
741            window.count += 1;
742            true
743        } else {
744            false
745        }
746    }
747
748    /// Check join rate limit
749    pub async fn check_join_rate(&self) -> bool {
750        let mut join_requests = self.join_requests.write().await;
751        let now = Instant::now();
752        let hour_ago = now - Duration::from_secs(3600);
753
754        // Remove old entries
755        while let Some(front) = join_requests.front() {
756            if *front < hour_ago {
757                join_requests.pop_front();
758            } else {
759                break;
760            }
761        }
762
763        // Check if under limit
764        if join_requests.len() < self.config.max_joins_per_hour as usize {
765            join_requests.push_back(now);
766            true
767        } else {
768            false
769        }
770    }
771
772    /// Get violation count
773    pub async fn get_violation_count(&self) -> u64 {
774        // In a real implementation, would track violations
775        0
776    }
777}
778
779impl BlacklistManager {
780    /// Create new blacklist manager
781    pub fn new(config: BlacklistConfig) -> Self {
782        Self {
783            config,
784            blacklist: Arc::new(RwLock::new(HashMap::new())),
785            violations: Arc::new(RwLock::new(HashMap::new())),
786        }
787    }
788
789    /// Check if node is blacklisted
790    pub async fn is_blacklisted(&self, node_id: &NodeId) -> bool {
791        let blacklist = self.blacklist.read().await;
792
793        if let Some(entry) = blacklist.get(node_id) {
794            // Check if entry has expired
795            let now = SystemTime::now();
796            let elapsed = now
797                .duration_since(entry.timestamp)
798                .unwrap_or(Duration::ZERO);
799
800            elapsed < self.config.entry_ttl
801        } else {
802            false
803        }
804    }
805
806    /// Add blacklist entry
807    pub async fn add_entry(&self, node_id: NodeId, reason: BlacklistReason) {
808        let mut blacklist = self.blacklist.write().await;
809
810        // Enforce max size
811        if blacklist.len() >= self.config.max_entries {
812            // Remove oldest entry
813            if let Some(oldest) = blacklist
814                .iter()
815                .min_by_key(|(_, entry)| entry.timestamp)
816                .map(|(id, _)| id.clone())
817            {
818                blacklist.remove(&oldest);
819            }
820        }
821
822        blacklist.insert(
823            node_id.clone(),
824            BlacklistEntry {
825                node_id,
826                reason,
827                timestamp: SystemTime::now(),
828                reporter: None,
829            },
830        );
831    }
832
833    /// Record violation
834    pub async fn record_violation(&self, node_id: &NodeId, reason: BlacklistReason) {
835        let mut violations = self.violations.write().await;
836        let count = violations.entry(node_id.clone()).or_insert(0);
837        *count += 1;
838
839        // Auto-blacklist if threshold exceeded
840        if *count >= self.config.violation_threshold {
841            drop(violations);
842            self.add_entry(node_id.clone(), reason).await;
843        }
844    }
845
846    /// Get blacklist size
847    pub async fn get_blacklist_size(&self) -> usize {
848        self.blacklist.read().await.len()
849    }
850
851    /// Export blacklist for sharing
852    pub async fn export_blacklist(&self) -> Vec<BlacklistEntry> {
853        let blacklist = self.blacklist.read().await;
854        let now = SystemTime::now();
855
856        blacklist
857            .values()
858            .filter(|entry| {
859                let elapsed = now
860                    .duration_since(entry.timestamp)
861                    .unwrap_or(Duration::ZERO);
862                elapsed < self.config.entry_ttl
863            })
864            .cloned()
865            .collect()
866    }
867
868    /// Import blacklist entries
869    pub async fn import_blacklist(&self, entries: Vec<BlacklistEntry>) {
870        let mut blacklist = self.blacklist.write().await;
871
872        for entry in entries {
873            // Only import if not already present or newer
874            match blacklist.get(&entry.node_id) {
875                Some(existing) if existing.timestamp >= entry.timestamp => continue,
876                _ => {
877                    blacklist.insert(entry.node_id.clone(), entry);
878                }
879            }
880        }
881    }
882}
883
884impl EclipseDetector {
885    /// Create new eclipse detector
886    pub fn new(config: EclipseDetectionConfig) -> Self {
887        Self {
888            config,
889            patterns: Arc::new(RwLock::new(AnomalyPatterns::default())),
890        }
891    }
892
893    /// Calculate diversity score of routing table
894    pub async fn calculate_diversity_score(&self, routing_table: &[NodeId]) -> f64 {
895        if routing_table.is_empty() {
896            return 0.0;
897        }
898
899        // Calculate based on unique hash prefixes
900        let mut prefixes = HashSet::new();
901        for node_id in routing_table {
902            // Use first 4 bytes as prefix
903            let prefix = &node_id.hash[..4];
904            prefixes.insert(prefix.to_vec());
905        }
906
907        // Diversity score is ratio of unique prefixes to total nodes
908        prefixes.len() as f64 / routing_table.len() as f64
909    }
910
911    /// Detect suspicious patterns
912    pub async fn detect_suspicious_patterns(&self, routing_table: &[NodeId]) -> bool {
913        let mut patterns = self.patterns.write().await;
914
915        // Check for subnet concentration
916        // In a real implementation, would extract IPs from node descriptors
917        // For now, use hash prefix as proxy
918        patterns.subnet_distribution.clear();
919
920        for node_id in routing_table {
921            let subnet = format!("{:02x}{:02x}", node_id.hash[0], node_id.hash[1]);
922            *patterns.subnet_distribution.entry(subnet).or_insert(0) += 1;
923        }
924
925        // Check if any subnet has too many nodes
926        let max_allowed = (routing_table.len() as f64 * self.config.max_subnet_ratio) as u32;
927        for count in patterns.subnet_distribution.values() {
928            if *count > max_allowed {
929                return true;
930            }
931        }
932
933        false
934    }
935
936    /// Record routing anomaly
937    pub async fn record_anomaly(&self, node_id: NodeId, anomaly_type: AnomalyType) {
938        let mut patterns = self.patterns.write().await;
939
940        patterns.routing_anomalies.push(RoutingAnomaly {
941            _node_id: node_id,
942            _anomaly_type: anomaly_type,
943            timestamp: Instant::now(),
944        });
945
946        // Keep only recent anomalies (last hour)
947        let cutoff = Instant::now() - Duration::from_secs(3600);
948        patterns.routing_anomalies.retain(|a| a.timestamp > cutoff);
949    }
950
951    /// Get detection count
952    pub async fn get_detection_count(&self) -> u64 {
953        self.patterns.read().await.routing_anomalies.len() as u64
954    }
955}
956
957impl IntegrityVerifier {
958    /// Create new integrity verifier
959    pub fn new(config: IntegrityConfig) -> Self {
960        Self {
961            _config: config,
962            stats: Arc::new(RwLock::new(VerificationStats::default())),
963        }
964    }
965
966    /// Verify content hash
967    pub async fn verify_hash(&self, content: &[u8], expected_hash: &[u8]) -> bool {
968        let mut stats = self.stats.write().await;
969        stats.total_verified += 1;
970
971        let mut hasher = Sha256::new();
972        hasher.update(content);
973        let computed_hash = hasher.finalize();
974
975        if computed_hash.as_slice() == expected_hash {
976            true
977        } else {
978            stats.failed_verifications += 1;
979            stats.invalid_hashes += 1;
980            false
981        }
982    }
983
984    /// Verify signature (placeholder)
985    pub async fn verify_signature(&self, _message: &[u8], _signature: &[u8]) -> bool {
986        // In a real implementation, would verify ML-DSA signature
987        // For now, always return true
988        true
989    }
990
991    /// Get failure count
992    pub async fn get_failure_count(&self) -> u64 {
993        self.stats.read().await.failed_verifications
994    }
995}
996
997impl SecurityAuditor {
998    /// Create new security auditor
999    pub fn new(config: AuditConfig) -> Self {
1000        Self {
1001            config,
1002            audit_log: Arc::new(Mutex::new(VecDeque::new())),
1003            event_counts: Arc::new(RwLock::new(HashMap::new())),
1004        }
1005    }
1006
1007    /// Log security event
1008    pub async fn log_event(
1009        &self,
1010        event_type: SecurityEvent,
1011        node_id: Option<NodeId>,
1012        details: String,
1013        severity: Severity,
1014    ) {
1015        if !self.config.enabled {
1016            return;
1017        }
1018
1019        let entry = AuditEntry {
1020            timestamp: SystemTime::now(),
1021            event_type: event_type.clone(),
1022            node_id,
1023            details,
1024            severity,
1025        };
1026
1027        // Add to log
1028        let mut log = self.audit_log.lock().await;
1029        log.push_back(entry);
1030
1031        // Enforce retention
1032        let retention_duration = Duration::from_secs(self.config.retention_days as u64 * 86400);
1033        let cutoff = SystemTime::now() - retention_duration;
1034
1035        while let Some(front) = log.front() {
1036            if front.timestamp < cutoff {
1037                log.pop_front();
1038            } else {
1039                break;
1040            }
1041        }
1042
1043        // Update event counts
1044        let event_name = format!("{event_type:?}");
1045        let mut counts = self.event_counts.write().await;
1046        *counts.entry(event_name).or_insert(0) += 1;
1047    }
1048
1049    /// Get audit entries
1050    pub async fn get_entries(
1051        &self,
1052        since: Option<SystemTime>,
1053        severity_filter: Option<Severity>,
1054    ) -> Vec<AuditEntry> {
1055        let log = self.audit_log.lock().await;
1056
1057        log.iter()
1058            .filter(|entry| {
1059                if let Some(min_time) = since
1060                    && entry.timestamp < min_time
1061                {
1062                    return false;
1063                }
1064                if let Some(min_severity) = severity_filter
1065                    && (entry.severity as u8) < (min_severity as u8)
1066                {
1067                    return false;
1068                }
1069                true
1070            })
1071            .cloned()
1072            .collect()
1073    }
1074
1075    /// Get entry count
1076    pub async fn get_entry_count(&self) -> u64 {
1077        self.audit_log.lock().await.len() as u64
1078    }
1079
1080    /// Export audit report
1081    pub async fn export_report(&self) -> AuditReport {
1082        let entries = self.get_entries(None, None).await;
1083        let event_counts = self.event_counts.read().await.clone();
1084
1085        AuditReport {
1086            generated_at: SystemTime::now(),
1087            total_entries: entries.len(),
1088            event_counts,
1089            severity_breakdown: self.calculate_severity_breakdown(&entries),
1090            recent_critical_events: entries
1091                .iter()
1092                .filter(|e| e.severity == Severity::Critical)
1093                .take(10)
1094                .cloned()
1095                .collect(),
1096        }
1097    }
1098
1099    /// Calculate severity breakdown
1100    fn calculate_severity_breakdown(&self, entries: &[AuditEntry]) -> HashMap<Severity, u64> {
1101        let mut breakdown = HashMap::new();
1102
1103        for entry in entries {
1104            *breakdown.entry(entry.severity).or_insert(0) += 1;
1105        }
1106
1107        breakdown
1108    }
1109}
1110
1111/// Security metrics
1112#[derive(Debug, Clone, Default)]
1113pub struct SecurityMetrics {
1114    pub rate_limit_violations: u64,
1115    pub blacklisted_nodes: usize,
1116    pub verification_failures: u64,
1117    pub eclipse_detections: u64,
1118    pub audit_entries: u64,
1119}
1120
1121/// Audit report
1122#[derive(Debug, Clone, Serialize, Deserialize)]
1123pub struct AuditReport {
1124    pub generated_at: SystemTime,
1125    pub total_entries: usize,
1126    pub event_counts: HashMap<String, u64>,
1127    pub severity_breakdown: HashMap<Severity, u64>,
1128    pub recent_critical_events: Vec<AuditEntry>,
1129}
1130
1131#[cfg(test)]
1132mod tests {
1133    use super::*;
1134
1135    #[tokio::test]
1136    async fn test_rate_limiter_node_limits() {
1137        let config = RateLimitConfig {
1138            node_requests_per_window: 5,
1139            window_duration: Duration::from_secs(1),
1140            ..Default::default()
1141        };
1142
1143        let limiter = RateLimiter::new(config);
1144        let node_id = NodeId { hash: [1u8; 32] };
1145
1146        // Should allow first 5 requests
1147        for _ in 0..5 {
1148            assert!(limiter.check_node_rate(&node_id).await);
1149        }
1150
1151        // 6th request should fail
1152        assert!(!limiter.check_node_rate(&node_id).await);
1153
1154        // Wait for window to reset
1155        tokio::time::sleep(Duration::from_secs(2)).await;
1156
1157        // Should allow again
1158        assert!(limiter.check_node_rate(&node_id).await);
1159    }
1160
1161    #[tokio::test]
1162    async fn test_blacklist_management() {
1163        let config = BlacklistConfig::default();
1164        let blacklist = BlacklistManager::new(config);
1165
1166        let node_id = NodeId { hash: [2u8; 32] };
1167
1168        // Should not be blacklisted initially
1169        assert!(!blacklist.is_blacklisted(&node_id).await);
1170
1171        // Add to blacklist
1172        blacklist
1173            .add_entry(
1174                node_id.clone(),
1175                BlacklistReason::MaliciousBehavior("Test".to_string()),
1176            )
1177            .await;
1178
1179        // Should now be blacklisted
1180        assert!(blacklist.is_blacklisted(&node_id).await);
1181
1182        // Check blacklist size
1183        assert_eq!(blacklist.get_blacklist_size().await, 1);
1184    }
1185
1186    #[tokio::test]
1187    async fn test_eclipse_detection() {
1188        let config = EclipseDetectionConfig {
1189            min_diversity_score: 0.5,
1190            max_subnet_ratio: 0.3,
1191            pattern_threshold: 0.7,
1192        };
1193
1194        let detector = EclipseDetector::new(config);
1195
1196        // Create routing table with low diversity
1197        let mut routing_table = vec![];
1198        for i in 0..10 {
1199            let mut hash = [0u8; 32];
1200            hash[0] = 1; // Same prefix
1201            hash[31] = i;
1202            routing_table.push(NodeId { hash });
1203        }
1204
1205        // Should have low diversity score
1206        let score = detector.calculate_diversity_score(&routing_table).await;
1207        assert!(score < 0.5);
1208
1209        // Create diverse routing table
1210        let mut diverse_table = vec![];
1211        for i in 0..10 {
1212            let mut hash = [0u8; 32];
1213            hash[0] = i * 25; // Different prefixes
1214            diverse_table.push(NodeId { hash });
1215        }
1216
1217        // Should have high diversity score
1218        let diverse_score = detector.calculate_diversity_score(&diverse_table).await;
1219        assert!(diverse_score > 0.8);
1220    }
1221
1222    #[tokio::test]
1223    async fn test_integrity_verification() {
1224        let config = IntegrityConfig::default();
1225        let verifier = IntegrityVerifier::new(config);
1226
1227        let content = b"Test content";
1228        let mut hasher = Sha256::new();
1229        hasher.update(content);
1230        let correct_hash = hasher.finalize();
1231
1232        // Should verify correct hash
1233        assert!(verifier.verify_hash(content, &correct_hash).await);
1234
1235        // Should fail with incorrect hash
1236        let wrong_hash = [0u8; 32];
1237        assert!(!verifier.verify_hash(content, &wrong_hash).await);
1238
1239        // Check failure count
1240        assert_eq!(verifier.get_failure_count().await, 1);
1241    }
1242
1243    #[tokio::test]
1244    async fn test_security_auditor() {
1245        let config = AuditConfig::default();
1246        let auditor = SecurityAuditor::new(config);
1247
1248        // Log some events
1249        auditor
1250            .log_event(
1251                SecurityEvent::RateLimitExceeded,
1252                None,
1253                "Test rate limit".to_string(),
1254                Severity::Warning,
1255            )
1256            .await;
1257
1258        auditor
1259            .log_event(
1260                SecurityEvent::EclipseAttackDetected,
1261                None,
1262                "Test eclipse attack".to_string(),
1263                Severity::Critical,
1264            )
1265            .await;
1266
1267        // Check entry count
1268        assert_eq!(auditor.get_entry_count().await, 2);
1269
1270        // Get critical events
1271        let entries = auditor.get_entries(None, Some(Severity::Critical)).await;
1272        assert_eq!(entries.len(), 1);
1273        assert_eq!(entries[0].severity, Severity::Critical);
1274
1275        // Export report
1276        let report = auditor.export_report().await;
1277        assert_eq!(report.total_entries, 2);
1278        assert_eq!(report.recent_critical_events.len(), 1);
1279    }
1280
1281    #[tokio::test]
1282    async fn test_security_manager_integration() {
1283        let config = SecurityConfig::default();
1284        let identity = NodeIdentity::generate().unwrap();
1285        let manager = SecurityManager::new(config, &identity);
1286
1287        // Test node join validation
1288        // Generate a valid ML-DSA key and derive matching UserId via blake3(pubkey)
1289        let (ml_pub, _ml_sec) = crate::quantum_crypto::generate_ml_dsa_keypair().unwrap();
1290        let derived_hash = blake3::hash(ml_pub.as_bytes());
1291        let derived_id = crate::peer_record::UserId::from_bytes(*derived_hash.as_bytes());
1292        let node = NodeDescriptor {
1293            id: derived_id,
1294            public_key: ml_pub,
1295            addresses: vec![],
1296            hyperbolic: None,
1297            som_position: None,
1298            trust: 0.5,
1299            capabilities: NodeCapabilities {
1300                storage: 100,
1301                compute: 50,
1302                bandwidth: 10,
1303            },
1304        };
1305
1306        // Should pass validation
1307        assert!(manager.validate_node_join(&node).await.is_ok());
1308
1309        // Blacklist the node
1310        manager
1311            .blacklist_node(node.id.clone(), BlacklistReason::Manual("Test".to_string()))
1312            .await;
1313
1314        // Should now fail validation
1315        assert!(matches!(
1316            manager.validate_node_join(&node).await,
1317            Err(SecurityError::Blacklisted)
1318        ));
1319
1320        // Check metrics
1321        let metrics = manager.get_metrics().await;
1322        assert_eq!(metrics.blacklisted_nodes, 1);
1323        assert!(metrics.audit_entries > 0);
1324    }
1325}