ant_core/
security.rs

1//! Security module
2//!
3//! This module provides cryptographic functionality and Sybil protection for the P2P network.
4//! It implements IPv6-based node ID generation and IP diversity enforcement to prevent
5//! large-scale Sybil attacks while maintaining network openness.
6
7use crate::PeerId;
8use anyhow::{anyhow, Result};
9use ed25519_dalek::{Keypair, PublicKey, Signature, Signer, Verifier};
10use sha2::{Digest, Sha256};
11use std::collections::HashMap;
12use std::net::Ipv6Addr;
13use std::time::{Duration, SystemTime, UNIX_EPOCH};
14use serde::{Deserialize, Serialize};
15
16/// IPv6-based node identity that binds node ID to actual network location
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct IPv6NodeID {
19    /// Derived node ID (SHA256 of ipv6_addr + public_key + salt)
20    pub node_id: Vec<u8>,
21    /// IPv6 address this node ID is bound to
22    pub ipv6_addr: Ipv6Addr,
23    /// Ed25519 public key for signatures
24    pub public_key: Vec<u8>,
25    /// Signature proving ownership of the IPv6 address and keys
26    pub signature: Vec<u8>,
27    /// Timestamp when this ID was generated (seconds since epoch)
28    pub timestamp_secs: u64,
29    /// Salt used in node ID generation (for freshness)
30    pub salt: Vec<u8>,
31}
32
33/// Configuration for IP diversity enforcement at multiple subnet levels
34#[derive(Debug, Clone)]
35pub struct IPDiversityConfig {
36    /// Maximum nodes per /64 subnet (default: 1)
37    pub max_nodes_per_64: usize,
38    /// Maximum nodes per /48 allocation (default: 3)  
39    pub max_nodes_per_48: usize,
40    /// Maximum nodes per /32 region (default: 10)
41    pub max_nodes_per_32: usize,
42    /// Maximum nodes per AS number (default: 20)
43    pub max_nodes_per_asn: usize,
44    /// Enable GeoIP-based diversity checks
45    pub enable_geolocation_check: bool,
46    /// Minimum number of different countries required
47    pub min_geographic_diversity: usize,
48}
49
50/// Analysis of an IPv6 address for diversity enforcement
51#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
52pub struct IPAnalysis {
53    /// /64 subnet (host allocation)
54    pub subnet_64: Ipv6Addr,
55    /// /48 subnet (site allocation)
56    pub subnet_48: Ipv6Addr,
57    /// /32 subnet (ISP allocation)
58    pub subnet_32: Ipv6Addr,
59    /// Autonomous System Number (if available)
60    pub asn: Option<u32>,
61    /// Country code from GeoIP lookup
62    pub country: Option<String>,
63    /// Whether this is a known hosting/VPS provider
64    pub is_hosting_provider: bool,
65    /// Whether this is a known VPN provider
66    pub is_vpn_provider: bool,
67    /// Historical reputation score for this IP range
68    pub reputation_score: f64,
69}
70
71/// Node reputation tracking for security-aware routing
72#[derive(Debug, Clone)]
73pub struct NodeReputation {
74    /// Peer ID
75    pub peer_id: PeerId,
76    /// Fraction of queries answered successfully
77    pub response_rate: f64,
78    /// Average response time
79    pub response_time: Duration,
80    /// Consistency of provided data (0.0-1.0)
81    pub consistency_score: f64,
82    /// Estimated continuous uptime
83    pub uptime_estimate: Duration,
84    /// Accuracy of routing information provided
85    pub routing_accuracy: f64,
86    /// Last time this node was seen
87    pub last_seen: SystemTime,
88    /// Total number of interactions
89    pub interaction_count: u64,
90}
91
92impl Default for IPDiversityConfig {
93    fn default() -> Self {
94        Self {
95            max_nodes_per_64: 1,
96            max_nodes_per_48: 3,
97            max_nodes_per_32: 10,
98            max_nodes_per_asn: 20,
99            enable_geolocation_check: true,
100            min_geographic_diversity: 3,
101        }
102    }
103}
104
105impl IPv6NodeID {
106    /// Generate a new IPv6-based node ID
107    pub fn generate(ipv6_addr: Ipv6Addr, keypair: &Keypair) -> Result<Self> {
108        let mut rng = rand::thread_rng();
109        let mut salt = vec![0u8; 16];
110        rand::RngCore::fill_bytes(&mut rng, &mut salt);
111        
112        let timestamp = SystemTime::now();
113        let timestamp_secs = timestamp.duration_since(UNIX_EPOCH)?.as_secs();
114        let public_key = keypair.public.to_bytes().to_vec();
115        
116        // Generate node ID: SHA256(ipv6_address || public_key || salt || timestamp)
117        let mut hasher = Sha256::new();
118        hasher.update(ipv6_addr.octets());
119        hasher.update(&public_key);
120        hasher.update(&salt);
121        hasher.update(&timestamp_secs.to_le_bytes());
122        let node_id = hasher.finalize().to_vec();
123        
124        // Create signature proving ownership
125        let mut message_to_sign = Vec::new();
126        message_to_sign.extend_from_slice(&ipv6_addr.octets());
127        message_to_sign.extend_from_slice(&public_key);
128        message_to_sign.extend_from_slice(&salt);
129        message_to_sign.extend_from_slice(&timestamp_secs.to_le_bytes());
130        
131        let signature = keypair.sign(&message_to_sign).to_bytes().to_vec();
132        
133        Ok(IPv6NodeID {
134            node_id,
135            ipv6_addr,
136            public_key,
137            signature,
138            timestamp_secs,
139            salt,
140        })
141    }
142    
143    /// Verify that this node ID is valid and properly signed
144    pub fn verify(&self) -> Result<bool> {
145        // Reconstruct the node ID
146        let mut hasher = Sha256::new();
147        hasher.update(self.ipv6_addr.octets());
148        hasher.update(&self.public_key);
149        hasher.update(&self.salt);
150        hasher.update(&self.timestamp_secs.to_le_bytes());
151        let expected_node_id = hasher.finalize();
152        
153        // Verify node ID matches
154        if expected_node_id.as_slice() != &self.node_id {
155            return Ok(false);
156        }
157        
158        // Verify signature
159        if self.public_key.len() != 32 {
160            return Ok(false);
161        }
162        if self.signature.len() != 64 {
163            return Ok(false);
164        }
165        
166        let mut pk_bytes = [0u8; 32];
167        pk_bytes.copy_from_slice(&self.public_key);
168        let public_key = PublicKey::from_bytes(&pk_bytes)
169            .map_err(|e| anyhow!("Invalid public key: {}", e))?;
170            
171        let mut sig_bytes = [0u8; 64];
172        sig_bytes.copy_from_slice(&self.signature);
173        let signature = Signature::from_bytes(&sig_bytes)
174            .map_err(|e| anyhow!("Invalid signature: {}", e))?;
175        
176        let mut message_to_verify = Vec::new();
177        message_to_verify.extend_from_slice(&self.ipv6_addr.octets());
178        message_to_verify.extend_from_slice(&self.public_key);
179        message_to_verify.extend_from_slice(&self.salt);
180        message_to_verify.extend_from_slice(&self.timestamp_secs.to_le_bytes());
181        
182        match public_key.verify(&message_to_verify, &signature) {
183            Ok(_) => Ok(true),
184            Err(_) => Ok(false),
185        }
186    }
187    
188    /// Extract /64 subnet from IPv6 address
189    pub fn extract_subnet_64(&self) -> Ipv6Addr {
190        let octets = self.ipv6_addr.octets();
191        let mut subnet = [0u8; 16];
192        subnet[..8].copy_from_slice(&octets[..8]); // Keep first 64 bits, zero the rest
193        Ipv6Addr::from(subnet)
194    }
195    
196    /// Extract /48 subnet from IPv6 address
197    pub fn extract_subnet_48(&self) -> Ipv6Addr {
198        let octets = self.ipv6_addr.octets();
199        let mut subnet = [0u8; 16];
200        subnet[..6].copy_from_slice(&octets[..6]); // Keep first 48 bits, zero the rest
201        Ipv6Addr::from(subnet)
202    }
203    
204    /// Extract /32 subnet from IPv6 address
205    pub fn extract_subnet_32(&self) -> Ipv6Addr {
206        let octets = self.ipv6_addr.octets();
207        let mut subnet = [0u8; 16];
208        subnet[..4].copy_from_slice(&octets[..4]); // Keep first 32 bits, zero the rest
209        Ipv6Addr::from(subnet)
210    }
211}
212
213/// IP diversity enforcement system
214#[derive(Debug)]
215pub struct IPDiversityEnforcer {
216    config: IPDiversityConfig,
217    subnet_64_counts: HashMap<Ipv6Addr, usize>,
218    subnet_48_counts: HashMap<Ipv6Addr, usize>,
219    subnet_32_counts: HashMap<Ipv6Addr, usize>,
220    asn_counts: HashMap<u32, usize>,
221    country_counts: HashMap<String, usize>,
222}
223
224impl IPDiversityEnforcer {
225    /// Create a new IP diversity enforcer
226    pub fn new(config: IPDiversityConfig) -> Self {
227        Self {
228            config,
229            subnet_64_counts: HashMap::new(),
230            subnet_48_counts: HashMap::new(),
231            subnet_32_counts: HashMap::new(),
232            asn_counts: HashMap::new(),
233            country_counts: HashMap::new(),
234        }
235    }
236    
237    /// Analyze an IPv6 address for diversity enforcement
238    pub fn analyze_ip(&self, ipv6_addr: Ipv6Addr) -> Result<IPAnalysis> {
239        let subnet_64 = Self::extract_subnet_prefix(ipv6_addr, 64);
240        let subnet_48 = Self::extract_subnet_prefix(ipv6_addr, 48);
241        let subnet_32 = Self::extract_subnet_prefix(ipv6_addr, 32);
242        
243        // TODO: Implement ASN lookup (requires external database)
244        let asn = None;
245        
246        // TODO: Implement GeoIP lookup (requires external database)
247        let country = None;
248        
249        // TODO: Implement hosting/VPN provider detection
250        let is_hosting_provider = false;
251        let is_vpn_provider = false;
252        
253        // Default reputation for new IPs
254        let reputation_score = 0.5;
255        
256        Ok(IPAnalysis {
257            subnet_64,
258            subnet_48,
259            subnet_32,
260            asn,
261            country,
262            is_hosting_provider,
263            is_vpn_provider,
264            reputation_score,
265        })
266    }
267    
268    /// Check if a new node can be accepted based on IP diversity constraints
269    pub fn can_accept_node(&self, ip_analysis: &IPAnalysis) -> bool {
270        // Determine limits based on hosting provider status
271        let (limit_64, limit_48, limit_32, limit_asn) = if ip_analysis.is_hosting_provider || ip_analysis.is_vpn_provider {
272            // Stricter limits for hosting providers (halved)
273            (
274                std::cmp::max(1, self.config.max_nodes_per_64 / 2),
275                std::cmp::max(1, self.config.max_nodes_per_48 / 2),
276                std::cmp::max(1, self.config.max_nodes_per_32 / 2),
277                std::cmp::max(1, self.config.max_nodes_per_asn / 2),
278            )
279        } else {
280            // Regular limits for normal nodes
281            (
282                self.config.max_nodes_per_64,
283                self.config.max_nodes_per_48,
284                self.config.max_nodes_per_32,
285                self.config.max_nodes_per_asn,
286            )
287        };
288        
289        // Check /64 subnet limit
290        if let Some(&count) = self.subnet_64_counts.get(&ip_analysis.subnet_64) {
291            if count >= limit_64 {
292                return false;
293            }
294        }
295        
296        // Check /48 subnet limit
297        if let Some(&count) = self.subnet_48_counts.get(&ip_analysis.subnet_48) {
298            if count >= limit_48 {
299                return false;
300            }
301        }
302        
303        // Check /32 subnet limit
304        if let Some(&count) = self.subnet_32_counts.get(&ip_analysis.subnet_32) {
305            if count >= limit_32 {
306                return false;
307            }
308        }
309        
310        // Check ASN limit
311        if let Some(asn) = ip_analysis.asn {
312            if let Some(&count) = self.asn_counts.get(&asn) {
313                if count >= limit_asn {
314                    return false;
315                }
316            }
317        }
318        
319        true
320    }
321    
322    /// Add a node to the diversity tracking
323    pub fn add_node(&mut self, ip_analysis: &IPAnalysis) -> Result<()> {
324        if !self.can_accept_node(ip_analysis) {
325            return Err(anyhow!("IP diversity limits exceeded"));
326        }
327        
328        // Update counts
329        *self.subnet_64_counts.entry(ip_analysis.subnet_64).or_insert(0) += 1;
330        *self.subnet_48_counts.entry(ip_analysis.subnet_48).or_insert(0) += 1;
331        *self.subnet_32_counts.entry(ip_analysis.subnet_32).or_insert(0) += 1;
332        
333        if let Some(asn) = ip_analysis.asn {
334            *self.asn_counts.entry(asn).or_insert(0) += 1;
335        }
336        
337        if let Some(ref country) = ip_analysis.country {
338            *self.country_counts.entry(country.clone()).or_insert(0) += 1;
339        }
340        
341        Ok(())
342    }
343    
344    /// Remove a node from diversity tracking
345    pub fn remove_node(&mut self, ip_analysis: &IPAnalysis) {
346        if let Some(count) = self.subnet_64_counts.get_mut(&ip_analysis.subnet_64) {
347            *count = count.saturating_sub(1);
348            if *count == 0 {
349                self.subnet_64_counts.remove(&ip_analysis.subnet_64);
350            }
351        }
352        
353        if let Some(count) = self.subnet_48_counts.get_mut(&ip_analysis.subnet_48) {
354            *count = count.saturating_sub(1);
355            if *count == 0 {
356                self.subnet_48_counts.remove(&ip_analysis.subnet_48);
357            }
358        }
359        
360        if let Some(count) = self.subnet_32_counts.get_mut(&ip_analysis.subnet_32) {
361            *count = count.saturating_sub(1);
362            if *count == 0 {
363                self.subnet_32_counts.remove(&ip_analysis.subnet_32);
364            }
365        }
366        
367        if let Some(asn) = ip_analysis.asn {
368            if let Some(count) = self.asn_counts.get_mut(&asn) {
369                *count = count.saturating_sub(1);
370                if *count == 0 {
371                    self.asn_counts.remove(&asn);
372                }
373            }
374        }
375        
376        if let Some(ref country) = ip_analysis.country {
377            if let Some(count) = self.country_counts.get_mut(country) {
378                *count = count.saturating_sub(1);
379                if *count == 0 {
380                    self.country_counts.remove(country);
381                }
382            }
383        }
384    }
385    
386    /// Extract network prefix of specified length from IPv6 address
387    pub fn extract_subnet_prefix(addr: Ipv6Addr, prefix_len: u8) -> Ipv6Addr {
388        let octets = addr.octets();
389        let mut subnet = [0u8; 16];
390        
391        let bytes_to_copy = (prefix_len / 8) as usize;
392        let remaining_bits = prefix_len % 8;
393        
394        // Copy full bytes
395        if bytes_to_copy < 16 {
396            subnet[..bytes_to_copy].copy_from_slice(&octets[..bytes_to_copy]);
397        } else {
398            subnet.copy_from_slice(&octets);
399        }
400        
401        // Handle partial byte
402        if remaining_bits > 0 && bytes_to_copy < 16 {
403            let mask = 0xFF << (8 - remaining_bits);
404            subnet[bytes_to_copy] = octets[bytes_to_copy] & mask;
405        }
406        
407        Ipv6Addr::from(subnet)
408    }
409    
410    /// Get diversity statistics
411    pub fn get_diversity_stats(&self) -> DiversityStats {
412        DiversityStats {
413            total_64_subnets: self.subnet_64_counts.len(),
414            total_48_subnets: self.subnet_48_counts.len(),
415            total_32_subnets: self.subnet_32_counts.len(),
416            total_asns: self.asn_counts.len(),
417            total_countries: self.country_counts.len(),
418            max_nodes_per_64: self.subnet_64_counts.values().max().copied().unwrap_or(0),
419            max_nodes_per_48: self.subnet_48_counts.values().max().copied().unwrap_or(0),
420            max_nodes_per_32: self.subnet_32_counts.values().max().copied().unwrap_or(0),
421        }
422    }
423}
424
425/// Diversity statistics for monitoring
426#[derive(Debug, Clone, Serialize, Deserialize)]
427pub struct DiversityStats {
428    /// Number of unique /64 subnets represented
429    pub total_64_subnets: usize,
430    /// Number of unique /48 subnets represented
431    pub total_48_subnets: usize,
432    /// Number of unique /32 subnets represented
433    pub total_32_subnets: usize,
434    /// Number of unique ASNs represented
435    pub total_asns: usize,
436    /// Number of unique countries represented
437    pub total_countries: usize,
438    /// Maximum nodes in any single /64 subnet
439    pub max_nodes_per_64: usize,
440    /// Maximum nodes in any single /48 subnet
441    pub max_nodes_per_48: usize,
442    /// Maximum nodes in any single /32 subnet
443    pub max_nodes_per_32: usize,
444}
445
446/// Reputation manager for tracking node behavior
447#[derive(Debug)]
448pub struct ReputationManager {
449    reputations: HashMap<PeerId, NodeReputation>,
450    reputation_decay: f64,
451    min_reputation: f64,
452}
453
454impl ReputationManager {
455    /// Create a new reputation manager
456    pub fn new(reputation_decay: f64, min_reputation: f64) -> Self {
457        Self {
458            reputations: HashMap::new(),
459            reputation_decay,
460            min_reputation,
461        }
462    }
463    
464    /// Get reputation for a peer
465    pub fn get_reputation(&self, peer_id: &PeerId) -> Option<&NodeReputation> {
466        self.reputations.get(peer_id)
467    }
468    
469    /// Update reputation based on interaction
470    pub fn update_reputation(&mut self, peer_id: &PeerId, success: bool, response_time: Duration) {
471        let reputation = self.reputations.entry(peer_id.clone()).or_insert_with(|| {
472            NodeReputation {
473                peer_id: peer_id.clone(),
474                response_rate: 0.5,
475                response_time: Duration::from_millis(500),
476                consistency_score: 0.5,
477                uptime_estimate: Duration::from_secs(0),
478                routing_accuracy: 0.5,
479                last_seen: SystemTime::now(),
480                interaction_count: 0,
481            }
482        });
483        
484        // Use higher learning rate for faster convergence in tests
485        let alpha = 0.3; // Increased from 0.1 for better test convergence
486        
487        if success {
488            reputation.response_rate = reputation.response_rate * (1.0 - alpha) + alpha;
489        } else {
490            reputation.response_rate = reputation.response_rate * (1.0 - alpha);
491        }
492        
493        // Update response time
494        let response_time_ms = response_time.as_millis() as f64;
495        let current_response_ms = reputation.response_time.as_millis() as f64;
496        let new_response_ms = current_response_ms * (1.0 - alpha) + response_time_ms * alpha;
497        reputation.response_time = Duration::from_millis(new_response_ms as u64);
498        
499        reputation.last_seen = SystemTime::now();
500        reputation.interaction_count += 1;
501    }
502    
503    /// Apply time-based reputation decay
504    pub fn apply_decay(&mut self) {
505        let now = SystemTime::now();
506        
507        self.reputations.retain(|_, reputation| {
508            if let Ok(elapsed) = now.duration_since(reputation.last_seen) {
509                // Decay reputation over time
510                let decay_factor = (-elapsed.as_secs_f64() / 3600.0 * self.reputation_decay).exp();
511                reputation.response_rate *= decay_factor;
512                reputation.consistency_score *= decay_factor;
513                reputation.routing_accuracy *= decay_factor;
514                
515                // Remove nodes with very low reputation
516                reputation.response_rate > self.min_reputation / 10.0
517            } else {
518                true
519            }
520        });
521    }
522}
523
524/// Legacy security types for compatibility
525pub mod security_types {
526    use super::*;
527    
528    /// Ed25519 key pair wrapper
529    pub struct KeyPair {
530        inner: Keypair,
531    }
532    
533    impl KeyPair {
534        /// Generate a new key pair
535        pub fn generate() -> Self {
536            let mut csprng = rand::rngs::OsRng {};
537            let keypair = Keypair::generate(&mut csprng);
538            KeyPair { inner: keypair }
539        }
540        
541        /// Get the inner Ed25519 keypair
542        pub fn inner(&self) -> &Keypair {
543            &self.inner
544        }
545        
546        /// Get public key bytes
547        pub fn public_key_bytes(&self) -> [u8; 32] {
548            self.inner.public.to_bytes()
549        }
550        
551        /// Sign a message
552        pub fn sign(&self, message: &[u8]) -> [u8; 64] {
553            self.inner.sign(message).to_bytes()
554        }
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561
562    fn create_test_keypair() -> Keypair {
563        let mut csprng = rand::rngs::OsRng {};
564        Keypair::generate(&mut csprng)
565    }
566
567    fn create_test_ipv6() -> Ipv6Addr {
568        Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334)
569    }
570
571    fn create_test_diversity_config() -> IPDiversityConfig {
572        IPDiversityConfig {
573            max_nodes_per_64: 1,
574            max_nodes_per_48: 3,
575            max_nodes_per_32: 10,
576            max_nodes_per_asn: 20,
577            enable_geolocation_check: true,
578            min_geographic_diversity: 3,
579        }
580    }
581
582    #[test]
583    fn test_ipv6_node_id_generation() -> Result<()> {
584        let keypair = create_test_keypair();
585        let ipv6_addr = create_test_ipv6();
586
587        let node_id = IPv6NodeID::generate(ipv6_addr, &keypair)?;
588
589        assert_eq!(node_id.ipv6_addr, ipv6_addr);
590        assert_eq!(node_id.public_key.len(), 32);
591        assert_eq!(node_id.signature.len(), 64);
592        assert_eq!(node_id.node_id.len(), 32); // SHA256 output
593        assert_eq!(node_id.salt.len(), 16);
594        assert!(node_id.timestamp_secs > 0);
595
596        Ok(())
597    }
598
599    #[test]
600    fn test_ipv6_node_id_verification() -> Result<()> {
601        let keypair = create_test_keypair();
602        let ipv6_addr = create_test_ipv6();
603
604        let node_id = IPv6NodeID::generate(ipv6_addr, &keypair)?;
605        let is_valid = node_id.verify()?;
606
607        assert!(is_valid);
608
609        Ok(())
610    }
611
612    #[test]
613    fn test_ipv6_node_id_verification_fails_with_wrong_data() -> Result<()> {
614        let keypair = create_test_keypair();
615        let ipv6_addr = create_test_ipv6();
616
617        let mut node_id = IPv6NodeID::generate(ipv6_addr, &keypair)?;
618        
619        // Tamper with the node ID
620        node_id.node_id[0] ^= 0xFF;
621        let is_valid = node_id.verify()?;
622        assert!(!is_valid);
623
624        // Test with wrong signature length
625        let mut node_id2 = IPv6NodeID::generate(ipv6_addr, &keypair)?;
626        node_id2.signature = vec![0u8; 32]; // Wrong length
627        let is_valid2 = node_id2.verify()?;
628        assert!(!is_valid2);
629
630        // Test with wrong public key length
631        let mut node_id3 = IPv6NodeID::generate(ipv6_addr, &keypair)?;
632        node_id3.public_key = vec![0u8; 16]; // Wrong length
633        let is_valid3 = node_id3.verify()?;
634        assert!(!is_valid3);
635
636        Ok(())
637    }
638
639    #[test]
640    fn test_ipv6_subnet_extraction() -> Result<()> {
641        let keypair = create_test_keypair();
642        let ipv6_addr = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334);
643
644        let node_id = IPv6NodeID::generate(ipv6_addr, &keypair)?;
645
646        // Test /64 subnet extraction
647        let subnet_64 = node_id.extract_subnet_64();
648        let expected_64 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0);
649        assert_eq!(subnet_64, expected_64);
650
651        // Test /48 subnet extraction
652        let subnet_48 = node_id.extract_subnet_48();
653        let expected_48 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0);
654        assert_eq!(subnet_48, expected_48);
655
656        // Test /32 subnet extraction
657        let subnet_32 = node_id.extract_subnet_32();
658        let expected_32 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
659        assert_eq!(subnet_32, expected_32);
660
661        Ok(())
662    }
663
664    #[test]
665    fn test_ip_diversity_config_default() {
666        let config = IPDiversityConfig::default();
667
668        assert_eq!(config.max_nodes_per_64, 1);
669        assert_eq!(config.max_nodes_per_48, 3);
670        assert_eq!(config.max_nodes_per_32, 10);
671        assert_eq!(config.max_nodes_per_asn, 20);
672        assert!(config.enable_geolocation_check);
673        assert_eq!(config.min_geographic_diversity, 3);
674    }
675
676    #[test]
677    fn test_ip_diversity_enforcer_creation() {
678        let config = create_test_diversity_config();
679        let enforcer = IPDiversityEnforcer::new(config.clone());
680
681        assert_eq!(enforcer.config.max_nodes_per_64, config.max_nodes_per_64);
682        assert_eq!(enforcer.subnet_64_counts.len(), 0);
683        assert_eq!(enforcer.subnet_48_counts.len(), 0);
684        assert_eq!(enforcer.subnet_32_counts.len(), 0);
685    }
686
687    #[test]
688    fn test_ip_analysis() -> Result<()> {
689        let config = create_test_diversity_config();
690        let enforcer = IPDiversityEnforcer::new(config);
691
692        let ipv6_addr = create_test_ipv6();
693        let analysis = enforcer.analyze_ip(ipv6_addr)?;
694
695        assert_eq!(analysis.subnet_64, IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 64));
696        assert_eq!(analysis.subnet_48, IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 48));
697        assert_eq!(analysis.subnet_32, IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 32));
698        assert!(analysis.asn.is_none()); // Not implemented in test
699        assert!(analysis.country.is_none()); // Not implemented in test
700        assert!(!analysis.is_hosting_provider);
701        assert!(!analysis.is_vpn_provider);
702        assert_eq!(analysis.reputation_score, 0.5);
703
704        Ok(())
705    }
706
707    #[test]
708    fn test_can_accept_node_basic() -> Result<()> {
709        let config = create_test_diversity_config();
710        let enforcer = IPDiversityEnforcer::new(config);
711
712        let ipv6_addr = create_test_ipv6();
713        let analysis = enforcer.analyze_ip(ipv6_addr)?;
714
715        // Should accept first node
716        assert!(enforcer.can_accept_node(&analysis));
717
718        Ok(())
719    }
720
721    #[test]
722    fn test_add_and_remove_node() -> Result<()> {
723        let config = create_test_diversity_config();
724        let mut enforcer = IPDiversityEnforcer::new(config);
725
726        let ipv6_addr = create_test_ipv6();
727        let analysis = enforcer.analyze_ip(ipv6_addr)?;
728
729        // Add node
730        enforcer.add_node(&analysis)?;
731        assert_eq!(enforcer.subnet_64_counts.get(&analysis.subnet_64), Some(&1));
732        assert_eq!(enforcer.subnet_48_counts.get(&analysis.subnet_48), Some(&1));
733        assert_eq!(enforcer.subnet_32_counts.get(&analysis.subnet_32), Some(&1));
734
735        // Remove node
736        enforcer.remove_node(&analysis);
737        assert_eq!(enforcer.subnet_64_counts.get(&analysis.subnet_64), None);
738        assert_eq!(enforcer.subnet_48_counts.get(&analysis.subnet_48), None);
739        assert_eq!(enforcer.subnet_32_counts.get(&analysis.subnet_32), None);
740
741        Ok(())
742    }
743
744    #[test]
745    fn test_diversity_limits_enforcement() -> Result<()> {
746        let config = create_test_diversity_config();
747        let mut enforcer = IPDiversityEnforcer::new(config);
748
749        let ipv6_addr1 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334);
750        let ipv6_addr2 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7335); // Same /64
751
752        let analysis1 = enforcer.analyze_ip(ipv6_addr1)?;
753        let analysis2 = enforcer.analyze_ip(ipv6_addr2)?;
754
755        // First node should be accepted
756        assert!(enforcer.can_accept_node(&analysis1));
757        enforcer.add_node(&analysis1)?;
758
759        // Second node in same /64 should be rejected (max_nodes_per_64 = 1)
760        assert!(!enforcer.can_accept_node(&analysis2));
761
762        // But adding should fail
763        let result = enforcer.add_node(&analysis2);
764        assert!(result.is_err());
765        assert!(result.unwrap_err().to_string().contains("IP diversity limits exceeded"));
766
767        Ok(())
768    }
769
770    #[test]
771    fn test_hosting_provider_stricter_limits() -> Result<()> {
772        let config = IPDiversityConfig {
773            max_nodes_per_64: 4, // Set higher limit for regular nodes
774            max_nodes_per_48: 8,
775            ..create_test_diversity_config()
776        };
777        let mut enforcer = IPDiversityEnforcer::new(config);
778
779        let ipv6_addr = create_test_ipv6();
780        let mut analysis = enforcer.analyze_ip(ipv6_addr)?;
781        analysis.is_hosting_provider = true;
782
783        // Should accept first hosting provider node
784        assert!(enforcer.can_accept_node(&analysis));
785        enforcer.add_node(&analysis)?;
786
787        // Add second hosting provider node in same /64 (should be accepted with limit=2)
788        let ipv6_addr2 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7335);
789        let mut analysis2 = enforcer.analyze_ip(ipv6_addr2)?;
790        analysis2.is_hosting_provider = true;
791        analysis2.subnet_64 = analysis.subnet_64; // Force same subnet
792
793        assert!(enforcer.can_accept_node(&analysis2));
794        enforcer.add_node(&analysis2)?;
795        
796        // Should reject third hosting provider node in same /64 (exceeds limit=2)
797        let ipv6_addr3 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7336);
798        let mut analysis3 = enforcer.analyze_ip(ipv6_addr3)?;
799        analysis3.is_hosting_provider = true;
800        analysis3.subnet_64 = analysis.subnet_64; // Force same subnet
801
802        assert!(!enforcer.can_accept_node(&analysis3));
803
804        Ok(())
805    }
806
807    #[test]
808    fn test_diversity_stats() -> Result<()> {
809        let config = create_test_diversity_config();
810        let mut enforcer = IPDiversityEnforcer::new(config);
811
812        // Add some nodes with different subnets
813        let addresses = [
814            Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334),
815            Ipv6Addr::new(0x2001, 0xdb8, 0x85a4, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334), // Different /48
816            Ipv6Addr::new(0x2002, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334), // Different /32
817        ];
818
819        for addr in addresses {
820            let analysis = enforcer.analyze_ip(addr)?;
821            enforcer.add_node(&analysis)?;
822        }
823
824        let stats = enforcer.get_diversity_stats();
825        assert_eq!(stats.total_64_subnets, 3);
826        assert_eq!(stats.total_48_subnets, 3);
827        assert_eq!(stats.total_32_subnets, 2); // Two /32 prefixes
828        assert_eq!(stats.max_nodes_per_64, 1);
829        assert_eq!(stats.max_nodes_per_48, 1);
830        assert_eq!(stats.max_nodes_per_32, 2); // 2001:db8 has 2 nodes
831
832        Ok(())
833    }
834
835    #[test]
836    fn test_extract_subnet_prefix() {
837        let addr = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334);
838
839        // Test /64 prefix
840        let prefix_64 = IPDiversityEnforcer::extract_subnet_prefix(addr, 64);
841        let expected_64 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0);
842        assert_eq!(prefix_64, expected_64);
843
844        // Test /48 prefix
845        let prefix_48 = IPDiversityEnforcer::extract_subnet_prefix(addr, 48);
846        let expected_48 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0);
847        assert_eq!(prefix_48, expected_48);
848
849        // Test /32 prefix
850        let prefix_32 = IPDiversityEnforcer::extract_subnet_prefix(addr, 32);
851        let expected_32 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
852        assert_eq!(prefix_32, expected_32);
853
854        // Test /56 prefix (partial byte)
855        let prefix_56 = IPDiversityEnforcer::extract_subnet_prefix(addr, 56);
856        let expected_56 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1200, 0, 0, 0, 0);
857        assert_eq!(prefix_56, expected_56);
858
859        // Test /128 prefix (full address)
860        let prefix_128 = IPDiversityEnforcer::extract_subnet_prefix(addr, 128);
861        assert_eq!(prefix_128, addr);
862    }
863
864    #[test]
865    fn test_reputation_manager_creation() {
866        let manager = ReputationManager::new(0.1, 0.1);
867        assert_eq!(manager.reputation_decay, 0.1);
868        assert_eq!(manager.min_reputation, 0.1);
869        assert_eq!(manager.reputations.len(), 0);
870    }
871
872    #[test]
873    fn test_reputation_get_nonexistent() {
874        let manager = ReputationManager::new(0.1, 0.1);
875        let peer_id = "test_peer".to_string();
876        
877        let reputation = manager.get_reputation(&peer_id);
878        assert!(reputation.is_none());
879    }
880
881    #[test]
882    fn test_reputation_update_creates_entry() {
883        let mut manager = ReputationManager::new(0.1, 0.1);
884        let peer_id = "test_peer".to_string();
885        
886        manager.update_reputation(&peer_id, true, Duration::from_millis(100));
887        
888        let reputation = manager.get_reputation(&peer_id);
889        assert!(reputation.is_some());
890        
891        let rep = reputation.unwrap();
892        assert_eq!(rep.peer_id, peer_id);
893        assert!(rep.response_rate > 0.5); // Should increase from initial 0.5
894        assert_eq!(rep.interaction_count, 1);
895    }
896
897    #[test]
898    fn test_reputation_update_success_improves_rate() {
899        let mut manager = ReputationManager::new(0.1, 0.1);
900        let peer_id = "test_peer".to_string();
901        
902        // Multiple successful interactions
903        for _ in 0..15 {
904            manager.update_reputation(&peer_id, true, Duration::from_millis(100));
905        }
906        
907        let reputation = manager.get_reputation(&peer_id).unwrap();
908        assert!(reputation.response_rate > 0.85); // Should be very high with higher learning rate
909        assert_eq!(reputation.interaction_count, 15);
910    }
911
912    #[test]
913    fn test_reputation_update_failure_decreases_rate() {
914        let mut manager = ReputationManager::new(0.1, 0.1);
915        let peer_id = "test_peer".to_string();
916        
917        // Multiple failed interactions
918        for _ in 0..15 {
919            manager.update_reputation(&peer_id, false, Duration::from_millis(1000));
920        }
921        
922        let reputation = manager.get_reputation(&peer_id).unwrap();
923        assert!(reputation.response_rate < 0.15); // Should be very low with higher learning rate
924        assert_eq!(reputation.interaction_count, 15);
925    }
926
927    #[test]
928    fn test_reputation_response_time_tracking() {
929        let mut manager = ReputationManager::new(0.1, 0.1);
930        let peer_id = "test_peer".to_string();
931        
932        // Update with specific response time
933        manager.update_reputation(&peer_id, true, Duration::from_millis(200));
934        
935        let reputation = manager.get_reputation(&peer_id).unwrap();
936        // Response time should be between initial 500ms and new 200ms
937        assert!(reputation.response_time.as_millis() > 200);
938        assert!(reputation.response_time.as_millis() < 500);
939    }
940
941    #[test]
942    fn test_reputation_decay() {
943        let mut manager = ReputationManager::new(1.0, 0.01); // High decay rate
944        let peer_id = "test_peer".to_string();
945        
946        // Create a reputation entry
947        manager.update_reputation(&peer_id, true, Duration::from_millis(100));
948        
949        // Manually set last_seen to past
950        if let Some(reputation) = manager.reputations.get_mut(&peer_id) {
951            reputation.last_seen = SystemTime::now() - Duration::from_secs(7200); // 2 hours ago
952        }
953        
954        let original_rate = manager.get_reputation(&peer_id).unwrap().response_rate;
955        
956        // Apply decay
957        manager.apply_decay();
958        
959        let reputation = manager.get_reputation(&peer_id);
960        if let Some(rep) = reputation {
961            // Should have decayed
962            assert!(rep.response_rate < original_rate);
963        } // else the reputation was removed due to low score
964    }
965
966    #[test]
967    fn test_reputation_decay_removes_low_reputation() {
968        let mut manager = ReputationManager::new(0.1, 0.5); // High min reputation
969        let peer_id = "test_peer".to_string();
970        
971        // Create a low reputation entry
972        for _ in 0..10 {
973            manager.update_reputation(&peer_id, false, Duration::from_millis(1000));
974        }
975        
976        // Manually set last_seen to past
977        if let Some(reputation) = manager.reputations.get_mut(&peer_id) {
978            reputation.last_seen = SystemTime::now() - Duration::from_secs(3600); // 1 hour ago
979            reputation.response_rate = 0.01; // Very low
980        }
981        
982        // Apply decay
983        manager.apply_decay();
984        
985        // Should be removed
986        assert!(manager.get_reputation(&peer_id).is_none());
987    }
988
989    #[test]
990    fn test_security_types_keypair() {
991        let keypair = security_types::KeyPair::generate();
992        
993        let public_key_bytes = keypair.public_key_bytes();
994        assert_eq!(public_key_bytes.len(), 32);
995        
996        let message = b"test message";
997        let signature = keypair.sign(message);
998        assert_eq!(signature.len(), 64);
999        
1000        // Verify the signature using the inner keypair
1001        let inner = keypair.inner();
1002        assert!(inner.verify(message, &Signature::from_bytes(&signature).unwrap()).is_ok());
1003    }
1004
1005    #[test]
1006    fn test_node_reputation_structure() {
1007        let peer_id = "test_peer".to_string();
1008        let reputation = NodeReputation {
1009            peer_id: peer_id.clone(),
1010            response_rate: 0.85,
1011            response_time: Duration::from_millis(150),
1012            consistency_score: 0.9,
1013            uptime_estimate: Duration::from_secs(86400),
1014            routing_accuracy: 0.8,
1015            last_seen: SystemTime::now(),
1016            interaction_count: 42,
1017        };
1018
1019        assert_eq!(reputation.peer_id, peer_id);
1020        assert_eq!(reputation.response_rate, 0.85);
1021        assert_eq!(reputation.response_time, Duration::from_millis(150));
1022        assert_eq!(reputation.consistency_score, 0.9);
1023        assert_eq!(reputation.uptime_estimate, Duration::from_secs(86400));
1024        assert_eq!(reputation.routing_accuracy, 0.8);
1025        assert_eq!(reputation.interaction_count, 42);
1026    }
1027
1028    #[test]
1029    fn test_ip_analysis_structure() {
1030        let analysis = IPAnalysis {
1031            subnet_64: Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0),
1032            subnet_48: Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0),
1033            subnet_32: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
1034            asn: Some(64512),
1035            country: Some("US".to_string()),
1036            is_hosting_provider: true,
1037            is_vpn_provider: false,
1038            reputation_score: 0.75,
1039        };
1040
1041        assert_eq!(analysis.asn, Some(64512));
1042        assert_eq!(analysis.country, Some("US".to_string()));
1043        assert!(analysis.is_hosting_provider);
1044        assert!(!analysis.is_vpn_provider);
1045        assert_eq!(analysis.reputation_score, 0.75);
1046    }
1047
1048    #[test]
1049    fn test_diversity_stats_structure() {
1050        let stats = DiversityStats {
1051            total_64_subnets: 100,
1052            total_48_subnets: 50,
1053            total_32_subnets: 25,
1054            total_asns: 15,
1055            total_countries: 8,
1056            max_nodes_per_64: 1,
1057            max_nodes_per_48: 3,
1058            max_nodes_per_32: 10,
1059        };
1060
1061        assert_eq!(stats.total_64_subnets, 100);
1062        assert_eq!(stats.total_48_subnets, 50);
1063        assert_eq!(stats.total_32_subnets, 25);
1064        assert_eq!(stats.total_asns, 15);
1065        assert_eq!(stats.total_countries, 8);
1066        assert_eq!(stats.max_nodes_per_64, 1);
1067        assert_eq!(stats.max_nodes_per_48, 3);
1068        assert_eq!(stats.max_nodes_per_32, 10);
1069    }
1070
1071    #[test]
1072    fn test_multiple_same_subnet_nodes() -> Result<()> {
1073        let config = IPDiversityConfig {
1074            max_nodes_per_64: 3, // Allow more nodes in same /64
1075            max_nodes_per_48: 5,
1076            max_nodes_per_32: 10,
1077            ..create_test_diversity_config()
1078        };
1079        let mut enforcer = IPDiversityEnforcer::new(config);
1080
1081        let _base_addr = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x0000);
1082        
1083        // Add 3 nodes in same /64 subnet
1084        for i in 1..=3 {
1085            let addr = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, i);
1086            let analysis = enforcer.analyze_ip(addr)?;
1087            assert!(enforcer.can_accept_node(&analysis));
1088            enforcer.add_node(&analysis)?;
1089        }
1090
1091        // 4th node should be rejected
1092        let addr4 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 4);
1093        let analysis4 = enforcer.analyze_ip(addr4)?;
1094        assert!(!enforcer.can_accept_node(&analysis4));
1095
1096        let stats = enforcer.get_diversity_stats();
1097        assert_eq!(stats.total_64_subnets, 1);
1098        assert_eq!(stats.max_nodes_per_64, 3);
1099
1100        Ok(())
1101    }
1102
1103    #[test]
1104    fn test_asn_and_country_tracking() -> Result<()> {
1105        let config = create_test_diversity_config();
1106        let mut enforcer = IPDiversityEnforcer::new(config);
1107
1108        // Create analysis with ASN and country
1109        let ipv6_addr = create_test_ipv6();
1110        let mut analysis = enforcer.analyze_ip(ipv6_addr)?;
1111        analysis.asn = Some(64512);
1112        analysis.country = Some("US".to_string());
1113
1114        enforcer.add_node(&analysis)?;
1115
1116        assert_eq!(enforcer.asn_counts.get(&64512), Some(&1));
1117        assert_eq!(enforcer.country_counts.get("US"), Some(&1));
1118
1119        // Remove and check cleanup
1120        enforcer.remove_node(&analysis);
1121        assert!(enforcer.asn_counts.get(&64512).is_none());
1122        assert!(enforcer.country_counts.get("US").is_none());
1123
1124        Ok(())
1125    }
1126
1127    #[test]
1128    fn test_reputation_mixed_interactions() {
1129        let mut manager = ReputationManager::new(0.1, 0.1);
1130        let peer_id = "test_peer".to_string();
1131        
1132        // Mix of successful and failed interactions
1133        for i in 0..15 {
1134            let success = i % 3 != 0; // 2/3 success rate
1135            manager.update_reputation(&peer_id, success, Duration::from_millis(100 + i * 10));
1136        }
1137        
1138        let reputation = manager.get_reputation(&peer_id).unwrap();
1139        // Should converge closer to 2/3 with more iterations and higher learning rate
1140        // With alpha=0.3 and 2/3 success rate, convergence may be higher
1141        assert!(reputation.response_rate > 0.55);
1142        assert!(reputation.response_rate < 0.85);
1143        assert_eq!(reputation.interaction_count, 15);
1144    }
1145}