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: NodeIdentity,
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
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    /// Create new security manager
453    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    /// Validate node join request
472    pub async fn validate_node_join(&self, node: &NodeDescriptor) -> Result<(), SecurityError> {
473        // Check blacklist
474        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        // Check rate limits for joins
487        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        // Verify cryptographic identity
500        if !self.verify_identity(node).await {
501            return Err(SecurityError::InvalidIdentity);
502        }
503
504        Ok(())
505    }
506
507    /// Check if request should be rate limited
508    pub async fn check_rate_limit(
509        &self,
510        node_id: &NodeId,
511        ip: Option<IpAddr>,
512    ) -> Result<(), SecurityError> {
513        // Check node rate limit
514        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        // Check IP rate limit if provided
530        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    /// Detect eclipse attack
548    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    /// Verify message integrity
589    pub async fn verify_message_integrity(
590        &self,
591        message: &[u8],
592        hash: &[u8],
593        signature: Option<&[u8]>,
594    ) -> Result<(), SecurityError> {
595        // Check message size
596        if message.len() > self.config.integrity.max_message_size {
597            return Err(SecurityError::MessageTooLarge);
598        }
599
600        // Verify content hash
601        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        // Verify signature if required
616        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    /// Blacklist a node
638    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    /// Get security metrics
654    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    /// Verify node identity
665    async fn verify_identity(&self, _node: &NodeDescriptor) -> bool {
666        // Verify that the node ID matches the public key hash
667        // This is a simplified check - real implementation would be more thorough
668        true
669    }
670}
671
672impl RateLimiter {
673    /// Create new rate limiter
674    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    /// Check node rate limit
685    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        // Reset window if expired
695        if now.duration_since(window.window_start) > self.config.window_duration {
696            window.count = 0;
697            window.window_start = now;
698        }
699
700        // Check if under limit
701        if window.count < self.config.node_requests_per_window {
702            window.count += 1;
703            true
704        } else {
705            false
706        }
707    }
708
709    /// Check IP rate limit
710    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        // Reset window if expired
720        if now.duration_since(window.window_start) > self.config.window_duration {
721            window.count = 0;
722            window.window_start = now;
723        }
724
725        // Check if under limit
726        if window.count < self.config.ip_requests_per_window {
727            window.count += 1;
728            true
729        } else {
730            false
731        }
732    }
733
734    /// Check join rate limit
735    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        // Remove old entries
741        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        // Check if under limit
750        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    /// Get violation count
759    pub async fn get_violation_count(&self) -> u64 {
760        // In a real implementation, would track violations
761        0
762    }
763}
764
765impl BlacklistManager {
766    /// Create new blacklist manager
767    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    /// Check if node is blacklisted
776    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            // Check if entry has expired
781            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    /// Add blacklist entry
793    pub async fn add_entry(&self, node_id: NodeId, reason: BlacklistReason) {
794        let mut blacklist = self.blacklist.write().await;
795
796        // Enforce max size
797        if blacklist.len() >= self.config.max_entries {
798            // Remove oldest entry
799            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    /// Record violation
820    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        // Auto-blacklist if threshold exceeded
826        if *count >= self.config.violation_threshold {
827            drop(violations);
828            self.add_entry(node_id.clone(), reason).await;
829        }
830    }
831
832    /// Get blacklist size
833    pub async fn get_blacklist_size(&self) -> usize {
834        self.blacklist.read().await.len()
835    }
836
837    /// Export blacklist for sharing
838    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    /// Import blacklist entries
855    pub async fn import_blacklist(&self, entries: Vec<BlacklistEntry>) {
856        let mut blacklist = self.blacklist.write().await;
857
858        for entry in entries {
859            // Only import if not already present or newer
860            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    /// Create new eclipse detector
872    pub fn new(config: EclipseDetectionConfig) -> Self {
873        Self {
874            config,
875            patterns: Arc::new(RwLock::new(AnomalyPatterns::default())),
876        }
877    }
878
879    /// Calculate diversity score of routing table
880    pub async fn calculate_diversity_score(&self, routing_table: &[NodeId]) -> f64 {
881        if routing_table.is_empty() {
882            return 0.0;
883        }
884
885        // Calculate based on unique hash prefixes
886        let mut prefixes = HashSet::new();
887        for node_id in routing_table {
888            // Use first 4 bytes as prefix
889            let prefix = &node_id.hash[..4];
890            prefixes.insert(prefix.to_vec());
891        }
892
893        // Diversity score is ratio of unique prefixes to total nodes
894        prefixes.len() as f64 / routing_table.len() as f64
895    }
896
897    /// Detect suspicious patterns
898    pub async fn detect_suspicious_patterns(&self, routing_table: &[NodeId]) -> bool {
899        let mut patterns = self.patterns.write().await;
900
901        // Check for subnet concentration
902        // In a real implementation, would extract IPs from node descriptors
903        // For now, use hash prefix as proxy
904        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        // Check if any subnet has too many nodes
912        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    /// Record routing anomaly
923    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        // Keep only recent anomalies (last hour)
933        let cutoff = Instant::now() - Duration::from_secs(3600);
934        patterns.routing_anomalies.retain(|a| a.timestamp > cutoff);
935    }
936
937    /// Get detection count
938    pub async fn get_detection_count(&self) -> u64 {
939        self.patterns.read().await.routing_anomalies.len() as u64
940    }
941}
942
943impl IntegrityVerifier {
944    /// Create new integrity verifier
945    pub fn new(config: IntegrityConfig) -> Self {
946        Self {
947            _config: config,
948            stats: Arc::new(RwLock::new(VerificationStats::default())),
949        }
950    }
951
952    /// Verify content hash
953    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    /// Verify signature (placeholder)
971    pub async fn verify_signature(&self, _message: &[u8], _signature: &[u8]) -> bool {
972        // In a real implementation, would verify Ed25519 signature
973        // For now, always return true
974        true
975    }
976
977    /// Get failure count
978    pub async fn get_failure_count(&self) -> u64 {
979        self.stats.read().await.failed_verifications
980    }
981}
982
983impl SecurityAuditor {
984    /// Create new security auditor
985    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    /// Log security event
994    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        // Add to log
1014        let mut log = self.audit_log.lock().await;
1015        log.push_back(entry);
1016
1017        // Enforce retention
1018        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        // Update event counts
1030        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    /// Get audit entries
1036    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    /// Get entry count
1062    pub async fn get_entry_count(&self) -> u64 {
1063        self.audit_log.lock().await.len() as u64
1064    }
1065
1066    /// Export audit report
1067    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    /// Calculate severity breakdown
1086    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/// Security metrics
1098#[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/// Audit report
1108#[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        // Should allow first 5 requests
1133        for _ in 0..5 {
1134            assert!(limiter.check_node_rate(&node_id).await);
1135        }
1136
1137        // 6th request should fail
1138        assert!(!limiter.check_node_rate(&node_id).await);
1139
1140        // Wait for window to reset
1141        tokio::time::sleep(Duration::from_secs(2)).await;
1142
1143        // Should allow again
1144        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        // Should not be blacklisted initially
1155        assert!(!blacklist.is_blacklisted(&node_id).await);
1156
1157        // Add to blacklist
1158        blacklist
1159            .add_entry(
1160                node_id.clone(),
1161                BlacklistReason::MaliciousBehavior("Test".to_string()),
1162            )
1163            .await;
1164
1165        // Should now be blacklisted
1166        assert!(blacklist.is_blacklisted(&node_id).await);
1167
1168        // Check blacklist size
1169        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        // Create routing table with low diversity
1183        let mut routing_table = vec![];
1184        for i in 0..10 {
1185            let mut hash = [0u8; 32];
1186            hash[0] = 1; // Same prefix
1187            hash[31] = i;
1188            routing_table.push(NodeId { hash });
1189        }
1190
1191        // Should have low diversity score
1192        let score = detector.calculate_diversity_score(&routing_table).await;
1193        assert!(score < 0.5);
1194
1195        // Create diverse routing table
1196        let mut diverse_table = vec![];
1197        for i in 0..10 {
1198            let mut hash = [0u8; 32];
1199            hash[0] = i * 25; // Different prefixes
1200            diverse_table.push(NodeId { hash });
1201        }
1202
1203        // Should have high diversity score
1204        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        // Should verify correct hash
1219        assert!(verifier.verify_hash(content, &correct_hash).await);
1220
1221        // Should fail with incorrect hash
1222        let wrong_hash = [0u8; 32];
1223        assert!(!verifier.verify_hash(content, &wrong_hash).await);
1224
1225        // Check failure count
1226        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        // Log some events
1235        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        // Check entry count
1254        assert_eq!(auditor.get_entry_count().await, 2);
1255
1256        // Get critical events
1257        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        // Export report
1262        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        // Test node join validation
1274        let node = NodeDescriptor {
1275            id: NodeId { hash: [3u8; 32] },
1276            public_key: ed25519_dalek::VerifyingKey::from_bytes(&[0u8; 32]).unwrap(),
1277            addresses: vec![], // No hardcoded addresses in tests
1278            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        // Should pass validation
1289        assert!(manager.validate_node_join(&node).await.is_ok());
1290
1291        // Blacklist the node
1292        manager
1293            .blacklist_node(node.id.clone(), BlacklistReason::Manual("Test".to_string()))
1294            .await;
1295
1296        // Should now fail validation
1297        assert!(matches!(
1298            manager.validate_node_join(&node).await,
1299            Err(SecurityError::Blacklisted)
1300        ));
1301
1302        // Check metrics
1303        let metrics = manager.get_metrics().await;
1304        assert_eq!(metrics.blacklisted_nodes, 1);
1305        assert!(metrics.audit_entries > 0);
1306    }
1307}