1use crate::quantum_crypto::saorsa_transport_integration::{
21 MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature, ml_dsa_sign, ml_dsa_verify,
22};
23use anyhow::{Result, anyhow};
24use lru::LruCache;
25use serde::{Deserialize, Serialize};
26use std::collections::HashMap;
27use std::fmt::Debug;
28use std::net::{Ipv4Addr, Ipv6Addr};
29use std::num::NonZeroUsize;
30use std::time::{Duration, SystemTime, UNIX_EPOCH};
31
32use std::sync::Arc;
33
34const MAX_SUBNET_TRACKING: usize = 50_000;
36
37#[allow(dead_code)]
43pub trait NodeIpAddress: Debug + Clone + Send + Sync + 'static {
44 fn octets_vec(&self) -> Vec<u8>;
46}
47
48impl NodeIpAddress for Ipv6Addr {
49 fn octets_vec(&self) -> Vec<u8> {
50 self.octets().to_vec()
51 }
52}
53
54impl NodeIpAddress for Ipv4Addr {
55 fn octets_vec(&self) -> Vec<u8> {
56 self.octets().to_vec()
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
69#[allow(dead_code)]
70pub struct GenericIpNodeID<A: NodeIpAddress> {
71 pub node_id: Vec<u8>,
73 pub ip_addr: A,
75 pub public_key: Vec<u8>,
77 pub signature: Vec<u8>,
79 pub timestamp_secs: u64,
81 pub salt: Vec<u8>,
83}
84
85#[allow(dead_code)]
86impl<A: NodeIpAddress> GenericIpNodeID<A> {
87 const SIGNATURE_LENGTH: usize = 3309;
89
90 pub fn generate(ip_addr: A, secret: &MlDsaSecretKey, public: &MlDsaPublicKey) -> Result<Self> {
92 let mut rng = rand::thread_rng();
93 let mut salt = vec![0u8; 16];
94 rand::RngCore::fill_bytes(&mut rng, &mut salt);
95
96 let timestamp = SystemTime::now();
97 let timestamp_secs = timestamp.duration_since(UNIX_EPOCH)?.as_secs();
98 let public_key = public.as_bytes().to_vec();
99 let ip_octets = ip_addr.octets_vec();
100
101 let node_id = Self::compute_node_id(&ip_octets, &public_key, &salt, timestamp_secs);
103
104 let message_to_sign = Self::build_message(&ip_octets, &public_key, &salt, timestamp_secs);
106 let sig = ml_dsa_sign(secret, &message_to_sign)
107 .map_err(|e| anyhow!("ML-DSA sign failed: {:?}", e))?;
108 let signature = sig.0.to_vec();
109
110 Ok(Self {
111 node_id,
112 ip_addr,
113 public_key,
114 signature,
115 timestamp_secs,
116 salt,
117 })
118 }
119
120 pub fn verify(&self) -> Result<bool> {
122 let ip_octets = self.ip_addr.octets_vec();
123
124 let expected_node_id = Self::compute_node_id(
126 &ip_octets,
127 &self.public_key,
128 &self.salt,
129 self.timestamp_secs,
130 );
131
132 if expected_node_id != self.node_id {
133 return Ok(false);
134 }
135
136 let public_key = MlDsaPublicKey::from_bytes(&self.public_key)
138 .map_err(|e| anyhow!("Invalid ML-DSA public key: {:?}", e))?;
139
140 if self.signature.len() != Self::SIGNATURE_LENGTH {
141 return Ok(false);
142 }
143
144 let mut sig_bytes = [0u8; 3309];
145 sig_bytes.copy_from_slice(&self.signature);
146 let signature = MlDsaSignature(Box::new(sig_bytes));
147
148 let message_to_verify = Self::build_message(
149 &ip_octets,
150 &self.public_key,
151 &self.salt,
152 self.timestamp_secs,
153 );
154
155 let ok = ml_dsa_verify(&public_key, &message_to_verify, &signature)
156 .map_err(|e| anyhow!("ML-DSA verify error: {:?}", e))?;
157 Ok(ok)
158 }
159
160 pub fn age_secs(&self) -> u64 {
162 let now = SystemTime::now()
163 .duration_since(UNIX_EPOCH)
164 .map(|d| d.as_secs())
165 .unwrap_or(0);
166 now.saturating_sub(self.timestamp_secs)
167 }
168
169 pub fn is_expired(&self, max_age: Duration) -> bool {
171 self.age_secs() > max_age.as_secs()
172 }
173
174 #[inline]
177 fn compute_node_id(
178 ip_octets: &[u8],
179 public_key: &[u8],
180 salt: &[u8],
181 timestamp_secs: u64,
182 ) -> Vec<u8> {
183 let mut hasher = blake3::Hasher::new();
184 hasher.update(ip_octets);
185 hasher.update(public_key);
186 hasher.update(salt);
187 hasher.update(×tamp_secs.to_le_bytes());
188 hasher.finalize().as_bytes().to_vec()
189 }
190
191 #[inline]
192 fn build_message(
193 ip_octets: &[u8],
194 public_key: &[u8],
195 salt: &[u8],
196 timestamp_secs: u64,
197 ) -> Vec<u8> {
198 let mut message = Vec::with_capacity(ip_octets.len() + public_key.len() + salt.len() + 8);
199 message.extend_from_slice(ip_octets);
200 message.extend_from_slice(public_key);
201 message.extend_from_slice(salt);
202 message.extend_from_slice(×tamp_secs.to_le_bytes());
203 message
204 }
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
213#[allow(dead_code)]
214pub struct IPv6NodeID {
215 pub node_id: Vec<u8>,
217 pub ipv6_addr: Ipv6Addr,
219 pub public_key: Vec<u8>,
221 pub signature: Vec<u8>,
223 pub timestamp_secs: u64,
225 pub salt: Vec<u8>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct IPDiversityConfig {
232 pub max_nodes_per_ipv6_64: usize,
235 pub max_nodes_per_ipv6_48: usize,
237 pub max_nodes_per_ipv6_32: usize,
239
240 #[serde(default, skip_serializing_if = "Option::is_none")]
247 pub max_nodes_per_ipv4_32: Option<usize>,
248 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub max_nodes_per_ipv4_24: Option<usize>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub max_nodes_per_ipv4_16: Option<usize>,
258
259 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub ipv4_limit_floor: Option<usize>,
265 #[serde(default, skip_serializing_if = "Option::is_none")]
268 pub ipv4_limit_ceiling: Option<usize>,
269 #[serde(default, skip_serializing_if = "Option::is_none")]
271 pub ipv6_limit_floor: Option<usize>,
272 #[serde(default, skip_serializing_if = "Option::is_none")]
274 pub ipv6_limit_ceiling: Option<usize>,
275
276 pub max_per_ip_cap: usize,
279 pub max_network_fraction: f64,
281
282 pub max_nodes_per_asn: usize,
285 pub enable_geolocation_check: bool,
287 pub min_geographic_diversity: usize,
289}
290
291#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
293pub struct IPAnalysis {
294 #[serde(default)]
297 pub is_loopback: bool,
298 pub subnet_64: Ipv6Addr,
300 pub subnet_48: Ipv6Addr,
302 pub subnet_32: Ipv6Addr,
304 pub asn: Option<u32>,
306 pub country: Option<String>,
308 pub is_hosting_provider: bool,
310 pub is_vpn_provider: bool,
312 pub reputation_score: f64,
314}
315
316#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
318#[allow(dead_code)]
319pub struct IPv4Analysis {
320 pub ip_addr: Ipv4Addr,
322 pub subnet_24: Ipv4Addr,
324 pub subnet_16: Ipv4Addr,
326 pub subnet_8: Ipv4Addr,
328 pub asn: Option<u32>,
330 pub country: Option<String>,
332 pub is_hosting_provider: bool,
334 pub is_vpn_provider: bool,
336 pub reputation_score: f64,
338}
339
340#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
342#[allow(dead_code)]
343pub enum UnifiedIPAnalysis {
344 IPv4(IPv4Analysis),
346 IPv6(IPAnalysis),
348}
349
350impl Default for IPDiversityConfig {
351 fn default() -> Self {
352 Self {
353 max_nodes_per_ipv6_64: 1,
355 max_nodes_per_ipv6_48: 3,
356 max_nodes_per_ipv6_32: 10,
357 max_nodes_per_ipv4_32: None,
359 max_nodes_per_ipv4_24: None,
360 max_nodes_per_ipv4_16: None,
361 ipv4_limit_floor: None,
363 ipv4_limit_ceiling: None,
364 ipv6_limit_floor: None,
365 ipv6_limit_ceiling: None,
366 max_per_ip_cap: 50, max_network_fraction: 0.005, max_nodes_per_asn: 20,
371 enable_geolocation_check: true,
372 min_geographic_diversity: 3,
373 }
374 }
375}
376
377impl IPDiversityConfig {
378 #[must_use]
389 pub fn testnet() -> Self {
390 Self {
391 max_nodes_per_ipv6_64: 100, max_nodes_per_ipv6_48: 500, max_nodes_per_ipv6_32: 1000, max_nodes_per_ipv4_32: Some(100),
400 max_nodes_per_ipv4_24: Some(500),
401 max_nodes_per_ipv4_16: Some(1000),
402 ipv4_limit_floor: Some(5),
405 ipv4_limit_ceiling: None,
406 ipv6_limit_floor: Some(5),
407 ipv6_limit_ceiling: None,
408 max_per_ip_cap: 100, max_network_fraction: 0.1, max_nodes_per_asn: 5000, enable_geolocation_check: false, min_geographic_diversity: 1, }
416 }
417
418 #[must_use]
423 pub fn permissive() -> Self {
424 Self {
425 max_nodes_per_ipv6_64: usize::MAX,
427 max_nodes_per_ipv6_48: usize::MAX,
428 max_nodes_per_ipv6_32: usize::MAX,
429 max_nodes_per_ipv4_32: None,
431 max_nodes_per_ipv4_24: None,
432 max_nodes_per_ipv4_16: None,
433 ipv4_limit_floor: None,
435 ipv4_limit_ceiling: None,
436 ipv6_limit_floor: None,
437 ipv6_limit_ceiling: None,
438 max_per_ip_cap: usize::MAX,
440 max_network_fraction: 1.0, max_nodes_per_asn: usize::MAX,
443 enable_geolocation_check: false,
444 min_geographic_diversity: 0,
445 }
446 }
447
448 #[must_use]
450 pub fn is_relaxed(&self) -> bool {
451 self.max_nodes_per_asn > 100 || !self.enable_geolocation_check
452 }
453}
454
455#[allow(dead_code)]
456impl IPv6NodeID {
457 pub fn generate(
461 ipv6_addr: Ipv6Addr,
462 secret: &MlDsaSecretKey,
463 public: &MlDsaPublicKey,
464 ) -> Result<Self> {
465 let generic = GenericIpNodeID::generate(ipv6_addr, secret, public)?;
466 Ok(Self::from_generic(generic))
467 }
468
469 pub fn verify(&self) -> Result<bool> {
471 self.to_generic().verify()
472 }
473
474 pub fn extract_subnet_64(&self) -> Ipv6Addr {
476 let octets = self.ipv6_addr.octets();
477 let mut subnet = [0u8; 16];
478 subnet[..8].copy_from_slice(&octets[..8]);
479 Ipv6Addr::from(subnet)
480 }
481
482 pub fn extract_subnet_48(&self) -> Ipv6Addr {
484 let octets = self.ipv6_addr.octets();
485 let mut subnet = [0u8; 16];
486 subnet[..6].copy_from_slice(&octets[..6]);
487 Ipv6Addr::from(subnet)
488 }
489
490 pub fn extract_subnet_32(&self) -> Ipv6Addr {
492 let octets = self.ipv6_addr.octets();
493 let mut subnet = [0u8; 16];
494 subnet[..4].copy_from_slice(&octets[..4]);
495 Ipv6Addr::from(subnet)
496 }
497
498 fn from_generic(g: GenericIpNodeID<Ipv6Addr>) -> Self {
501 Self {
502 node_id: g.node_id,
503 ipv6_addr: g.ip_addr,
504 public_key: g.public_key,
505 signature: g.signature,
506 timestamp_secs: g.timestamp_secs,
507 salt: g.salt,
508 }
509 }
510
511 fn to_generic(&self) -> GenericIpNodeID<Ipv6Addr> {
512 GenericIpNodeID {
513 node_id: self.node_id.clone(),
514 ip_addr: self.ipv6_addr,
515 public_key: self.public_key.clone(),
516 signature: self.signature.clone(),
517 timestamp_secs: self.timestamp_secs,
518 salt: self.salt.clone(),
519 }
520 }
521}
522
523#[derive(Debug, Clone, Serialize, Deserialize)]
526#[allow(dead_code)]
527pub struct IPv4NodeID {
528 pub node_id: Vec<u8>,
530 pub ipv4_addr: Ipv4Addr,
532 pub public_key: Vec<u8>,
534 pub signature: Vec<u8>,
536 pub timestamp_secs: u64,
538 pub salt: Vec<u8>,
540}
541
542#[allow(dead_code)]
543impl IPv4NodeID {
544 pub fn generate(
548 ipv4_addr: Ipv4Addr,
549 secret: &MlDsaSecretKey,
550 public: &MlDsaPublicKey,
551 ) -> Result<Self> {
552 let generic = GenericIpNodeID::generate(ipv4_addr, secret, public)?;
553 Ok(Self::from_generic(generic))
554 }
555
556 pub fn verify(&self) -> Result<bool> {
558 self.to_generic().verify()
559 }
560
561 pub fn extract_subnet_24(&self) -> Ipv4Addr {
563 let octets = self.ipv4_addr.octets();
564 Ipv4Addr::new(octets[0], octets[1], octets[2], 0)
565 }
566
567 pub fn extract_subnet_16(&self) -> Ipv4Addr {
569 let octets = self.ipv4_addr.octets();
570 Ipv4Addr::new(octets[0], octets[1], 0, 0)
571 }
572
573 pub fn extract_subnet_8(&self) -> Ipv4Addr {
575 let octets = self.ipv4_addr.octets();
576 Ipv4Addr::new(octets[0], 0, 0, 0)
577 }
578
579 pub fn age_secs(&self) -> u64 {
581 self.to_generic().age_secs()
582 }
583
584 pub fn is_expired(&self, max_age: Duration) -> bool {
586 self.to_generic().is_expired(max_age)
587 }
588
589 fn from_generic(g: GenericIpNodeID<Ipv4Addr>) -> Self {
592 Self {
593 node_id: g.node_id,
594 ipv4_addr: g.ip_addr,
595 public_key: g.public_key,
596 signature: g.signature,
597 timestamp_secs: g.timestamp_secs,
598 salt: g.salt,
599 }
600 }
601
602 fn to_generic(&self) -> GenericIpNodeID<Ipv4Addr> {
603 GenericIpNodeID {
604 node_id: self.node_id.clone(),
605 ip_addr: self.ipv4_addr,
606 public_key: self.public_key.clone(),
607 signature: self.signature.clone(),
608 timestamp_secs: self.timestamp_secs,
609 salt: self.salt.clone(),
610 }
611 }
612}
613
614#[derive(Debug)]
616pub struct IPDiversityEnforcer {
617 config: IPDiversityConfig,
618 allow_loopback: bool,
624 subnet_64_counts: LruCache<Ipv6Addr, usize>,
626 subnet_48_counts: LruCache<Ipv6Addr, usize>,
627 subnet_32_counts: LruCache<Ipv6Addr, usize>,
628 #[allow(dead_code)]
630 ipv4_32_counts: LruCache<Ipv4Addr, usize>, #[allow(dead_code)]
632 ipv4_24_counts: LruCache<Ipv4Addr, usize>, #[allow(dead_code)]
634 ipv4_16_counts: LruCache<Ipv4Addr, usize>, asn_counts: LruCache<u32, usize>,
637 country_counts: LruCache<String, usize>,
638 geo_provider: Option<Arc<dyn GeoProvider + Send + Sync>>,
639 #[allow(dead_code)]
641 network_size: usize,
642}
643
644#[allow(dead_code)]
645impl IPDiversityEnforcer {
646 pub fn new(config: IPDiversityConfig) -> Self {
648 Self::with_loopback(config, false)
649 }
650
651 pub fn with_loopback(config: IPDiversityConfig, allow_loopback: bool) -> Self {
656 let cache_size = NonZeroUsize::new(MAX_SUBNET_TRACKING).unwrap_or(NonZeroUsize::MIN);
657 Self {
658 config,
659 allow_loopback,
660 subnet_64_counts: LruCache::new(cache_size),
662 subnet_48_counts: LruCache::new(cache_size),
663 subnet_32_counts: LruCache::new(cache_size),
664 ipv4_32_counts: LruCache::new(cache_size),
666 ipv4_24_counts: LruCache::new(cache_size),
667 ipv4_16_counts: LruCache::new(cache_size),
668 asn_counts: LruCache::new(cache_size),
670 country_counts: LruCache::new(cache_size),
671 geo_provider: None,
672 network_size: 0,
673 }
674 }
675
676 pub fn with_geo_provider(
678 config: IPDiversityConfig,
679 provider: Arc<dyn GeoProvider + Send + Sync>,
680 ) -> Self {
681 let mut s = Self::new(config);
682 s.geo_provider = Some(provider);
683 s
684 }
685
686 pub fn analyze_ip(&self, ipv6_addr: Ipv6Addr) -> Result<IPAnalysis> {
688 let subnet_64 = Self::extract_subnet_prefix(ipv6_addr, 64);
689 let subnet_48 = Self::extract_subnet_prefix(ipv6_addr, 48);
690 let subnet_32 = Self::extract_subnet_prefix(ipv6_addr, 32);
691
692 let (asn, country, is_hosting_provider, is_vpn_provider) =
694 if let Some(p) = &self.geo_provider {
695 let info = p.lookup(ipv6_addr);
696 (
697 info.asn,
698 info.country,
699 info.is_hosting_provider,
700 info.is_vpn_provider,
701 )
702 } else {
703 (None, None, false, false)
704 };
705
706 let reputation_score = 0.5;
708
709 let is_loopback = ipv6_addr.is_loopback()
711 || ipv6_addr
712 .to_ipv4_mapped()
713 .is_some_and(|v4| v4.is_loopback());
714
715 Ok(IPAnalysis {
716 is_loopback,
717 subnet_64,
718 subnet_48,
719 subnet_32,
720 asn,
721 country,
722 is_hosting_provider,
723 is_vpn_provider,
724 reputation_score,
725 })
726 }
727
728 pub fn can_accept_node(&self, ip_analysis: &IPAnalysis) -> bool {
730 if ip_analysis.is_loopback && self.allow_loopback {
732 return true;
733 }
734
735 let (limit_64, limit_48, limit_32, limit_asn) =
737 if ip_analysis.is_hosting_provider || ip_analysis.is_vpn_provider {
738 (
740 std::cmp::max(1, self.config.max_nodes_per_ipv6_64 / 2),
741 std::cmp::max(1, self.config.max_nodes_per_ipv6_48 / 2),
742 std::cmp::max(1, self.config.max_nodes_per_ipv6_32 / 2),
743 std::cmp::max(1, self.config.max_nodes_per_asn / 2),
744 )
745 } else {
746 (
748 self.config.max_nodes_per_ipv6_64,
749 self.config.max_nodes_per_ipv6_48,
750 self.config.max_nodes_per_ipv6_32,
751 self.config.max_nodes_per_asn,
752 )
753 };
754
755 if let Some(&count) = self.subnet_64_counts.peek(&ip_analysis.subnet_64)
757 && count >= limit_64
758 {
759 return false;
760 }
761
762 if let Some(&count) = self.subnet_48_counts.peek(&ip_analysis.subnet_48)
764 && count >= limit_48
765 {
766 return false;
767 }
768
769 if let Some(&count) = self.subnet_32_counts.peek(&ip_analysis.subnet_32)
771 && count >= limit_32
772 {
773 return false;
774 }
775
776 if let Some(asn) = ip_analysis.asn
778 && let Some(&count) = self.asn_counts.peek(&asn)
779 && count >= limit_asn
780 {
781 return false;
782 }
783
784 true
785 }
786
787 pub fn add_node(&mut self, ip_analysis: &IPAnalysis) -> Result<()> {
789 if !self.can_accept_node(ip_analysis) {
790 return Err(anyhow!("IP diversity limits exceeded"));
791 }
792
793 let count_64 = self
795 .subnet_64_counts
796 .get(&ip_analysis.subnet_64)
797 .copied()
798 .unwrap_or(0)
799 + 1;
800 self.subnet_64_counts.put(ip_analysis.subnet_64, count_64);
801
802 let count_48 = self
803 .subnet_48_counts
804 .get(&ip_analysis.subnet_48)
805 .copied()
806 .unwrap_or(0)
807 + 1;
808 self.subnet_48_counts.put(ip_analysis.subnet_48, count_48);
809
810 let count_32 = self
811 .subnet_32_counts
812 .get(&ip_analysis.subnet_32)
813 .copied()
814 .unwrap_or(0)
815 + 1;
816 self.subnet_32_counts.put(ip_analysis.subnet_32, count_32);
817
818 if let Some(asn) = ip_analysis.asn {
819 let count = self.asn_counts.get(&asn).copied().unwrap_or(0) + 1;
820 self.asn_counts.put(asn, count);
821 }
822
823 if let Some(ref country) = ip_analysis.country {
824 let count = self.country_counts.get(country).copied().unwrap_or(0) + 1;
825 self.country_counts.put(country.clone(), count);
826 }
827
828 Ok(())
829 }
830
831 pub fn remove_node(&mut self, ip_analysis: &IPAnalysis) {
833 if let Some(count) = self.subnet_64_counts.pop(&ip_analysis.subnet_64) {
835 let new_count = count.saturating_sub(1);
836 if new_count > 0 {
837 self.subnet_64_counts.put(ip_analysis.subnet_64, new_count);
838 }
839 }
840
841 if let Some(count) = self.subnet_48_counts.pop(&ip_analysis.subnet_48) {
842 let new_count = count.saturating_sub(1);
843 if new_count > 0 {
844 self.subnet_48_counts.put(ip_analysis.subnet_48, new_count);
845 }
846 }
847
848 if let Some(count) = self.subnet_32_counts.pop(&ip_analysis.subnet_32) {
849 let new_count = count.saturating_sub(1);
850 if new_count > 0 {
851 self.subnet_32_counts.put(ip_analysis.subnet_32, new_count);
852 }
853 }
854
855 if let Some(asn) = ip_analysis.asn
856 && let Some(count) = self.asn_counts.pop(&asn)
857 {
858 let new_count = count.saturating_sub(1);
859 if new_count > 0 {
860 self.asn_counts.put(asn, new_count);
861 }
862 }
863
864 if let Some(ref country) = ip_analysis.country
865 && let Some(count) = self.country_counts.pop(country)
866 {
867 let new_count = count.saturating_sub(1);
868 if new_count > 0 {
869 self.country_counts.put(country.clone(), new_count);
870 }
871 }
872 }
873
874 pub fn extract_subnet_prefix(addr: Ipv6Addr, prefix_len: u8) -> Ipv6Addr {
876 let octets = addr.octets();
877 let mut subnet = [0u8; 16];
878
879 let bytes_to_copy = (prefix_len / 8) as usize;
880 let remaining_bits = prefix_len % 8;
881
882 if bytes_to_copy < 16 {
884 subnet[..bytes_to_copy].copy_from_slice(&octets[..bytes_to_copy]);
885 } else {
886 subnet.copy_from_slice(&octets);
887 }
888
889 if remaining_bits > 0 && bytes_to_copy < 16 {
891 let mask = 0xFF << (8 - remaining_bits);
892 subnet[bytes_to_copy] = octets[bytes_to_copy] & mask;
893 }
894
895 Ipv6Addr::from(subnet)
896 }
897
898 pub fn get_diversity_stats(&self) -> DiversityStats {
900 let max_nodes_per_ipv6_64 = self
902 .subnet_64_counts
903 .iter()
904 .map(|(_, &v)| v)
905 .max()
906 .unwrap_or(0);
907 let max_nodes_per_ipv6_48 = self
908 .subnet_48_counts
909 .iter()
910 .map(|(_, &v)| v)
911 .max()
912 .unwrap_or(0);
913 let max_nodes_per_ipv6_32 = self
914 .subnet_32_counts
915 .iter()
916 .map(|(_, &v)| v)
917 .max()
918 .unwrap_or(0);
919 let max_nodes_per_ipv4_32 = self
920 .ipv4_32_counts
921 .iter()
922 .map(|(_, &v)| v)
923 .max()
924 .unwrap_or(0);
925 let max_nodes_per_ipv4_24 = self
926 .ipv4_24_counts
927 .iter()
928 .map(|(_, &v)| v)
929 .max()
930 .unwrap_or(0);
931 let max_nodes_per_ipv4_16 = self
932 .ipv4_16_counts
933 .iter()
934 .map(|(_, &v)| v)
935 .max()
936 .unwrap_or(0);
937
938 DiversityStats {
939 total_64_subnets: self.subnet_64_counts.len(),
940 total_48_subnets: self.subnet_48_counts.len(),
941 total_32_subnets: self.subnet_32_counts.len(),
942 total_asns: self.asn_counts.len(),
943 total_countries: self.country_counts.len(),
944 max_nodes_per_ipv6_64,
945 max_nodes_per_ipv6_48,
946 max_nodes_per_ipv6_32,
947 total_ipv4_32: self.ipv4_32_counts.len(),
949 total_ipv4_24_subnets: self.ipv4_24_counts.len(),
950 total_ipv4_16_subnets: self.ipv4_16_counts.len(),
951 max_nodes_per_ipv4_32,
952 max_nodes_per_ipv4_24,
953 max_nodes_per_ipv4_16,
954 }
955 }
956
957 pub fn set_network_size(&mut self, size: usize) {
961 self.network_size = size;
962 }
963
964 pub fn get_network_size(&self) -> usize {
966 self.network_size
967 }
968
969 pub fn get_per_ip_limit(&self) -> usize {
972 let fraction_limit =
973 (self.network_size as f64 * self.config.max_network_fraction).floor() as usize;
974 std::cmp::min(self.config.max_per_ip_cap, std::cmp::max(1, fraction_limit))
975 }
976
977 fn extract_ipv4_subnet_24(addr: Ipv4Addr) -> Ipv4Addr {
979 let octets = addr.octets();
980 Ipv4Addr::new(octets[0], octets[1], octets[2], 0)
981 }
982
983 fn extract_ipv4_subnet_16(addr: Ipv4Addr) -> Ipv4Addr {
985 let octets = addr.octets();
986 Ipv4Addr::new(octets[0], octets[1], 0, 0)
987 }
988
989 fn extract_ipv4_subnet_8(addr: Ipv4Addr) -> Ipv4Addr {
991 let octets = addr.octets();
992 Ipv4Addr::new(octets[0], 0, 0, 0)
993 }
994
995 pub fn analyze_ipv4(&self, ipv4_addr: Ipv4Addr) -> Result<IPv4Analysis> {
997 let subnet_24 = Self::extract_ipv4_subnet_24(ipv4_addr);
998 let subnet_16 = Self::extract_ipv4_subnet_16(ipv4_addr);
999 let subnet_8 = Self::extract_ipv4_subnet_8(ipv4_addr);
1000
1001 let asn = None;
1004 let country = None;
1005 let is_hosting_provider = false;
1006 let is_vpn_provider = false;
1007 let reputation_score = 0.5;
1008
1009 Ok(IPv4Analysis {
1010 ip_addr: ipv4_addr,
1011 subnet_24,
1012 subnet_16,
1013 subnet_8,
1014 asn,
1015 country,
1016 is_hosting_provider,
1017 is_vpn_provider,
1018 reputation_score,
1019 })
1020 }
1021
1022 pub fn analyze_unified(&self, addr: std::net::IpAddr) -> Result<UnifiedIPAnalysis> {
1024 match addr {
1025 std::net::IpAddr::V4(ipv4) => {
1026 let analysis = self.analyze_ipv4(ipv4)?;
1027 Ok(UnifiedIPAnalysis::IPv4(analysis))
1028 }
1029 std::net::IpAddr::V6(ipv6) => {
1030 let analysis = self.analyze_ip(ipv6)?;
1031 Ok(UnifiedIPAnalysis::IPv6(analysis))
1032 }
1033 }
1034 }
1035
1036 pub fn can_accept_unified(&self, analysis: &UnifiedIPAnalysis) -> bool {
1038 match analysis {
1039 UnifiedIPAnalysis::IPv4(ipv4_analysis) => self.can_accept_ipv4(ipv4_analysis),
1040 UnifiedIPAnalysis::IPv6(ipv6_analysis) => self.can_accept_node(ipv6_analysis),
1041 }
1042 }
1043
1044 fn can_accept_ipv4(&self, analysis: &IPv4Analysis) -> bool {
1046 if analysis.ip_addr.is_loopback() && self.allow_loopback {
1048 return true;
1049 }
1050
1051 let per_ip_limit = self.get_per_ip_limit();
1053
1054 let limit_32 = self
1056 .config
1057 .max_nodes_per_ipv4_32
1058 .map_or(per_ip_limit, |floor| floor.max(per_ip_limit));
1059 let limit_24 = self
1060 .config
1061 .max_nodes_per_ipv4_24
1062 .map_or(per_ip_limit * 3, |floor| floor.max(per_ip_limit * 3));
1063 let limit_16 = self
1064 .config
1065 .max_nodes_per_ipv4_16
1066 .map_or(per_ip_limit * 10, |floor| floor.max(per_ip_limit * 10));
1067
1068 let (limit_32, limit_24, limit_16) =
1070 if analysis.is_hosting_provider || analysis.is_vpn_provider {
1071 (
1072 std::cmp::max(1, limit_32 / 2),
1073 std::cmp::max(1, limit_24 / 2),
1074 std::cmp::max(1, limit_16 / 2),
1075 )
1076 } else {
1077 (limit_32, limit_24, limit_16)
1078 };
1079
1080 if let Some(&count) = self.ipv4_32_counts.peek(&analysis.ip_addr)
1082 && count >= limit_32
1083 {
1084 return false;
1085 }
1086
1087 if let Some(&count) = self.ipv4_24_counts.peek(&analysis.subnet_24)
1089 && count >= limit_24
1090 {
1091 return false;
1092 }
1093
1094 if let Some(&count) = self.ipv4_16_counts.peek(&analysis.subnet_16)
1096 && count >= limit_16
1097 {
1098 return false;
1099 }
1100
1101 if let Some(asn) = analysis.asn
1103 && let Some(&count) = self.asn_counts.peek(&asn)
1104 && count >= self.config.max_nodes_per_asn
1105 {
1106 return false;
1107 }
1108
1109 true
1110 }
1111
1112 pub fn add_unified(&mut self, analysis: &UnifiedIPAnalysis) -> Result<()> {
1114 match analysis {
1115 UnifiedIPAnalysis::IPv4(ipv4_analysis) => self.add_ipv4(ipv4_analysis),
1116 UnifiedIPAnalysis::IPv6(ipv6_analysis) => self.add_node(ipv6_analysis),
1117 }
1118 }
1119
1120 fn add_ipv4(&mut self, analysis: &IPv4Analysis) -> Result<()> {
1122 if !self.can_accept_ipv4(analysis) {
1123 return Err(anyhow!("IPv4 diversity limits exceeded"));
1124 }
1125
1126 let count_32 = self
1128 .ipv4_32_counts
1129 .get(&analysis.ip_addr)
1130 .copied()
1131 .unwrap_or(0)
1132 + 1;
1133 self.ipv4_32_counts.put(analysis.ip_addr, count_32);
1134
1135 let count_24 = self
1136 .ipv4_24_counts
1137 .get(&analysis.subnet_24)
1138 .copied()
1139 .unwrap_or(0)
1140 + 1;
1141 self.ipv4_24_counts.put(analysis.subnet_24, count_24);
1142
1143 let count_16 = self
1144 .ipv4_16_counts
1145 .get(&analysis.subnet_16)
1146 .copied()
1147 .unwrap_or(0)
1148 + 1;
1149 self.ipv4_16_counts.put(analysis.subnet_16, count_16);
1150
1151 if let Some(asn) = analysis.asn {
1152 let count = self.asn_counts.get(&asn).copied().unwrap_or(0) + 1;
1153 self.asn_counts.put(asn, count);
1154 }
1155
1156 if let Some(ref country) = analysis.country {
1157 let count = self.country_counts.get(country).copied().unwrap_or(0) + 1;
1158 self.country_counts.put(country.clone(), count);
1159 }
1160
1161 Ok(())
1162 }
1163
1164 pub fn remove_unified(&mut self, analysis: &UnifiedIPAnalysis) {
1166 match analysis {
1167 UnifiedIPAnalysis::IPv4(ipv4_analysis) => self.remove_ipv4(ipv4_analysis),
1168 UnifiedIPAnalysis::IPv6(ipv6_analysis) => self.remove_node(ipv6_analysis),
1169 }
1170 }
1171
1172 fn remove_ipv4(&mut self, analysis: &IPv4Analysis) {
1174 if let Some(count) = self.ipv4_32_counts.pop(&analysis.ip_addr) {
1176 let new_count = count.saturating_sub(1);
1177 if new_count > 0 {
1178 self.ipv4_32_counts.put(analysis.ip_addr, new_count);
1179 }
1180 }
1181
1182 if let Some(count) = self.ipv4_24_counts.pop(&analysis.subnet_24) {
1183 let new_count = count.saturating_sub(1);
1184 if new_count > 0 {
1185 self.ipv4_24_counts.put(analysis.subnet_24, new_count);
1186 }
1187 }
1188
1189 if let Some(count) = self.ipv4_16_counts.pop(&analysis.subnet_16) {
1190 let new_count = count.saturating_sub(1);
1191 if new_count > 0 {
1192 self.ipv4_16_counts.put(analysis.subnet_16, new_count);
1193 }
1194 }
1195
1196 if let Some(asn) = analysis.asn
1197 && let Some(count) = self.asn_counts.pop(&asn)
1198 {
1199 let new_count = count.saturating_sub(1);
1200 if new_count > 0 {
1201 self.asn_counts.put(asn, new_count);
1202 }
1203 }
1204
1205 if let Some(ref country) = analysis.country
1206 && let Some(count) = self.country_counts.pop(country)
1207 {
1208 let new_count = count.saturating_sub(1);
1209 if new_count > 0 {
1210 self.country_counts.put(country.clone(), new_count);
1211 }
1212 }
1213 }
1214}
1215
1216#[cfg(test)]
1217impl IPDiversityEnforcer {
1218 #[allow(dead_code)]
1219 pub fn config(&self) -> &IPDiversityConfig {
1220 &self.config
1221 }
1222}
1223
1224pub trait GeoProvider: std::fmt::Debug {
1226 fn lookup(&self, ip: Ipv6Addr) -> GeoInfo;
1227}
1228
1229#[derive(Debug, Clone)]
1231pub struct GeoInfo {
1232 pub asn: Option<u32>,
1233 pub country: Option<String>,
1234 pub is_hosting_provider: bool,
1235 pub is_vpn_provider: bool,
1236}
1237
1238#[derive(Debug)]
1240#[allow(dead_code)]
1241pub struct CachedGeoProvider<P: GeoProvider> {
1242 inner: P,
1243 cache: parking_lot::RwLock<HashMap<Ipv6Addr, GeoInfo>>,
1244}
1245
1246#[allow(dead_code)]
1247impl<P: GeoProvider> CachedGeoProvider<P> {
1248 pub fn new(inner: P) -> Self {
1249 Self {
1250 inner,
1251 cache: parking_lot::RwLock::new(HashMap::new()),
1252 }
1253 }
1254}
1255
1256impl<P: GeoProvider> GeoProvider for CachedGeoProvider<P> {
1257 fn lookup(&self, ip: Ipv6Addr) -> GeoInfo {
1258 if let Some(info) = self.cache.read().get(&ip).cloned() {
1259 return info;
1260 }
1261 let info = self.inner.lookup(ip);
1262 self.cache.write().insert(ip, info.clone());
1263 info
1264 }
1265}
1266
1267#[derive(Debug)]
1269#[allow(dead_code)]
1270pub struct StubGeoProvider;
1271impl GeoProvider for StubGeoProvider {
1272 fn lookup(&self, _ip: Ipv6Addr) -> GeoInfo {
1273 GeoInfo {
1274 asn: None,
1275 country: None,
1276 is_hosting_provider: false,
1277 is_vpn_provider: false,
1278 }
1279 }
1280}
1281
1282#[derive(Debug, Clone, Serialize, Deserialize)]
1284#[allow(dead_code)]
1285pub struct DiversityStats {
1286 pub total_64_subnets: usize,
1289 pub total_48_subnets: usize,
1291 pub total_32_subnets: usize,
1293 pub max_nodes_per_ipv6_64: usize,
1295 pub max_nodes_per_ipv6_48: usize,
1297 pub max_nodes_per_ipv6_32: usize,
1299
1300 pub total_ipv4_32: usize,
1303 pub total_ipv4_24_subnets: usize,
1305 pub total_ipv4_16_subnets: usize,
1307 pub max_nodes_per_ipv4_32: usize,
1309 pub max_nodes_per_ipv4_24: usize,
1311 pub max_nodes_per_ipv4_16: usize,
1313
1314 pub total_asns: usize,
1317 pub total_countries: usize,
1319}
1320
1321#[cfg(test)]
1324mod tests {
1325 use super::*;
1326 use crate::quantum_crypto::generate_ml_dsa_keypair;
1327
1328 fn create_test_keypair() -> (MlDsaPublicKey, MlDsaSecretKey) {
1329 generate_ml_dsa_keypair().expect("Failed to generate test keypair")
1330 }
1331
1332 fn create_test_ipv6() -> Ipv6Addr {
1333 Ipv6Addr::new(
1334 0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7334,
1335 )
1336 }
1337
1338 fn create_test_diversity_config() -> IPDiversityConfig {
1339 IPDiversityConfig {
1340 max_nodes_per_ipv6_64: 1,
1342 max_nodes_per_ipv6_48: 3,
1343 max_nodes_per_ipv6_32: 10,
1344 max_nodes_per_ipv4_32: None,
1346 max_nodes_per_ipv4_24: None,
1347 max_nodes_per_ipv4_16: None,
1348 ipv4_limit_floor: None,
1349 ipv4_limit_ceiling: None,
1350 ipv6_limit_floor: None,
1351 ipv6_limit_ceiling: None,
1352 max_per_ip_cap: 50,
1354 max_network_fraction: 0.005,
1355 max_nodes_per_asn: 20,
1357 enable_geolocation_check: true,
1358 min_geographic_diversity: 3,
1359 }
1360 }
1361
1362 #[test]
1363 fn test_ipv6_node_id_generation() -> Result<()> {
1364 let (public_key, secret_key) = create_test_keypair();
1365 let ipv6_addr = create_test_ipv6();
1366
1367 let node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
1368
1369 assert_eq!(node_id.ipv6_addr, ipv6_addr);
1370 assert_eq!(node_id.public_key.len(), 1952); assert_eq!(node_id.signature.len(), 3309); assert_eq!(node_id.node_id.len(), 32); assert_eq!(node_id.salt.len(), 16);
1374 assert!(node_id.timestamp_secs > 0);
1375
1376 Ok(())
1377 }
1378
1379 #[test]
1380 fn test_ipv6_node_id_verification() -> Result<()> {
1381 let (public_key, secret_key) = create_test_keypair();
1382 let ipv6_addr = create_test_ipv6();
1383
1384 let node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
1385 let is_valid = node_id.verify()?;
1386
1387 assert!(is_valid);
1388
1389 Ok(())
1390 }
1391
1392 #[test]
1393 fn test_ipv6_node_id_verification_fails_with_wrong_data() -> Result<()> {
1394 let (public_key, secret_key) = create_test_keypair();
1395 let ipv6_addr = create_test_ipv6();
1396
1397 let mut node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
1398
1399 node_id.node_id[0] ^= 0xFF;
1401 let is_valid = node_id.verify()?;
1402 assert!(!is_valid);
1403
1404 let mut node_id2 = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
1406 node_id2.signature = vec![0u8; 32]; let is_valid2 = node_id2.verify()?;
1408 assert!(!is_valid2);
1409
1410 let mut node_id3 = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
1412 node_id3.public_key = vec![0u8; 16]; let is_valid3 = node_id3.verify()?;
1414 assert!(!is_valid3);
1415
1416 Ok(())
1417 }
1418
1419 #[test]
1420 fn test_ipv6_subnet_extraction() -> Result<()> {
1421 let (public_key, secret_key) = create_test_keypair();
1422 let ipv6_addr = Ipv6Addr::new(
1423 0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1424 );
1425
1426 let node_id = IPv6NodeID::generate(ipv6_addr, &secret_key, &public_key)?;
1427
1428 let subnet_64 = node_id.extract_subnet_64();
1430 let expected_64 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0);
1431 assert_eq!(subnet_64, expected_64);
1432
1433 let subnet_48 = node_id.extract_subnet_48();
1435 let expected_48 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0);
1436 assert_eq!(subnet_48, expected_48);
1437
1438 let subnet_32 = node_id.extract_subnet_32();
1440 let expected_32 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
1441 assert_eq!(subnet_32, expected_32);
1442
1443 Ok(())
1444 }
1445
1446 fn create_test_ipv4() -> Ipv4Addr {
1449 Ipv4Addr::new(192, 168, 1, 100)
1450 }
1451
1452 #[test]
1453 fn test_ipv4_node_id_generation() -> Result<()> {
1454 let (public_key, secret_key) = create_test_keypair();
1455 let ipv4_addr = create_test_ipv4();
1456
1457 let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1458
1459 assert_eq!(node_id.ipv4_addr, ipv4_addr);
1460 assert_eq!(node_id.public_key.len(), 1952); assert_eq!(node_id.signature.len(), 3309); assert_eq!(node_id.node_id.len(), 32); assert_eq!(node_id.salt.len(), 16);
1464 assert!(node_id.timestamp_secs > 0);
1465
1466 Ok(())
1467 }
1468
1469 #[test]
1470 fn test_ipv4_node_id_verification() -> Result<()> {
1471 let (public_key, secret_key) = create_test_keypair();
1472 let ipv4_addr = create_test_ipv4();
1473
1474 let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1475 let is_valid = node_id.verify()?;
1476
1477 assert!(is_valid);
1478
1479 Ok(())
1480 }
1481
1482 #[test]
1483 fn test_ipv4_node_id_verification_fails_with_wrong_data() -> Result<()> {
1484 let (public_key, secret_key) = create_test_keypair();
1485 let ipv4_addr = create_test_ipv4();
1486
1487 let mut node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1488
1489 node_id.node_id[0] ^= 0xFF;
1491 let is_valid = node_id.verify()?;
1492 assert!(!is_valid);
1493
1494 let mut node_id2 = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1496 node_id2.signature = vec![0u8; 32]; let is_valid2 = node_id2.verify()?;
1498 assert!(!is_valid2);
1499
1500 let mut node_id3 = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1502 node_id3.public_key = vec![0u8; 16]; let is_valid3 = node_id3.verify()?;
1504 assert!(!is_valid3);
1505
1506 Ok(())
1507 }
1508
1509 #[test]
1510 fn test_ipv4_subnet_extraction() -> Result<()> {
1511 let (public_key, secret_key) = create_test_keypair();
1512 let ipv4_addr = Ipv4Addr::new(192, 168, 42, 100);
1513
1514 let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1515
1516 let subnet_24 = node_id.extract_subnet_24();
1518 let expected_24 = Ipv4Addr::new(192, 168, 42, 0);
1519 assert_eq!(subnet_24, expected_24);
1520
1521 let subnet_16 = node_id.extract_subnet_16();
1523 let expected_16 = Ipv4Addr::new(192, 168, 0, 0);
1524 assert_eq!(subnet_16, expected_16);
1525
1526 let subnet_8 = node_id.extract_subnet_8();
1528 let expected_8 = Ipv4Addr::new(192, 0, 0, 0);
1529 assert_eq!(subnet_8, expected_8);
1530
1531 Ok(())
1532 }
1533
1534 #[test]
1535 fn test_ipv4_node_id_age() -> Result<()> {
1536 let (public_key, secret_key) = create_test_keypair();
1537 let ipv4_addr = create_test_ipv4();
1538
1539 let node_id = IPv4NodeID::generate(ipv4_addr, &secret_key, &public_key)?;
1540
1541 assert!(node_id.age_secs() < 5);
1543
1544 assert!(!node_id.is_expired(Duration::from_secs(3600)));
1546
1547 assert!(!node_id.is_expired(Duration::from_secs(0)));
1550
1551 Ok(())
1552 }
1553
1554 #[test]
1555 fn test_ipv4_different_addresses_different_node_ids() -> Result<()> {
1556 let (public_key, secret_key) = create_test_keypair();
1557 let addr1 = Ipv4Addr::new(192, 168, 1, 1);
1558 let addr2 = Ipv4Addr::new(192, 168, 1, 2);
1559
1560 let node_id1 = IPv4NodeID::generate(addr1, &secret_key, &public_key)?;
1561 let node_id2 = IPv4NodeID::generate(addr2, &secret_key, &public_key)?;
1562
1563 assert_ne!(node_id1.node_id, node_id2.node_id);
1565
1566 assert!(node_id1.verify()?);
1568 assert!(node_id2.verify()?);
1569
1570 Ok(())
1571 }
1572
1573 #[test]
1576 fn test_ip_diversity_config_default() {
1577 let config = IPDiversityConfig::default();
1578
1579 assert_eq!(config.max_nodes_per_ipv6_64, 1);
1580 assert_eq!(config.max_nodes_per_ipv6_48, 3);
1581 assert_eq!(config.max_nodes_per_ipv6_32, 10);
1582 assert_eq!(config.max_nodes_per_asn, 20);
1583 assert!(config.enable_geolocation_check);
1584 assert_eq!(config.min_geographic_diversity, 3);
1585 }
1586
1587 #[test]
1588 fn test_ip_diversity_enforcer_creation() {
1589 let config = create_test_diversity_config();
1590 let enforcer = IPDiversityEnforcer::with_loopback(config.clone(), true);
1591
1592 assert_eq!(
1593 enforcer.config.max_nodes_per_ipv6_64,
1594 config.max_nodes_per_ipv6_64
1595 );
1596 assert_eq!(enforcer.subnet_64_counts.len(), 0);
1597 assert_eq!(enforcer.subnet_48_counts.len(), 0);
1598 assert_eq!(enforcer.subnet_32_counts.len(), 0);
1599 }
1600
1601 #[test]
1602 fn test_ip_analysis() -> Result<()> {
1603 let config = create_test_diversity_config();
1604 let enforcer = IPDiversityEnforcer::new(config);
1605
1606 let ipv6_addr = create_test_ipv6();
1607 let analysis = enforcer.analyze_ip(ipv6_addr)?;
1608
1609 assert_eq!(
1610 analysis.subnet_64,
1611 IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 64)
1612 );
1613 assert_eq!(
1614 analysis.subnet_48,
1615 IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 48)
1616 );
1617 assert_eq!(
1618 analysis.subnet_32,
1619 IPDiversityEnforcer::extract_subnet_prefix(ipv6_addr, 32)
1620 );
1621 assert!(analysis.asn.is_none()); assert!(analysis.country.is_none()); assert!(!analysis.is_hosting_provider);
1624 assert!(!analysis.is_vpn_provider);
1625 assert_eq!(analysis.reputation_score, 0.5);
1626
1627 Ok(())
1628 }
1629
1630 #[test]
1631 fn test_can_accept_node_basic() -> Result<()> {
1632 let config = create_test_diversity_config();
1633 let enforcer = IPDiversityEnforcer::new(config);
1634
1635 let ipv6_addr = create_test_ipv6();
1636 let analysis = enforcer.analyze_ip(ipv6_addr)?;
1637
1638 assert!(enforcer.can_accept_node(&analysis));
1640
1641 Ok(())
1642 }
1643
1644 #[test]
1645 fn test_add_and_remove_node() -> Result<()> {
1646 let config = create_test_diversity_config();
1647 let mut enforcer = IPDiversityEnforcer::new(config);
1648
1649 let ipv6_addr = create_test_ipv6();
1650 let analysis = enforcer.analyze_ip(ipv6_addr)?;
1651
1652 enforcer.add_node(&analysis)?;
1654 assert_eq!(enforcer.subnet_64_counts.get(&analysis.subnet_64), Some(&1));
1655 assert_eq!(enforcer.subnet_48_counts.get(&analysis.subnet_48), Some(&1));
1656 assert_eq!(enforcer.subnet_32_counts.get(&analysis.subnet_32), Some(&1));
1657
1658 enforcer.remove_node(&analysis);
1660 assert_eq!(enforcer.subnet_64_counts.get(&analysis.subnet_64), None);
1661 assert_eq!(enforcer.subnet_48_counts.get(&analysis.subnet_48), None);
1662 assert_eq!(enforcer.subnet_32_counts.get(&analysis.subnet_32), None);
1663
1664 Ok(())
1665 }
1666
1667 #[test]
1668 fn test_diversity_limits_enforcement() -> Result<()> {
1669 let config = create_test_diversity_config();
1670 let mut enforcer = IPDiversityEnforcer::new(config);
1671
1672 let ipv6_addr1 = Ipv6Addr::new(
1673 0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1674 );
1675 let ipv6_addr2 = Ipv6Addr::new(
1676 0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7335,
1677 ); let analysis1 = enforcer.analyze_ip(ipv6_addr1)?;
1680 let analysis2 = enforcer.analyze_ip(ipv6_addr2)?;
1681
1682 assert!(enforcer.can_accept_node(&analysis1));
1684 enforcer.add_node(&analysis1)?;
1685
1686 assert!(!enforcer.can_accept_node(&analysis2));
1688
1689 let result = enforcer.add_node(&analysis2);
1691 assert!(result.is_err());
1692 assert!(
1693 result
1694 .unwrap_err()
1695 .to_string()
1696 .contains("IP diversity limits exceeded")
1697 );
1698
1699 Ok(())
1700 }
1701
1702 #[test]
1703 fn test_hosting_provider_stricter_limits() -> Result<()> {
1704 let config = IPDiversityConfig {
1705 max_nodes_per_ipv6_64: 4, max_nodes_per_ipv6_48: 8,
1707 ..create_test_diversity_config()
1708 };
1709 let mut enforcer = IPDiversityEnforcer::new(config);
1710
1711 let ipv6_addr = create_test_ipv6();
1712 let mut analysis = enforcer.analyze_ip(ipv6_addr)?;
1713 analysis.is_hosting_provider = true;
1714
1715 assert!(enforcer.can_accept_node(&analysis));
1717 enforcer.add_node(&analysis)?;
1718
1719 let ipv6_addr2 = Ipv6Addr::new(
1721 0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7335,
1722 );
1723 let mut analysis2 = enforcer.analyze_ip(ipv6_addr2)?;
1724 analysis2.is_hosting_provider = true;
1725 analysis2.subnet_64 = analysis.subnet_64; assert!(enforcer.can_accept_node(&analysis2));
1728 enforcer.add_node(&analysis2)?;
1729
1730 let ipv6_addr3 = Ipv6Addr::new(
1732 0x2001, 0xdb8, 0x85a3, 0x0000, 0x0000, 0x8a2e, 0x0370, 0x7336,
1733 );
1734 let mut analysis3 = enforcer.analyze_ip(ipv6_addr3)?;
1735 analysis3.is_hosting_provider = true;
1736 analysis3.subnet_64 = analysis.subnet_64; assert!(!enforcer.can_accept_node(&analysis3));
1739
1740 Ok(())
1741 }
1742
1743 #[test]
1744 fn test_diversity_stats() -> Result<()> {
1745 let config = create_test_diversity_config();
1746 let mut enforcer = IPDiversityEnforcer::new(config);
1747
1748 let addresses = [
1750 Ipv6Addr::new(
1751 0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1752 ),
1753 Ipv6Addr::new(
1754 0x2001, 0xdb8, 0x85a4, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1755 ), Ipv6Addr::new(
1757 0x2002, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1758 ), ];
1760
1761 for addr in addresses {
1762 let analysis = enforcer.analyze_ip(addr)?;
1763 enforcer.add_node(&analysis)?;
1764 }
1765
1766 let stats = enforcer.get_diversity_stats();
1767 assert_eq!(stats.total_64_subnets, 3);
1768 assert_eq!(stats.total_48_subnets, 3);
1769 assert_eq!(stats.total_32_subnets, 2); assert_eq!(stats.max_nodes_per_ipv6_64, 1);
1771 assert_eq!(stats.max_nodes_per_ipv6_48, 1);
1772 assert_eq!(stats.max_nodes_per_ipv6_32, 2); Ok(())
1775 }
1776
1777 #[test]
1778 fn test_extract_subnet_prefix() {
1779 let addr = Ipv6Addr::new(
1780 0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x7334,
1781 );
1782
1783 let prefix_64 = IPDiversityEnforcer::extract_subnet_prefix(addr, 64);
1785 let expected_64 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0);
1786 assert_eq!(prefix_64, expected_64);
1787
1788 let prefix_48 = IPDiversityEnforcer::extract_subnet_prefix(addr, 48);
1790 let expected_48 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0);
1791 assert_eq!(prefix_48, expected_48);
1792
1793 let prefix_32 = IPDiversityEnforcer::extract_subnet_prefix(addr, 32);
1795 let expected_32 = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
1796 assert_eq!(prefix_32, expected_32);
1797
1798 let prefix_56 = IPDiversityEnforcer::extract_subnet_prefix(addr, 56);
1800 let expected_56 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1200, 0, 0, 0, 0);
1801 assert_eq!(prefix_56, expected_56);
1802
1803 let prefix_128 = IPDiversityEnforcer::extract_subnet_prefix(addr, 128);
1805 assert_eq!(prefix_128, addr);
1806 }
1807
1808 #[test]
1809 fn test_security_types_keypair() {
1810 let (public_key, secret_key) =
1811 generate_ml_dsa_keypair().expect("Failed to generate keypair");
1812
1813 let public_key_bytes = public_key.as_bytes();
1814 assert_eq!(public_key_bytes.len(), 1952); let message = b"test message";
1817 let signature = ml_dsa_sign(&secret_key, message).expect("Failed to sign message");
1818 assert_eq!(signature.as_bytes().len(), 3309); assert!(ml_dsa_verify(&public_key, message, &signature).is_ok());
1822 }
1823
1824 #[test]
1825 fn test_ip_analysis_structure() {
1826 let analysis = IPAnalysis {
1827 is_loopback: false,
1828 subnet_64: Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0, 0, 0, 0),
1829 subnet_48: Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0, 0, 0, 0, 0),
1830 subnet_32: Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
1831 asn: Some(64512),
1832 country: Some("US".to_string()),
1833 is_hosting_provider: true,
1834 is_vpn_provider: false,
1835 reputation_score: 0.75,
1836 };
1837
1838 assert_eq!(analysis.asn, Some(64512));
1839 assert_eq!(analysis.country, Some("US".to_string()));
1840 assert!(analysis.is_hosting_provider);
1841 assert!(!analysis.is_vpn_provider);
1842 assert_eq!(analysis.reputation_score, 0.75);
1843 }
1844
1845 #[test]
1846 fn test_diversity_stats_structure() {
1847 let stats = DiversityStats {
1848 total_64_subnets: 100,
1850 total_48_subnets: 50,
1851 total_32_subnets: 25,
1852 max_nodes_per_ipv6_64: 1,
1853 max_nodes_per_ipv6_48: 3,
1854 max_nodes_per_ipv6_32: 10,
1855 total_ipv4_32: 80,
1857 total_ipv4_24_subnets: 40,
1858 total_ipv4_16_subnets: 20,
1859 max_nodes_per_ipv4_32: 1,
1860 max_nodes_per_ipv4_24: 3,
1861 max_nodes_per_ipv4_16: 10,
1862 total_asns: 15,
1864 total_countries: 8,
1865 };
1866
1867 assert_eq!(stats.total_64_subnets, 100);
1869 assert_eq!(stats.total_48_subnets, 50);
1870 assert_eq!(stats.total_32_subnets, 25);
1871 assert_eq!(stats.max_nodes_per_ipv6_64, 1);
1872 assert_eq!(stats.max_nodes_per_ipv6_48, 3);
1873 assert_eq!(stats.max_nodes_per_ipv6_32, 10);
1874 assert_eq!(stats.total_ipv4_32, 80);
1876 assert_eq!(stats.total_ipv4_24_subnets, 40);
1877 assert_eq!(stats.total_ipv4_16_subnets, 20);
1878 assert_eq!(stats.max_nodes_per_ipv4_32, 1);
1879 assert_eq!(stats.max_nodes_per_ipv4_24, 3);
1880 assert_eq!(stats.max_nodes_per_ipv4_16, 10);
1881 assert_eq!(stats.total_asns, 15);
1883 assert_eq!(stats.total_countries, 8);
1884 }
1885
1886 #[test]
1887 fn test_multiple_same_subnet_nodes() -> Result<()> {
1888 let config = IPDiversityConfig {
1889 max_nodes_per_ipv6_64: 3, max_nodes_per_ipv6_48: 5,
1891 max_nodes_per_ipv6_32: 10,
1892 ..create_test_diversity_config()
1893 };
1894 let mut enforcer = IPDiversityEnforcer::new(config);
1895
1896 let _base_addr = Ipv6Addr::new(
1897 0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 0x0000,
1898 );
1899
1900 for i in 1..=3 {
1902 let addr = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, i);
1903 let analysis = enforcer.analyze_ip(addr)?;
1904 assert!(enforcer.can_accept_node(&analysis));
1905 enforcer.add_node(&analysis)?;
1906 }
1907
1908 let addr4 = Ipv6Addr::new(0x2001, 0xdb8, 0x85a3, 0x1234, 0x5678, 0x8a2e, 0x0370, 4);
1910 let analysis4 = enforcer.analyze_ip(addr4)?;
1911 assert!(!enforcer.can_accept_node(&analysis4));
1912
1913 let stats = enforcer.get_diversity_stats();
1914 assert_eq!(stats.total_64_subnets, 1);
1915 assert_eq!(stats.max_nodes_per_ipv6_64, 3);
1916
1917 Ok(())
1918 }
1919
1920 #[test]
1921 fn test_asn_and_country_tracking() -> Result<()> {
1922 let config = create_test_diversity_config();
1923 let mut enforcer = IPDiversityEnforcer::new(config);
1924
1925 let ipv6_addr = create_test_ipv6();
1927 let mut analysis = enforcer.analyze_ip(ipv6_addr)?;
1928 analysis.asn = Some(64512);
1929 analysis.country = Some("US".to_string());
1930
1931 enforcer.add_node(&analysis)?;
1932
1933 assert_eq!(enforcer.asn_counts.get(&64512), Some(&1));
1934 assert_eq!(enforcer.country_counts.get("US"), Some(&1));
1935
1936 enforcer.remove_node(&analysis);
1938 assert!(!enforcer.asn_counts.contains(&64512));
1939 assert!(!enforcer.country_counts.contains("US"));
1940
1941 Ok(())
1942 }
1943}