saorsa_core/
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 module
15//!
16//! This module provides cryptographic functionality and Sybil protection for the P2P network.
17//! It implements IPv6-based node ID generation and IP diversity enforcement to prevent
18//! large-scale Sybil attacks while maintaining network openness.
19
20use crate::PeerId;
21use crate::quantum_crypto::ant_quic_integration::{
22    MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature, ml_dsa_sign, ml_dsa_verify,
23};
24use anyhow::{Result, anyhow};
25use serde::{Deserialize, Serialize};
26use sha2::{Digest, Sha256};
27use std::collections::HashMap;
28use std::net::{Ipv4Addr, Ipv6Addr};
29use std::time::{Duration, SystemTime, UNIX_EPOCH};
30
31use std::sync::Arc;
32
33/// IPv6-based node identity that binds node ID to actual network location
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct IPv6NodeID {
36    /// Derived node ID (SHA256 of ipv6_addr + public_key + salt)
37    pub node_id: Vec<u8>,
38    /// IPv6 address this node ID is bound to
39    pub ipv6_addr: Ipv6Addr,
40    /// ML-DSA public key for signatures
41    pub public_key: Vec<u8>,
42    /// Signature proving ownership of the IPv6 address and keys
43    pub signature: Vec<u8>,
44    /// Timestamp when this ID was generated (seconds since epoch)
45    pub timestamp_secs: u64,
46    /// Salt used in node ID generation (for freshness)
47    pub salt: Vec<u8>,
48}
49
50/// Configuration for IP diversity enforcement at multiple subnet levels
51#[derive(Debug, Clone)]
52pub struct IPDiversityConfig {
53    /// Maximum nodes per /64 subnet (default: 1)
54    pub max_nodes_per_64: usize,
55    /// Maximum nodes per /48 allocation (default: 3)
56    pub max_nodes_per_48: usize,
57    /// Maximum nodes per /32 region (default: 10)
58    pub max_nodes_per_32: usize,
59    /// Maximum nodes per AS number (default: 20)
60    pub max_nodes_per_asn: usize,
61    /// Enable GeoIP-based diversity checks
62    pub enable_geolocation_check: bool,
63    /// Minimum number of different countries required
64    pub min_geographic_diversity: usize,
65}
66
67/// Analysis of an IPv6 address for diversity enforcement
68#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
69pub struct IPAnalysis {
70    /// /64 subnet (host allocation)
71    pub subnet_64: Ipv6Addr,
72    /// /48 subnet (site allocation)
73    pub subnet_48: Ipv6Addr,
74    /// /32 subnet (ISP allocation)
75    pub subnet_32: Ipv6Addr,
76    /// Autonomous System Number (if available)
77    pub asn: Option<u32>,
78    /// Country code from GeoIP lookup
79    pub country: Option<String>,
80    /// Whether this is a known hosting/VPS provider
81    pub is_hosting_provider: bool,
82    /// Whether this is a known VPN provider
83    pub is_vpn_provider: bool,
84    /// Historical reputation score for this IP range
85    pub reputation_score: f64,
86}
87
88/// Node reputation tracking for security-aware routing
89#[derive(Debug, Clone)]
90pub struct NodeReputation {
91    /// Peer ID
92    pub peer_id: PeerId,
93    /// Fraction of queries answered successfully
94    pub response_rate: f64,
95    /// Average response time
96    pub response_time: Duration,
97    /// Consistency of provided data (0.0-1.0)
98    pub consistency_score: f64,
99    /// Estimated continuous uptime
100    pub uptime_estimate: Duration,
101    /// Accuracy of routing information provided
102    pub routing_accuracy: f64,
103    /// Last time this node was seen
104    pub last_seen: SystemTime,
105    /// Total number of interactions
106    pub interaction_count: u64,
107}
108
109impl Default for IPDiversityConfig {
110    fn default() -> Self {
111        Self {
112            max_nodes_per_64: 1,
113            max_nodes_per_48: 3,
114            max_nodes_per_32: 10,
115            max_nodes_per_asn: 20,
116            enable_geolocation_check: true,
117            min_geographic_diversity: 3,
118        }
119    }
120}
121
122impl IPv6NodeID {
123    /// Generate a new IPv6-based node ID
124    pub fn generate(
125        ipv6_addr: Ipv6Addr,
126        secret: &MlDsaSecretKey,
127        public: &MlDsaPublicKey,
128    ) -> Result<Self> {
129        let mut rng = rand::thread_rng();
130        let mut salt = vec![0u8; 16];
131        rand::RngCore::fill_bytes(&mut rng, &mut salt);
132
133        let timestamp = SystemTime::now();
134        let timestamp_secs = timestamp.duration_since(UNIX_EPOCH)?.as_secs();
135        let public_key = public.as_bytes().to_vec();
136
137        // Generate node ID: SHA256(ipv6_address || public_key || salt || timestamp)
138        let mut hasher = Sha256::new();
139        hasher.update(ipv6_addr.octets());
140        hasher.update(&public_key);
141        hasher.update(&salt);
142        hasher.update(timestamp_secs.to_le_bytes());
143        let node_id = hasher.finalize().to_vec();
144
145        // Create signature proving ownership
146        let mut message_to_sign = Vec::new();
147        message_to_sign.extend_from_slice(&ipv6_addr.octets());
148        message_to_sign.extend_from_slice(&public_key);
149        message_to_sign.extend_from_slice(&salt);
150        message_to_sign.extend_from_slice(&timestamp_secs.to_le_bytes());
151
152        let sig = ml_dsa_sign(secret, &message_to_sign)
153            .map_err(|e| anyhow!("ML-DSA sign failed: {:?}", e))?;
154        let signature = sig.0.to_vec();
155
156        Ok(IPv6NodeID {
157            node_id,
158            ipv6_addr,
159            public_key,
160            signature,
161            timestamp_secs,
162            salt,
163        })
164    }
165
166    /// Verify that this node ID is valid and properly signed
167    pub fn verify(&self) -> Result<bool> {
168        // Reconstruct the node ID
169        let mut hasher = Sha256::new();
170        hasher.update(self.ipv6_addr.octets());
171        hasher.update(&self.public_key);
172        hasher.update(&self.salt);
173        hasher.update(self.timestamp_secs.to_le_bytes());
174        let expected_node_id = hasher.finalize();
175
176        // Verify node ID matches
177        if expected_node_id.as_slice() != self.node_id {
178            return Ok(false);
179        }
180
181        let public_key = MlDsaPublicKey::from_bytes(&self.public_key)
182            .map_err(|e| anyhow!("Invalid ML-DSA public key: {:?}", e))?;
183
184        // Convert Vec<u8> to fixed-size array for MlDsaSignature
185        if self.signature.len() != 3309 {
186            return Ok(false); // Invalid signature length
187        }
188        let mut sig_bytes = [0u8; 3309];
189        sig_bytes.copy_from_slice(&self.signature);
190        let signature = MlDsaSignature(Box::new(sig_bytes));
191
192        let mut message_to_verify = Vec::new();
193        message_to_verify.extend_from_slice(&self.ipv6_addr.octets());
194        message_to_verify.extend_from_slice(&self.public_key);
195        message_to_verify.extend_from_slice(&self.salt);
196        message_to_verify.extend_from_slice(&self.timestamp_secs.to_le_bytes());
197
198        let ok = ml_dsa_verify(&public_key, &message_to_verify, &signature)
199            .map_err(|e| anyhow!("ML-DSA verify error: {:?}", e))?;
200        Ok(ok)
201    }
202
203    /// Extract /64 subnet from IPv6 address
204    pub fn extract_subnet_64(&self) -> Ipv6Addr {
205        let octets = self.ipv6_addr.octets();
206        let mut subnet = [0u8; 16];
207        subnet[..8].copy_from_slice(&octets[..8]); // Keep first 64 bits, zero the rest
208        Ipv6Addr::from(subnet)
209    }
210
211    /// Extract /48 subnet from IPv6 address
212    pub fn extract_subnet_48(&self) -> Ipv6Addr {
213        let octets = self.ipv6_addr.octets();
214        let mut subnet = [0u8; 16];
215        subnet[..6].copy_from_slice(&octets[..6]); // Keep first 48 bits, zero the rest
216        Ipv6Addr::from(subnet)
217    }
218
219    /// Extract /32 subnet from IPv6 address
220    pub fn extract_subnet_32(&self) -> Ipv6Addr {
221        let octets = self.ipv6_addr.octets();
222        let mut subnet = [0u8; 16];
223        subnet[..4].copy_from_slice(&octets[..4]); // Keep first 32 bits, zero the rest
224        Ipv6Addr::from(subnet)
225    }
226}
227
228/// IPv4-based node identity that binds node ID to actual network location
229/// Mirrors IPv6NodeID for security parity on IPv4 networks
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct IPv4NodeID {
232    /// Derived node ID (SHA256 of ipv4_addr + public_key + salt + timestamp)
233    pub node_id: Vec<u8>,
234    /// IPv4 address this node ID is bound to
235    pub ipv4_addr: Ipv4Addr,
236    /// ML-DSA public key for signatures
237    pub public_key: Vec<u8>,
238    /// Signature proving ownership of the IPv4 address and keys
239    pub signature: Vec<u8>,
240    /// Timestamp when this ID was generated (seconds since epoch)
241    pub timestamp_secs: u64,
242    /// Salt used in node ID generation (for freshness)
243    pub salt: Vec<u8>,
244}
245
246impl IPv4NodeID {
247    /// Generate a new IPv4-based node ID
248    pub fn generate(
249        ipv4_addr: Ipv4Addr,
250        secret: &MlDsaSecretKey,
251        public: &MlDsaPublicKey,
252    ) -> Result<Self> {
253        let mut rng = rand::thread_rng();
254        let mut salt = vec![0u8; 16];
255        rand::RngCore::fill_bytes(&mut rng, &mut salt);
256
257        let timestamp = SystemTime::now();
258        let timestamp_secs = timestamp.duration_since(UNIX_EPOCH)?.as_secs();
259        let public_key = public.as_bytes().to_vec();
260
261        // Generate node ID: SHA256(ipv4_address || public_key || salt || timestamp)
262        let mut hasher = Sha256::new();
263        hasher.update(ipv4_addr.octets());
264        hasher.update(&public_key);
265        hasher.update(&salt);
266        hasher.update(timestamp_secs.to_le_bytes());
267        let node_id = hasher.finalize().to_vec();
268
269        // Create signature proving ownership
270        let mut message_to_sign = Vec::new();
271        message_to_sign.extend_from_slice(&ipv4_addr.octets());
272        message_to_sign.extend_from_slice(&public_key);
273        message_to_sign.extend_from_slice(&salt);
274        message_to_sign.extend_from_slice(&timestamp_secs.to_le_bytes());
275
276        let sig = ml_dsa_sign(secret, &message_to_sign)
277            .map_err(|e| anyhow!("ML-DSA sign failed: {:?}", e))?;
278        let signature = sig.0.to_vec();
279
280        Ok(IPv4NodeID {
281            node_id,
282            ipv4_addr,
283            public_key,
284            signature,
285            timestamp_secs,
286            salt,
287        })
288    }
289
290    /// Verify that this node ID is valid and properly signed
291    pub fn verify(&self) -> Result<bool> {
292        // Reconstruct the node ID
293        let mut hasher = Sha256::new();
294        hasher.update(self.ipv4_addr.octets());
295        hasher.update(&self.public_key);
296        hasher.update(&self.salt);
297        hasher.update(self.timestamp_secs.to_le_bytes());
298        let expected_node_id = hasher.finalize();
299
300        // Verify node ID matches
301        if expected_node_id.as_slice() != self.node_id {
302            return Ok(false);
303        }
304
305        let public_key = MlDsaPublicKey::from_bytes(&self.public_key)
306            .map_err(|e| anyhow!("Invalid ML-DSA public key: {:?}", e))?;
307
308        // Convert Vec<u8> to fixed-size array for MlDsaSignature
309        if self.signature.len() != 3309 {
310            return Ok(false); // Invalid signature length
311        }
312        let mut sig_bytes = [0u8; 3309];
313        sig_bytes.copy_from_slice(&self.signature);
314        let signature = MlDsaSignature(Box::new(sig_bytes));
315
316        let mut message_to_verify = Vec::new();
317        message_to_verify.extend_from_slice(&self.ipv4_addr.octets());
318        message_to_verify.extend_from_slice(&self.public_key);
319        message_to_verify.extend_from_slice(&self.salt);
320        message_to_verify.extend_from_slice(&self.timestamp_secs.to_le_bytes());
321
322        let ok = ml_dsa_verify(&public_key, &message_to_verify, &signature)
323            .map_err(|e| anyhow!("ML-DSA verify error: {:?}", e))?;
324        Ok(ok)
325    }
326
327    /// Extract /24 subnet from IPv4 address (Class C / most ISP allocations)
328    pub fn extract_subnet_24(&self) -> Ipv4Addr {
329        let octets = self.ipv4_addr.octets();
330        Ipv4Addr::new(octets[0], octets[1], octets[2], 0)
331    }
332
333    /// Extract /16 subnet from IPv4 address (Class B / large ISP allocations)
334    pub fn extract_subnet_16(&self) -> Ipv4Addr {
335        let octets = self.ipv4_addr.octets();
336        Ipv4Addr::new(octets[0], octets[1], 0, 0)
337    }
338
339    /// Extract /8 subnet from IPv4 address (Class A / regional allocations)
340    pub fn extract_subnet_8(&self) -> Ipv4Addr {
341        let octets = self.ipv4_addr.octets();
342        Ipv4Addr::new(octets[0], 0, 0, 0)
343    }
344
345    /// Get the age of this node ID in seconds
346    pub fn age_secs(&self) -> u64 {
347        let now = SystemTime::now()
348            .duration_since(UNIX_EPOCH)
349            .map(|d| d.as_secs())
350            .unwrap_or(0);
351        now.saturating_sub(self.timestamp_secs)
352    }
353
354    /// Check if the node ID has expired (older than max_age)
355    pub fn is_expired(&self, max_age: Duration) -> bool {
356        self.age_secs() > max_age.as_secs()
357    }
358}
359
360/// IP diversity enforcement system
361#[derive(Debug)]
362pub struct IPDiversityEnforcer {
363    config: IPDiversityConfig,
364    subnet_64_counts: HashMap<Ipv6Addr, usize>,
365    subnet_48_counts: HashMap<Ipv6Addr, usize>,
366    subnet_32_counts: HashMap<Ipv6Addr, usize>,
367    asn_counts: HashMap<u32, usize>,
368    country_counts: HashMap<String, usize>,
369    geo_provider: Option<Arc<dyn GeoProvider + Send + Sync>>,
370}
371
372impl IPDiversityEnforcer {
373    /// Create a new IP diversity enforcer
374    pub fn new(config: IPDiversityConfig) -> Self {
375        Self {
376            config,
377            subnet_64_counts: HashMap::new(),
378            subnet_48_counts: HashMap::new(),
379            subnet_32_counts: HashMap::new(),
380            asn_counts: HashMap::new(),
381            country_counts: HashMap::new(),
382            geo_provider: None,
383        }
384    }
385
386    /// Create a new IP diversity enforcer with a GeoIP/ASN provider
387    pub fn with_geo_provider(
388        config: IPDiversityConfig,
389        provider: Arc<dyn GeoProvider + Send + Sync>,
390    ) -> Self {
391        let mut s = Self::new(config);
392        s.geo_provider = Some(provider);
393        s
394    }
395
396    /// Analyze an IPv6 address for diversity enforcement
397    pub fn analyze_ip(&self, ipv6_addr: Ipv6Addr) -> Result<IPAnalysis> {
398        let subnet_64 = Self::extract_subnet_prefix(ipv6_addr, 64);
399        let subnet_48 = Self::extract_subnet_prefix(ipv6_addr, 48);
400        let subnet_32 = Self::extract_subnet_prefix(ipv6_addr, 32);
401
402        // GeoIP/ASN lookup via provider if available
403        let (asn, country, is_hosting_provider, is_vpn_provider) =
404            if let Some(p) = &self.geo_provider {
405                let info = p.lookup(ipv6_addr);
406                (
407                    info.asn,
408                    info.country,
409                    info.is_hosting_provider,
410                    info.is_vpn_provider,
411                )
412            } else {
413                (None, None, false, false)
414            };
415
416        // Default reputation for new IPs
417        let reputation_score = 0.5;
418
419        Ok(IPAnalysis {
420            subnet_64,
421            subnet_48,
422            subnet_32,
423            asn,
424            country,
425            is_hosting_provider,
426            is_vpn_provider,
427            reputation_score,
428        })
429    }
430
431    /// Check if a new node can be accepted based on IP diversity constraints
432    pub fn can_accept_node(&self, ip_analysis: &IPAnalysis) -> bool {
433        // Determine limits based on hosting provider status
434        let (limit_64, limit_48, limit_32, limit_asn) =
435            if ip_analysis.is_hosting_provider || ip_analysis.is_vpn_provider {
436                // Stricter limits for hosting providers (halved)
437                (
438                    std::cmp::max(1, self.config.max_nodes_per_64 / 2),
439                    std::cmp::max(1, self.config.max_nodes_per_48 / 2),
440                    std::cmp::max(1, self.config.max_nodes_per_32 / 2),
441                    std::cmp::max(1, self.config.max_nodes_per_asn / 2),
442                )
443            } else {
444                // Regular limits for normal nodes
445                (
446                    self.config.max_nodes_per_64,
447                    self.config.max_nodes_per_48,
448                    self.config.max_nodes_per_32,
449                    self.config.max_nodes_per_asn,
450                )
451            };
452
453        // Check /64 subnet limit
454        if let Some(&count) = self.subnet_64_counts.get(&ip_analysis.subnet_64)
455            && count >= limit_64
456        {
457            return false;
458        }
459
460        // Check /48 subnet limit
461        if let Some(&count) = self.subnet_48_counts.get(&ip_analysis.subnet_48)
462            && count >= limit_48
463        {
464            return false;
465        }
466
467        // Check /32 subnet limit
468        if let Some(&count) = self.subnet_32_counts.get(&ip_analysis.subnet_32)
469            && count >= limit_32
470        {
471            return false;
472        }
473
474        // Check ASN limit
475        if let Some(asn) = ip_analysis.asn
476            && let Some(&count) = self.asn_counts.get(&asn)
477            && count >= limit_asn
478        {
479            return false;
480        }
481
482        true
483    }
484
485    /// Add a node to the diversity tracking
486    pub fn add_node(&mut self, ip_analysis: &IPAnalysis) -> Result<()> {
487        if !self.can_accept_node(ip_analysis) {
488            return Err(anyhow!("IP diversity limits exceeded"));
489        }
490
491        // Update counts
492        *self
493            .subnet_64_counts
494            .entry(ip_analysis.subnet_64)
495            .or_insert(0) += 1;
496        *self
497            .subnet_48_counts
498            .entry(ip_analysis.subnet_48)
499            .or_insert(0) += 1;
500        *self
501            .subnet_32_counts
502            .entry(ip_analysis.subnet_32)
503            .or_insert(0) += 1;
504
505        if let Some(asn) = ip_analysis.asn {
506            *self.asn_counts.entry(asn).or_insert(0) += 1;
507        }
508
509        if let Some(ref country) = ip_analysis.country {
510            *self.country_counts.entry(country.clone()).or_insert(0) += 1;
511        }
512
513        Ok(())
514    }
515
516    /// Remove a node from diversity tracking
517    pub fn remove_node(&mut self, ip_analysis: &IPAnalysis) {
518        if let Some(count) = self.subnet_64_counts.get_mut(&ip_analysis.subnet_64) {
519            *count = count.saturating_sub(1);
520            if *count == 0 {
521                self.subnet_64_counts.remove(&ip_analysis.subnet_64);
522            }
523        }
524
525        if let Some(count) = self.subnet_48_counts.get_mut(&ip_analysis.subnet_48) {
526            *count = count.saturating_sub(1);
527            if *count == 0 {
528                self.subnet_48_counts.remove(&ip_analysis.subnet_48);
529            }
530        }
531
532        if let Some(count) = self.subnet_32_counts.get_mut(&ip_analysis.subnet_32) {
533            *count = count.saturating_sub(1);
534            if *count == 0 {
535                self.subnet_32_counts.remove(&ip_analysis.subnet_32);
536            }
537        }
538
539        if let Some(asn) = ip_analysis.asn
540            && let Some(count) = self.asn_counts.get_mut(&asn)
541        {
542            *count = count.saturating_sub(1);
543            if *count == 0 {
544                self.asn_counts.remove(&asn);
545            }
546        }
547
548        if let Some(ref country) = ip_analysis.country
549            && let Some(count) = self.country_counts.get_mut(country)
550        {
551            *count = count.saturating_sub(1);
552            if *count == 0 {
553                self.country_counts.remove(country);
554            }
555        }
556    }
557
558    /// Extract network prefix of specified length from IPv6 address
559    pub fn extract_subnet_prefix(addr: Ipv6Addr, prefix_len: u8) -> Ipv6Addr {
560        let octets = addr.octets();
561        let mut subnet = [0u8; 16];
562
563        let bytes_to_copy = (prefix_len / 8) as usize;
564        let remaining_bits = prefix_len % 8;
565
566        // Copy full bytes
567        if bytes_to_copy < 16 {
568            subnet[..bytes_to_copy].copy_from_slice(&octets[..bytes_to_copy]);
569        } else {
570            subnet.copy_from_slice(&octets);
571        }
572
573        // Handle partial byte
574        if remaining_bits > 0 && bytes_to_copy < 16 {
575            let mask = 0xFF << (8 - remaining_bits);
576            subnet[bytes_to_copy] = octets[bytes_to_copy] & mask;
577        }
578
579        Ipv6Addr::from(subnet)
580    }
581
582    /// Get diversity statistics
583    pub fn get_diversity_stats(&self) -> DiversityStats {
584        DiversityStats {
585            total_64_subnets: self.subnet_64_counts.len(),
586            total_48_subnets: self.subnet_48_counts.len(),
587            total_32_subnets: self.subnet_32_counts.len(),
588            total_asns: self.asn_counts.len(),
589            total_countries: self.country_counts.len(),
590            max_nodes_per_64: self.subnet_64_counts.values().max().copied().unwrap_or(0),
591            max_nodes_per_48: self.subnet_48_counts.values().max().copied().unwrap_or(0),
592            max_nodes_per_32: self.subnet_32_counts.values().max().copied().unwrap_or(0),
593        }
594    }
595}
596
597/// GeoIP/ASN provider trait
598pub trait GeoProvider: std::fmt::Debug {
599    fn lookup(&self, ip: Ipv6Addr) -> GeoInfo;
600}
601
602/// Geo information
603#[derive(Debug, Clone)]
604pub struct GeoInfo {
605    pub asn: Option<u32>,
606    pub country: Option<String>,
607    pub is_hosting_provider: bool,
608    pub is_vpn_provider: bool,
609}
610
611/// A simple in-memory caching wrapper for a GeoProvider
612#[derive(Debug)]
613pub struct CachedGeoProvider<P: GeoProvider> {
614    inner: P,
615    cache: parking_lot::RwLock<HashMap<Ipv6Addr, GeoInfo>>,
616}
617
618impl<P: GeoProvider> CachedGeoProvider<P> {
619    pub fn new(inner: P) -> Self {
620        Self {
621            inner,
622            cache: parking_lot::RwLock::new(HashMap::new()),
623        }
624    }
625}
626
627impl<P: GeoProvider> GeoProvider for CachedGeoProvider<P> {
628    fn lookup(&self, ip: Ipv6Addr) -> GeoInfo {
629        if let Some(info) = self.cache.read().get(&ip).cloned() {
630            return info;
631        }
632        let info = self.inner.lookup(ip);
633        self.cache.write().insert(ip, info.clone());
634        info
635    }
636}
637
638/// Stub provider returning no ASN/GeoIP info
639#[derive(Debug)]
640pub struct StubGeoProvider;
641impl GeoProvider for StubGeoProvider {
642    fn lookup(&self, _ip: Ipv6Addr) -> GeoInfo {
643        GeoInfo {
644            asn: None,
645            country: None,
646            is_hosting_provider: false,
647            is_vpn_provider: false,
648        }
649    }
650}
651
652/// Diversity statistics for monitoring
653#[derive(Debug, Clone, Serialize, Deserialize)]
654pub struct DiversityStats {
655    /// Number of unique /64 subnets represented
656    pub total_64_subnets: usize,
657    /// Number of unique /48 subnets represented
658    pub total_48_subnets: usize,
659    /// Number of unique /32 subnets represented
660    pub total_32_subnets: usize,
661    /// Number of unique ASNs represented
662    pub total_asns: usize,
663    /// Number of unique countries represented
664    pub total_countries: usize,
665    /// Maximum nodes in any single /64 subnet
666    pub max_nodes_per_64: usize,
667    /// Maximum nodes in any single /48 subnet
668    pub max_nodes_per_48: usize,
669    /// Maximum nodes in any single /32 subnet
670    pub max_nodes_per_32: usize,
671}
672
673/// Reputation manager for tracking node behavior
674#[derive(Debug)]
675pub struct ReputationManager {
676    reputations: HashMap<PeerId, NodeReputation>,
677    reputation_decay: f64,
678    min_reputation: f64,
679}
680
681impl ReputationManager {
682    /// Create a new reputation manager
683    pub fn new(reputation_decay: f64, min_reputation: f64) -> Self {
684        Self {
685            reputations: HashMap::new(),
686            reputation_decay,
687            min_reputation,
688        }
689    }
690
691    /// Get reputation for a peer
692    pub fn get_reputation(&self, peer_id: &PeerId) -> Option<&NodeReputation> {
693        self.reputations.get(peer_id)
694    }
695
696    /// Update reputation based on interaction
697    pub fn update_reputation(&mut self, peer_id: &PeerId, success: bool, response_time: Duration) {
698        let reputation =
699            self.reputations
700                .entry(peer_id.clone())
701                .or_insert_with(|| NodeReputation {
702                    peer_id: peer_id.clone(),
703                    response_rate: 0.5,
704                    response_time: Duration::from_millis(500),
705                    consistency_score: 0.5,
706                    uptime_estimate: Duration::from_secs(0),
707                    routing_accuracy: 0.5,
708                    last_seen: SystemTime::now(),
709                    interaction_count: 0,
710                });
711
712        // Use higher learning rate for faster convergence in tests
713        let alpha = 0.3; // Increased from 0.1 for better test convergence
714
715        if success {
716            reputation.response_rate = reputation.response_rate * (1.0 - alpha) + alpha;
717        } else {
718            reputation.response_rate *= 1.0 - alpha;
719        }
720
721        // Update response time
722        let response_time_ms = response_time.as_millis() as f64;
723        let current_response_ms = reputation.response_time.as_millis() as f64;
724        let new_response_ms = current_response_ms * (1.0 - alpha) + response_time_ms * alpha;
725        reputation.response_time = Duration::from_millis(new_response_ms as u64);
726
727        reputation.last_seen = SystemTime::now();
728        reputation.interaction_count += 1;
729    }
730
731    /// Apply time-based reputation decay
732    pub fn apply_decay(&mut self) {
733        let now = SystemTime::now();
734
735        self.reputations.retain(|_, reputation| {
736            if let Ok(elapsed) = now.duration_since(reputation.last_seen) {
737                // Decay reputation over time
738                let decay_factor = (-elapsed.as_secs_f64() / 3600.0 * self.reputation_decay).exp();
739                reputation.response_rate *= decay_factor;
740                reputation.consistency_score *= decay_factor;
741                reputation.routing_accuracy *= decay_factor;
742
743                // Remove nodes with very low reputation
744                reputation.response_rate > self.min_reputation / 10.0
745            } else {
746                true
747            }
748        });
749    }
750}
751
752// Ed25519 compatibility removed
753
754#[cfg(test)]
755mod tests {
756    use super::*;
757    use crate::quantum_crypto::generate_ml_dsa_keypair;
758
759    fn create_test_keypair() -> (MlDsaPublicKey, MlDsaSecretKey) {
760        generate_ml_dsa_keypair().expect("Failed to generate test keypair")
761    }
762
763    fn create_test_ipv6() -> Ipv6Addr {
764        Ipv6Addr::new(
765            0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334,
766        )
767    }
768
769    fn create_test_diversity_config() -> IPDiversityConfig {
770        IPDiversityConfig {
771            max_nodes_per_64: 1,
772            max_nodes_per_48: 3,
773            max_nodes_per_32: 10,
774            max_nodes_per_asn: 20,
775            enable_geolocation_check: true,
776            min_geographic_diversity: 3,
777        }
778    }
779
780    #[test]
781    fn test_ipv6_node_id_generation() -> Result<()> {
782        let (public_key, secret_key) = create_test_keypair();
783        let ipv6_addr = create_test_ipv6();
784
785        let node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
786
787        assert_eq!(node_id.ipv6_addr, ipv6_addr);
788        assert_eq!(node_id.public_key.len(), 1952); // ML-DSA-65 public key size
789        assert_eq!(node_id.signature.len(), 3309); // ML-DSA-65 signature size
790        assert_eq!(node_id.node_id.len(), 32); // SHA256 output
791        assert_eq!(node_id.salt.len(), 16);
792        assert!(node_id.timestamp_secs > 0);
793
794        Ok(())
795    }
796
797    #[test]
798    fn test_ipv6_node_id_verification() -> Result<()> {
799        let (public_key, secret_key) = create_test_keypair();
800        let ipv6_addr = create_test_ipv6();
801
802        let node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
803        let is_valid = node_id.verify()?;
804
805        assert!(is_valid);
806
807        Ok(())
808    }
809
810    #[test]
811    fn test_ipv6_node_id_verification_fails_with_wrong_data() -> Result<()> {
812        let (public_key, secret_key) = create_test_keypair();
813        let ipv6_addr = create_test_ipv6();
814
815        let mut node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
816
817        // Tamper with the node ID
818        node_id.node_id[0] ^= 0xFF;
819        let is_valid = node_id.verify()?;
820        assert!(!is_valid);
821
822        // Test with wrong signature length
823        let mut node_id2 = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
824        node_id2.signature = vec![0u8; 32]; // Wrong length (should be 3309 for ML-DSA)
825        let is_valid2 = node_id2.verify()?;
826        assert!(!is_valid2);
827
828        // Test with wrong public key length
829        let mut node_id3 = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
830        node_id3.public_key = vec![0u8; 16]; // Wrong length (should be 1952 for ML-DSA-65)
831        let is_valid3 = node_id3.verify()?;
832        assert!(!is_valid3);
833
834        Ok(())
835    }
836
837    #[test]
838    fn test_ipv6_subnet_extraction() -> Result<()> {
839        let (public_key, secret_key) = create_test_keypair();
840        let ipv6_addr = Ipv6Addr::new(
841            0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
842        );
843
844        let node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
845
846        // Test /64 subnet extraction
847        let subnet_64 = node_id.extract_subnet_64();
848        let expected_64 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0);
849        assert_eq!(subnet_64, expected_64);
850
851        // Test /48 subnet extraction
852        let subnet_48 = node_id.extract_subnet_48();
853        let expected_48 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0);
854        assert_eq!(subnet_48, expected_48);
855
856        // Test /32 subnet extraction
857        let subnet_32 = node_id.extract_subnet_32();
858        let expected_32 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
859        assert_eq!(subnet_32, expected_32);
860
861        Ok(())
862    }
863
864    // =========== IPv4 Node ID Tests ===========
865
866    fn create_test_ipv4() -> Ipv4Addr {
867        Ipv4Addr::new(192, 168, 1, 100)
868    }
869
870    #[test]
871    fn test_ipv4_node_id_generation() -> Result<()> {
872        let (public_key, secret_key) = create_test_keypair();
873        let ipv4_addr = create_test_ipv4();
874
875        let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
876
877        assert_eq!(node_id.ipv4_addr, ipv4_addr);
878        assert_eq!(node_id.public_key.len(), 1952); // ML-DSA-65 public key size
879        assert_eq!(node_id.signature.len(), 3309); // ML-DSA-65 signature size
880        assert_eq!(node_id.node_id.len(), 32); // SHA256 output
881        assert_eq!(node_id.salt.len(), 16);
882        assert!(node_id.timestamp_secs > 0);
883
884        Ok(())
885    }
886
887    #[test]
888    fn test_ipv4_node_id_verification() -> Result<()> {
889        let (public_key, secret_key) = create_test_keypair();
890        let ipv4_addr = create_test_ipv4();
891
892        let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
893        let is_valid = node_id.verify()?;
894
895        assert!(is_valid);
896
897        Ok(())
898    }
899
900    #[test]
901    fn test_ipv4_node_id_verification_fails_with_wrong_data() -> Result<()> {
902        let (public_key, secret_key) = create_test_keypair();
903        let ipv4_addr = create_test_ipv4();
904
905        let mut node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
906
907        // Tamper with the node ID
908        node_id.node_id[0] ^= 0xFF;
909        let is_valid = node_id.verify()?;
910        assert!(!is_valid);
911
912        // Test with wrong signature length
913        let mut node_id2 = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
914        node_id2.signature = vec![0u8; 32]; // Wrong length (should be 3309 for ML-DSA)
915        let is_valid2 = node_id2.verify()?;
916        assert!(!is_valid2);
917
918        // Test with wrong public key length
919        let mut node_id3 = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
920        node_id3.public_key = vec![0u8; 16]; // Wrong length (should be 1952 for ML-DSA-65)
921        let is_valid3 = node_id3.verify()?;
922        assert!(!is_valid3);
923
924        Ok(())
925    }
926
927    #[test]
928    fn test_ipv4_subnet_extraction() -> Result<()> {
929        let (public_key, secret_key) = create_test_keypair();
930        let ipv4_addr = Ipv4Addr::new(192, 168, 42, 100);
931
932        let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
933
934        // Test /24 subnet extraction
935        let subnet_24 = node_id.extract_subnet_24();
936        let expected_24 = Ipv4Addr::new(192, 168, 42, 0);
937        assert_eq!(subnet_24, expected_24);
938
939        // Test /16 subnet extraction
940        let subnet_16 = node_id.extract_subnet_16();
941        let expected_16 = Ipv4Addr::new(192, 168, 0, 0);
942        assert_eq!(subnet_16, expected_16);
943
944        // Test /8 subnet extraction
945        let subnet_8 = node_id.extract_subnet_8();
946        let expected_8 = Ipv4Addr::new(192, 0, 0, 0);
947        assert_eq!(subnet_8, expected_8);
948
949        Ok(())
950    }
951
952    #[test]
953    fn test_ipv4_node_id_age() -> Result<()> {
954        let (public_key, secret_key) = create_test_keypair();
955        let ipv4_addr = create_test_ipv4();
956
957        let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
958
959        // Age should be very small (just created)
960        assert!(node_id.age_secs() < 5);
961
962        // Not expired with a 1 hour max age
963        assert!(!node_id.is_expired(Duration::from_secs(3600)));
964
965        // A freshly created node with 0 age is NOT expired (0 > 0 is false)
966        // This is correct behavior - a 0-second-old node is not older than 0 seconds
967        assert!(!node_id.is_expired(Duration::from_secs(0)));
968
969        Ok(())
970    }
971
972    #[test]
973    fn test_ipv4_different_addresses_different_node_ids() -> Result<()> {
974        let (public_key, secret_key) = create_test_keypair();
975        let addr1 = Ipv4Addr::new(192, 168, 1, 1);
976        let addr2 = Ipv4Addr::new(192, 168, 1, 2);
977
978        let node_id1 = IPv4NodeID::generate(addr1, &secret_key, &public_key)?;
979        let node_id2 = IPv4NodeID::generate(addr2, &secret_key, &public_key)?;
980
981        // Different addresses should produce different node IDs
982        assert_ne!(node_id1.node_id, node_id2.node_id);
983
984        // Both should verify successfully
985        assert!(node_id1.verify()?);
986        assert!(node_id2.verify()?);
987
988        Ok(())
989    }
990
991    // =========== End IPv4 Tests ===========
992
993    #[test]
994    fn test_ip_diversity_config_default() {
995        let config = IPDiversityConfig::default();
996
997        assert_eq!(config.max_nodes_per_64, 1);
998        assert_eq!(config.max_nodes_per_48, 3);
999        assert_eq!(config.max_nodes_per_32, 10);
1000        assert_eq!(config.max_nodes_per_asn, 20);
1001        assert!(config.enable_geolocation_check);
1002        assert_eq!(config.min_geographic_diversity, 3);
1003    }
1004
1005    #[test]
1006    fn test_ip_diversity_enforcer_creation() {
1007        let config = create_test_diversity_config();
1008        let enforcer = IPDiversityEnforcer::new(config.clone());
1009
1010        assert_eq!(enforcer.config.max_nodes_per_64, config.max_nodes_per_64);
1011        assert_eq!(enforcer.subnet_64_counts.len(), 0);
1012        assert_eq!(enforcer.subnet_48_counts.len(), 0);
1013        assert_eq!(enforcer.subnet_32_counts.len(), 0);
1014    }
1015
1016    #[test]
1017    fn test_ip_analysis() -> Result<()> {
1018        let config = create_test_diversity_config();
1019        let enforcer = IPDiversityEnforcer::new(config);
1020
1021        let ipv6_addr = create_test_ipv6();
1022        let analysis = enforcer.analyze_ip(ipv6_addr)?;
1023
1024        assert_eq!(
1025            analysis.subnet_64,
1026            IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 64)
1027        );
1028        assert_eq!(
1029            analysis.subnet_48,
1030            IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 48)
1031        );
1032        assert_eq!(
1033            analysis.subnet_32,
1034            IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 32)
1035        );
1036        assert!(analysis.asn.is_none()); // Not implemented in test
1037        assert!(analysis.country.is_none()); // Not implemented in test
1038        assert!(!analysis.is_hosting_provider);
1039        assert!(!analysis.is_vpn_provider);
1040        assert_eq!(analysis.reputation_score, 0.5);
1041
1042        Ok(())
1043    }
1044
1045    #[test]
1046    fn test_can_accept_node_basic() -> Result<()> {
1047        let config = create_test_diversity_config();
1048        let enforcer = IPDiversityEnforcer::new(config);
1049
1050        let ipv6_addr = create_test_ipv6();
1051        let analysis = enforcer.analyze_ip(ipv6_addr)?;
1052
1053        // Should accept first node
1054        assert!(enforcer.can_accept_node(&analysis));
1055
1056        Ok(())
1057    }
1058
1059    #[test]
1060    fn test_add_and_remove_node() -> Result<()> {
1061        let config = create_test_diversity_config();
1062        let mut enforcer = IPDiversityEnforcer::new(config);
1063
1064        let ipv6_addr = create_test_ipv6();
1065        let analysis = enforcer.analyze_ip(ipv6_addr)?;
1066
1067        // Add node
1068        enforcer.add_node(&analysis)?;
1069        assert_eq!(enforcer.subnet_64_counts.get(&analysis.subnet_64), Some(&1));
1070        assert_eq!(enforcer.subnet_48_counts.get(&analysis.subnet_48), Some(&1));
1071        assert_eq!(enforcer.subnet_32_counts.get(&analysis.subnet_32), Some(&1));
1072
1073        // Remove node
1074        enforcer.remove_node(&analysis);
1075        assert_eq!(enforcer.subnet_64_counts.get(&analysis.subnet_64), None);
1076        assert_eq!(enforcer.subnet_48_counts.get(&analysis.subnet_48), None);
1077        assert_eq!(enforcer.subnet_32_counts.get(&analysis.subnet_32), None);
1078
1079        Ok(())
1080    }
1081
1082    #[test]
1083    fn test_diversity_limits_enforcement() -> Result<()> {
1084        let config = create_test_diversity_config();
1085        let mut enforcer = IPDiversityEnforcer::new(config);
1086
1087        let ipv6_addr1 = Ipv6Addr::new(
1088            0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1089        );
1090        let ipv6_addr2 = Ipv6Addr::new(
1091            0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7335,
1092        ); // Same /64
1093
1094        let analysis1 = enforcer.analyze_ip(ipv6_addr1)?;
1095        let analysis2 = enforcer.analyze_ip(ipv6_addr2)?;
1096
1097        // First node should be accepted
1098        assert!(enforcer.can_accept_node(&analysis1));
1099        enforcer.add_node(&analysis1)?;
1100
1101        // Second node in same /64 should be rejected (max_nodes_per_64 = 1)
1102        assert!(!enforcer.can_accept_node(&analysis2));
1103
1104        // But adding should fail
1105        let result = enforcer.add_node(&analysis2);
1106        assert!(result.is_err());
1107        assert!(
1108            result
1109                .unwrap_err()
1110                .to_string()
1111                .contains("IP diversity limits exceeded")
1112        );
1113
1114        Ok(())
1115    }
1116
1117    #[test]
1118    fn test_hosting_provider_stricter_limits() -> Result<()> {
1119        let config = IPDiversityConfig {
1120            max_nodes_per_64: 4, // Set higher limit for regular nodes
1121            max_nodes_per_48: 8,
1122            ..create_test_diversity_config()
1123        };
1124        let mut enforcer = IPDiversityEnforcer::new(config);
1125
1126        let ipv6_addr = create_test_ipv6();
1127        let mut analysis = enforcer.analyze_ip(ipv6_addr)?;
1128        analysis.is_hosting_provider = true;
1129
1130        // Should accept first hosting provider node
1131        assert!(enforcer.can_accept_node(&analysis));
1132        enforcer.add_node(&analysis)?;
1133
1134        // Add second hosting provider node in same /64 (should be accepted with limit=2)
1135        let ipv6_addr2 = Ipv6Addr::new(
1136            0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7335,
1137        );
1138        let mut analysis2 = enforcer.analyze_ip(ipv6_addr2)?;
1139        analysis2.is_hosting_provider = true;
1140        analysis2.subnet_64 = analysis.subnet_64; // Force same subnet
1141
1142        assert!(enforcer.can_accept_node(&analysis2));
1143        enforcer.add_node(&analysis2)?;
1144
1145        // Should reject third hosting provider node in same /64 (exceeds limit=2)
1146        let ipv6_addr3 = Ipv6Addr::new(
1147            0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7336,
1148        );
1149        let mut analysis3 = enforcer.analyze_ip(ipv6_addr3)?;
1150        analysis3.is_hosting_provider = true;
1151        analysis3.subnet_64 = analysis.subnet_64; // Force same subnet
1152
1153        assert!(!enforcer.can_accept_node(&analysis3));
1154
1155        Ok(())
1156    }
1157
1158    #[test]
1159    fn test_diversity_stats() -> Result<()> {
1160        let config = create_test_diversity_config();
1161        let mut enforcer = IPDiversityEnforcer::new(config);
1162
1163        // Add some nodes with different subnets
1164        let addresses = [
1165            Ipv6Addr::new(
1166                0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1167            ),
1168            Ipv6Addr::new(
1169                0x2001, 0xdb8, 0x85a4, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1170            ), // Different /48
1171            Ipv6Addr::new(
1172                0x2002, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1173            ), // Different /32
1174        ];
1175
1176        for addr in addresses {
1177            let analysis = enforcer.analyze_ip(addr)?;
1178            enforcer.add_node(&analysis)?;
1179        }
1180
1181        let stats = enforcer.get_diversity_stats();
1182        assert_eq!(stats.total_64_subnets, 3);
1183        assert_eq!(stats.total_48_subnets, 3);
1184        assert_eq!(stats.total_32_subnets, 2); // Two /32 prefixes
1185        assert_eq!(stats.max_nodes_per_64, 1);
1186        assert_eq!(stats.max_nodes_per_48, 1);
1187        assert_eq!(stats.max_nodes_per_32, 2); // 2001:db8 has 2 nodes
1188
1189        Ok(())
1190    }
1191
1192    #[test]
1193    fn test_extract_subnet_prefix() {
1194        let addr = Ipv6Addr::new(
1195            0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1196        );
1197
1198        // Test /64 prefix
1199        let prefix_64 = IPDiversityEnforcer::extract_subnet_prefix(addr, 64);
1200        let expected_64 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0);
1201        assert_eq!(prefix_64, expected_64);
1202
1203        // Test /48 prefix
1204        let prefix_48 = IPDiversityEnforcer::extract_subnet_prefix(addr, 48);
1205        let expected_48 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0);
1206        assert_eq!(prefix_48, expected_48);
1207
1208        // Test /32 prefix
1209        let prefix_32 = IPDiversityEnforcer::extract_subnet_prefix(addr, 32);
1210        let expected_32 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
1211        assert_eq!(prefix_32, expected_32);
1212
1213        // Test /56 prefix (partial byte)
1214        let prefix_56 = IPDiversityEnforcer::extract_subnet_prefix(addr, 56);
1215        let expected_56 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1200, 0, 0, 0, 0);
1216        assert_eq!(prefix_56, expected_56);
1217
1218        // Test /128 prefix (full address)
1219        let prefix_128 = IPDiversityEnforcer::extract_subnet_prefix(addr, 128);
1220        assert_eq!(prefix_128, addr);
1221    }
1222
1223    #[test]
1224    fn test_reputation_manager_creation() {
1225        let manager = ReputationManager::new(0.1, 0.1);
1226        assert_eq!(manager.reputation_decay, 0.1);
1227        assert_eq!(manager.min_reputation, 0.1);
1228        assert_eq!(manager.reputations.len(), 0);
1229    }
1230
1231    #[test]
1232    fn test_reputation_get_nonexistent() {
1233        let manager = ReputationManager::new(0.1, 0.1);
1234        let peer_id = "test_peer".to_string();
1235
1236        let reputation = manager.get_reputation(&peer_id);
1237        assert!(reputation.is_none());
1238    }
1239
1240    #[test]
1241    fn test_reputation_update_creates_entry() {
1242        let mut manager = ReputationManager::new(0.1, 0.1);
1243        let peer_id = "test_peer".to_string();
1244
1245        manager.update_reputation(&peer_id, true, Duration::from_millis(100));
1246
1247        let reputation = manager.get_reputation(&peer_id);
1248        assert!(reputation.is_some());
1249
1250        let rep = reputation.unwrap();
1251        assert_eq!(rep.peer_id, peer_id);
1252        assert!(rep.response_rate > 0.5); // Should increase from initial 0.5
1253        assert_eq!(rep.interaction_count, 1);
1254    }
1255
1256    #[test]
1257    fn test_reputation_update_success_improves_rate() {
1258        let mut manager = ReputationManager::new(0.1, 0.1);
1259        let peer_id = "test_peer".to_string();
1260
1261        // Multiple successful interactions
1262        for _ in 0..15 {
1263            manager.update_reputation(&peer_id, true, Duration::from_millis(100));
1264        }
1265
1266        let reputation = manager.get_reputation(&peer_id).unwrap();
1267        assert!(reputation.response_rate > 0.85); // Should be very high with higher learning rate
1268        assert_eq!(reputation.interaction_count, 15);
1269    }
1270
1271    #[test]
1272    fn test_reputation_update_failure_decreases_rate() {
1273        let mut manager = ReputationManager::new(0.1, 0.1);
1274        let peer_id = "test_peer".to_string();
1275
1276        // Multiple failed interactions
1277        for _ in 0..15 {
1278            manager.update_reputation(&peer_id, false, Duration::from_millis(1000));
1279        }
1280
1281        let reputation = manager.get_reputation(&peer_id).unwrap();
1282        assert!(reputation.response_rate < 0.15); // Should be very low with higher learning rate
1283        assert_eq!(reputation.interaction_count, 15);
1284    }
1285
1286    #[test]
1287    fn test_reputation_response_time_tracking() {
1288        let mut manager = ReputationManager::new(0.1, 0.1);
1289        let peer_id = "test_peer".to_string();
1290
1291        // Update with specific response time
1292        manager.update_reputation(&peer_id, true, Duration::from_millis(200));
1293
1294        let reputation = manager.get_reputation(&peer_id).unwrap();
1295        // Response time should be between initial 500ms and new 200ms
1296        assert!(reputation.response_time.as_millis() > 200);
1297        assert!(reputation.response_time.as_millis() < 500);
1298    }
1299
1300    #[test]
1301    fn test_reputation_decay() {
1302        let mut manager = ReputationManager::new(1.0, 0.01); // High decay rate
1303        let peer_id = "test_peer".to_string();
1304
1305        // Create a reputation entry
1306        manager.update_reputation(&peer_id, true, Duration::from_millis(100));
1307
1308        // Manually set last_seen to past
1309        if let Some(reputation) = manager.reputations.get_mut(&peer_id) {
1310            reputation.last_seen = SystemTime::now() - Duration::from_secs(7200); // 2 hours ago
1311        }
1312
1313        let original_rate = manager.get_reputation(&peer_id).unwrap().response_rate;
1314
1315        // Apply decay
1316        manager.apply_decay();
1317
1318        let reputation = manager.get_reputation(&peer_id);
1319        if let Some(rep) = reputation {
1320            // Should have decayed
1321            assert!(rep.response_rate < original_rate);
1322        } // else the reputation was removed due to low score
1323    }
1324
1325    #[test]
1326    fn test_reputation_decay_removes_low_reputation() {
1327        let mut manager = ReputationManager::new(0.1, 0.5); // High min reputation
1328        let peer_id = "test_peer".to_string();
1329
1330        // Create a low reputation entry
1331        for _ in 0..10 {
1332            manager.update_reputation(&peer_id, false, Duration::from_millis(1000));
1333        }
1334
1335        // Manually set last_seen to past
1336        if let Some(reputation) = manager.reputations.get_mut(&peer_id) {
1337            reputation.last_seen = SystemTime::now() - Duration::from_secs(3600); // 1 hour ago
1338            reputation.response_rate = 0.01; // Very low
1339        }
1340
1341        // Apply decay
1342        manager.apply_decay();
1343
1344        // Should be removed
1345        assert!(manager.get_reputation(&peer_id).is_none());
1346    }
1347
1348    #[test]
1349    fn test_security_types_keypair() {
1350        let (public_key, secret_key) =
1351            generate_ml_dsa_keypair().expect("Failed to generate keypair");
1352
1353        let public_key_bytes = public_key.as_bytes();
1354        assert_eq!(public_key_bytes.len(), 1952); // ML-DSA-65 public key size
1355
1356        let message = b"test message";
1357        let signature = ml_dsa_sign(&secret_key, message).expect("Failed to sign message");
1358        assert_eq!(signature.as_bytes().len(), 3309); // ML-DSA-65 signature size
1359
1360        // Verify the signature
1361        assert!(ml_dsa_verify(&public_key, message, &signature).is_ok());
1362    }
1363
1364    #[test]
1365    fn test_node_reputation_structure() {
1366        let peer_id = "test_peer".to_string();
1367        let reputation = NodeReputation {
1368            peer_id: peer_id.clone(),
1369            response_rate: 0.85,
1370            response_time: Duration::from_millis(150),
1371            consistency_score: 0.9,
1372            uptime_estimate: Duration::from_secs(86400),
1373            routing_accuracy: 0.8,
1374            last_seen: SystemTime::now(),
1375            interaction_count: 42,
1376        };
1377
1378        assert_eq!(reputation.peer_id, peer_id);
1379        assert_eq!(reputation.response_rate, 0.85);
1380        assert_eq!(reputation.response_time, Duration::from_millis(150));
1381        assert_eq!(reputation.consistency_score, 0.9);
1382        assert_eq!(reputation.uptime_estimate, Duration::from_secs(86400));
1383        assert_eq!(reputation.routing_accuracy, 0.8);
1384        assert_eq!(reputation.interaction_count, 42);
1385    }
1386
1387    #[test]
1388    fn test_ip_analysis_structure() {
1389        let analysis = IPAnalysis {
1390            subnet_64: Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0),
1391            subnet_48: Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0),
1392            subnet_32: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
1393            asn: Some(64512),
1394            country: Some("US".to_string()),
1395            is_hosting_provider: true,
1396            is_vpn_provider: false,
1397            reputation_score: 0.75,
1398        };
1399
1400        assert_eq!(analysis.asn, Some(64512));
1401        assert_eq!(analysis.country, Some("US".to_string()));
1402        assert!(analysis.is_hosting_provider);
1403        assert!(!analysis.is_vpn_provider);
1404        assert_eq!(analysis.reputation_score, 0.75);
1405    }
1406
1407    #[test]
1408    fn test_diversity_stats_structure() {
1409        let stats = DiversityStats {
1410            total_64_subnets: 100,
1411            total_48_subnets: 50,
1412            total_32_subnets: 25,
1413            total_asns: 15,
1414            total_countries: 8,
1415            max_nodes_per_64: 1,
1416            max_nodes_per_48: 3,
1417            max_nodes_per_32: 10,
1418        };
1419
1420        assert_eq!(stats.total_64_subnets, 100);
1421        assert_eq!(stats.total_48_subnets, 50);
1422        assert_eq!(stats.total_32_subnets, 25);
1423        assert_eq!(stats.total_asns, 15);
1424        assert_eq!(stats.total_countries, 8);
1425        assert_eq!(stats.max_nodes_per_64, 1);
1426        assert_eq!(stats.max_nodes_per_48, 3);
1427        assert_eq!(stats.max_nodes_per_32, 10);
1428    }
1429
1430    #[test]
1431    fn test_multiple_same_subnet_nodes() -> Result<()> {
1432        let config = IPDiversityConfig {
1433            max_nodes_per_64: 3, // Allow more nodes in same /64
1434            max_nodes_per_48: 5,
1435            max_nodes_per_32: 10,
1436            ..create_test_diversity_config()
1437        };
1438        let mut enforcer = IPDiversityEnforcer::new(config);
1439
1440        let _base_addr = Ipv6Addr::new(
1441            0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x0000,
1442        );
1443
1444        // Add 3 nodes in same /64 subnet
1445        for i in 1..=3 {
1446            let addr = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, i);
1447            let analysis = enforcer.analyze_ip(addr)?;
1448            assert!(enforcer.can_accept_node(&analysis));
1449            enforcer.add_node(&analysis)?;
1450        }
1451
1452        // 4th node should be rejected
1453        let addr4 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 4);
1454        let analysis4 = enforcer.analyze_ip(addr4)?;
1455        assert!(!enforcer.can_accept_node(&analysis4));
1456
1457        let stats = enforcer.get_diversity_stats();
1458        assert_eq!(stats.total_64_subnets, 1);
1459        assert_eq!(stats.max_nodes_per_64, 3);
1460
1461        Ok(())
1462    }
1463
1464    #[test]
1465    fn test_asn_and_country_tracking() -> Result<()> {
1466        let config = create_test_diversity_config();
1467        let mut enforcer = IPDiversityEnforcer::new(config);
1468
1469        // Create analysis with ASN and country
1470        let ipv6_addr = create_test_ipv6();
1471        let mut analysis = enforcer.analyze_ip(ipv6_addr)?;
1472        analysis.asn = Some(64512);
1473        analysis.country = Some("US".to_string());
1474
1475        enforcer.add_node(&analysis)?;
1476
1477        assert_eq!(enforcer.asn_counts.get(&64512), Some(&1));
1478        assert_eq!(enforcer.country_counts.get("US"), Some(&1));
1479
1480        // Remove and check cleanup
1481        enforcer.remove_node(&analysis);
1482        assert!(!enforcer.asn_counts.contains_key(&64512));
1483        assert!(!enforcer.country_counts.contains_key("US"));
1484
1485        Ok(())
1486    }
1487
1488    #[test]
1489    fn test_reputation_mixed_interactions() {
1490        let mut manager = ReputationManager::new(0.1, 0.1);
1491        let peer_id = "test_peer".to_string();
1492
1493        // Mix of successful and failed interactions
1494        for i in 0..15 {
1495            let success = i % 3 != 0; // 2/3 success rate
1496            manager.update_reputation(&peer_id, success, Duration::from_millis(100 + i * 10));
1497        }
1498
1499        let reputation = manager.get_reputation(&peer_id).unwrap();
1500        // Should converge closer to 2/3 with more iterations and higher learning rate
1501        // With alpha=0.3 and 2/3 success rate, convergence may be higher
1502        assert!(reputation.response_rate > 0.55);
1503        assert!(reputation.response_rate < 0.85);
1504        assert_eq!(reputation.interaction_count, 15);
1505    }
1506}