1use blake3::Hasher;
2use bs58;
3use rand_core::{CryptoRng, RngCore};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::sync::{Arc, RwLock};
7use std::time::{SystemTime, UNIX_EPOCH};
8use thiserror::Error;
9
10use qudag_crypto::ml_dsa::{MlDsaError, MlDsaKeyPair, MlDsaPublicKey};
12use qudag_crypto::ml_kem::MlKem768;
13
14use crate::types::NetworkAddress;
15use crate::types::PeerId;
16
17#[derive(Error, Debug)]
19pub enum DarkResolverError {
20 #[error("Domain name already registered")]
21 DomainExists,
22 #[error("Domain not found")]
23 DomainNotFound,
24 #[error("Invalid domain name format")]
25 InvalidDomain,
26 #[error("Cryptographic operation failed: {0}")]
27 CryptoError(String),
28 #[error("Domain record access error")]
29 StorageError,
30 #[error("Domain has expired")]
31 DomainExpired,
32 #[error("Invalid signature")]
33 InvalidSignature,
34 #[error("Address generation failed: {0}")]
35 AddressGenerationError(String),
36 #[error("DHT operation failed: {0}")]
37 DhtError(String),
38 #[error("ML-DSA error: {0}")]
39 MlDsaError(#[from] MlDsaError),
40}
41
42#[derive(Clone, Debug, Serialize, Deserialize)]
44pub struct DarkDomainRecord {
45 pub signing_public_key: Vec<u8>,
47 pub encryption_public_key: Vec<u8>,
49 pub addresses: Vec<NetworkAddress>,
51 pub alias: Option<String>,
53 pub ttl: u32,
55 pub registered_at: u64,
57 pub expires_at: u64,
59 pub owner_id: PeerId,
61 pub signature: Vec<u8>,
63 pub metadata: HashMap<String, String>,
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
69pub struct DarkAddress {
70 pub address: String,
72 pub domain: String,
74}
75
76#[derive(Clone, Debug, Serialize, Deserialize)]
78pub struct AddressBookEntry {
79 pub name: String,
81 pub dark_address: DarkAddress,
83 pub notes: Option<String>,
85 pub added_at: u64,
87}
88
89impl DarkDomainRecord {
90 pub fn new(
92 signing_keypair: &MlDsaKeyPair,
93 encryption_public_key: Vec<u8>,
94 addresses: Vec<NetworkAddress>,
95 alias: Option<String>,
96 ttl: u32,
97 owner_id: PeerId,
98 ) -> Result<Self, DarkResolverError> {
99 let now = SystemTime::now()
100 .duration_since(UNIX_EPOCH)
101 .unwrap()
102 .as_secs();
103
104 let mut record = Self {
105 signing_public_key: signing_keypair.public_key().to_vec(),
106 encryption_public_key,
107 addresses,
108 alias,
109 ttl,
110 registered_at: now,
111 expires_at: now + ttl as u64,
112 owner_id,
113 signature: vec![],
114 metadata: HashMap::new(),
115 };
116
117 record.sign(signing_keypair)?;
119 Ok(record)
120 }
121
122 fn sign(&mut self, keypair: &MlDsaKeyPair) -> Result<(), DarkResolverError> {
124 let mut rng = rand::thread_rng();
125 let message = self.to_signable_bytes()?;
126 self.signature = keypair
127 .sign(&message, &mut rng)
128 .map_err(|e| DarkResolverError::MlDsaError(e))?;
129 Ok(())
130 }
131
132 pub fn verify_signature(&self) -> Result<(), DarkResolverError> {
134 let public_key = MlDsaPublicKey::from_bytes(&self.signing_public_key)
135 .map_err(|e| DarkResolverError::MlDsaError(e))?;
136 let message = self.to_signable_bytes()?;
137 public_key
138 .verify(&message, &self.signature)
139 .map_err(|e| DarkResolverError::MlDsaError(e))?;
140 Ok(())
141 }
142
143 fn to_signable_bytes(&self) -> Result<Vec<u8>, DarkResolverError> {
145 let mut hasher = Hasher::new();
146 hasher.update(&self.signing_public_key);
147 hasher.update(&self.encryption_public_key);
148 for addr in &self.addresses {
149 hasher.update(
150 &bincode::serialize(addr)
151 .map_err(|e| DarkResolverError::CryptoError(e.to_string()))?,
152 );
153 }
154 if let Some(alias) = &self.alias {
155 hasher.update(alias.as_bytes());
156 }
157 hasher.update(&self.ttl.to_le_bytes());
158 hasher.update(&self.registered_at.to_le_bytes());
159 hasher.update(&self.expires_at.to_le_bytes());
160 hasher.update(
161 &bincode::serialize(&self.owner_id)
162 .map_err(|e| DarkResolverError::CryptoError(e.to_string()))?,
163 );
164 Ok(hasher.finalize().as_bytes().to_vec())
165 }
166
167 pub fn is_expired(&self) -> bool {
169 let now = SystemTime::now()
170 .duration_since(UNIX_EPOCH)
171 .unwrap()
172 .as_secs();
173 now > self.expires_at
174 }
175}
176
177pub struct DarkResolver {
179 domains: Arc<RwLock<HashMap<String, DarkDomainRecord>>>,
181 address_book: Arc<RwLock<HashMap<String, AddressBookEntry>>>,
183 reverse_lookup: Arc<RwLock<HashMap<String, String>>>,
185 dht_client: Option<Arc<dyn DhtClient>>,
187}
188
189pub trait DhtClient: Send + Sync {
191 fn put(&self, key: &[u8], value: &[u8]) -> Result<(), DarkResolverError>;
193 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, DarkResolverError>;
195 fn remove(&self, key: &[u8]) -> Result<(), DarkResolverError>;
197}
198
199impl Default for DarkResolver {
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205impl DarkResolver {
206 pub fn new() -> Self {
208 Self {
209 domains: Arc::new(RwLock::new(HashMap::new())),
210 address_book: Arc::new(RwLock::new(HashMap::new())),
211 reverse_lookup: Arc::new(RwLock::new(HashMap::new())),
212 dht_client: None,
213 }
214 }
215
216 pub fn with_dht(dht_client: Arc<dyn DhtClient>) -> Self {
218 Self {
219 domains: Arc::new(RwLock::new(HashMap::new())),
220 address_book: Arc::new(RwLock::new(HashMap::new())),
221 reverse_lookup: Arc::new(RwLock::new(HashMap::new())),
222 dht_client: Some(dht_client),
223 }
224 }
225
226 pub fn generate_dark_address(
228 public_key: &[u8],
229 custom_name: Option<&str>,
230 ) -> Result<DarkAddress, DarkResolverError> {
231 let mut hasher = Hasher::new();
233 hasher.update(b"dark_address_v1");
234 hasher.update(public_key);
235 let hash = hasher.finalize();
236
237 let address_bytes = &hash.as_bytes()[..20];
239 let address = bs58::encode(address_bytes).into_string();
240
241 let domain = if let Some(name) = custom_name {
243 if !Self::is_valid_custom_name(name) {
244 return Err(DarkResolverError::InvalidDomain);
245 }
246 format!("{}.dark", name)
247 } else {
248 format!("{}.dark", &address[..8].to_lowercase())
250 };
251
252 Ok(DarkAddress { address, domain })
253 }
254
255 fn is_valid_custom_name(name: &str) -> bool {
257 name.len() >= 3
259 && name.len() <= 63
260 && name.chars().all(|c| c.is_alphanumeric() || c == '-')
261 && !name.starts_with('-')
262 && !name.ends_with('-')
263 }
264
265 pub fn register_domain<R: CryptoRng + RngCore>(
267 &self,
268 custom_name: Option<&str>,
269 addresses: Vec<NetworkAddress>,
270 alias: Option<String>,
271 ttl: u32,
272 owner_id: PeerId,
273 rng: &mut R,
274 ) -> Result<DarkAddress, DarkResolverError> {
275 let signing_keypair =
277 MlDsaKeyPair::generate(rng).map_err(|e| DarkResolverError::MlDsaError(e))?;
278
279 let (kem_public, _kem_secret) =
281 MlKem768::keygen().map_err(|e| DarkResolverError::CryptoError(e.to_string()))?;
282
283 let dark_address = Self::generate_dark_address(signing_keypair.public_key(), custom_name)?;
285
286 if !Self::is_valid_dark_domain(&dark_address.domain) {
288 return Err(DarkResolverError::InvalidDomain);
289 }
290
291 let record = DarkDomainRecord::new(
293 &signing_keypair,
294 kem_public.as_bytes().to_vec(),
295 addresses,
296 alias,
297 ttl,
298 owner_id,
299 )?;
300
301 {
303 let mut domains = self
304 .domains
305 .write()
306 .map_err(|_| DarkResolverError::StorageError)?;
307
308 if domains.contains_key(&dark_address.domain) {
309 return Err(DarkResolverError::DomainExists);
310 }
311
312 domains.insert(dark_address.domain.clone(), record.clone());
313 }
314
315 {
317 let mut reverse = self
318 .reverse_lookup
319 .write()
320 .map_err(|_| DarkResolverError::StorageError)?;
321 reverse.insert(dark_address.address.clone(), dark_address.domain.clone());
322 }
323
324 if let Some(dht) = &self.dht_client {
326 let key = Self::domain_to_dht_key(&dark_address.domain);
327 let value = bincode::serialize(&record)
328 .map_err(|e| DarkResolverError::DhtError(e.to_string()))?;
329 dht.put(&key, &value)?;
330 }
331
332 Ok(dark_address)
333 }
334
335 fn domain_to_dht_key(domain: &str) -> Vec<u8> {
337 let mut hasher = Hasher::new();
338 hasher.update(b"dark_domain:");
339 hasher.update(domain.as_bytes());
340 hasher.finalize().as_bytes().to_vec()
341 }
342
343 pub fn lookup_domain(&self, domain: &str) -> Result<DarkDomainRecord, DarkResolverError> {
345 if !Self::is_valid_dark_domain(domain) {
347 return Err(DarkResolverError::InvalidDomain);
348 }
349
350 {
352 let domains = self
353 .domains
354 .read()
355 .map_err(|_| DarkResolverError::StorageError)?;
356
357 if let Some(record) = domains.get(domain) {
358 if record.is_expired() {
360 return Err(DarkResolverError::DomainExpired);
361 }
362 record.verify_signature()?;
364 return Ok(record.clone());
365 }
366 }
367
368 if let Some(dht) = &self.dht_client {
370 let key = Self::domain_to_dht_key(domain);
371 if let Some(value) = dht.get(&key)? {
372 let record: DarkDomainRecord = bincode::deserialize(&value)
373 .map_err(|e| DarkResolverError::DhtError(e.to_string()))?;
374
375 if record.is_expired() {
377 return Err(DarkResolverError::DomainExpired);
378 }
379 record.verify_signature()?;
380
381 let mut domains = self
383 .domains
384 .write()
385 .map_err(|_| DarkResolverError::StorageError)?;
386 domains.insert(domain.to_string(), record.clone());
387
388 return Ok(record);
389 }
390 }
391
392 Err(DarkResolverError::DomainNotFound)
393 }
394
395 pub fn resolve_addresses(
397 &self,
398 domain: &str,
399 ) -> Result<Vec<NetworkAddress>, DarkResolverError> {
400 let record = self.lookup_domain(domain)?;
401 Ok(record.addresses)
402 }
403
404 pub fn add_to_address_book(
406 &self,
407 name: String,
408 dark_address: DarkAddress,
409 notes: Option<String>,
410 ) -> Result<(), DarkResolverError> {
411 let entry = AddressBookEntry {
412 name: name.clone(),
413 dark_address,
414 notes,
415 added_at: SystemTime::now()
416 .duration_since(UNIX_EPOCH)
417 .unwrap()
418 .as_secs(),
419 };
420
421 let mut book = self
422 .address_book
423 .write()
424 .map_err(|_| DarkResolverError::StorageError)?;
425 book.insert(name, entry);
426 Ok(())
427 }
428
429 pub fn lookup_address_book(&self, name: &str) -> Result<AddressBookEntry, DarkResolverError> {
431 let book = self
432 .address_book
433 .read()
434 .map_err(|_| DarkResolverError::StorageError)?;
435 book.get(name)
436 .cloned()
437 .ok_or(DarkResolverError::DomainNotFound)
438 }
439
440 pub fn list_address_book(&self) -> Result<Vec<AddressBookEntry>, DarkResolverError> {
442 let book = self
443 .address_book
444 .read()
445 .map_err(|_| DarkResolverError::StorageError)?;
446 Ok(book.values().cloned().collect())
447 }
448
449 pub fn update_domain(
451 &self,
452 domain: &str,
453 record: DarkDomainRecord,
454 ) -> Result<(), DarkResolverError> {
455 record.verify_signature()?;
457
458 let existing = self.lookup_domain(domain)?;
460
461 if existing.signing_public_key != record.signing_public_key {
463 return Err(DarkResolverError::InvalidSignature);
464 }
465
466 {
468 let mut domains = self
469 .domains
470 .write()
471 .map_err(|_| DarkResolverError::StorageError)?;
472 domains.insert(domain.to_string(), record.clone());
473 }
474
475 if let Some(dht) = &self.dht_client {
477 let key = Self::domain_to_dht_key(domain);
478 let value = bincode::serialize(&record)
479 .map_err(|e| DarkResolverError::DhtError(e.to_string()))?;
480 dht.put(&key, &value)?;
481 }
482
483 Ok(())
484 }
485
486 pub fn cleanup_expired(&self) -> Result<usize, DarkResolverError> {
488 let mut count = 0;
489 let mut to_remove = Vec::new();
490
491 {
493 let domains = self
494 .domains
495 .read()
496 .map_err(|_| DarkResolverError::StorageError)?;
497 for (domain, record) in domains.iter() {
498 if record.is_expired() {
499 to_remove.push(domain.clone());
500 }
501 }
502 }
503
504 {
506 let mut domains = self
507 .domains
508 .write()
509 .map_err(|_| DarkResolverError::StorageError)?;
510 let mut reverse = self
511 .reverse_lookup
512 .write()
513 .map_err(|_| DarkResolverError::StorageError)?;
514
515 for domain in to_remove {
516 if let Some(record) = domains.remove(&domain) {
517 count += 1;
518 let addr = Self::generate_dark_address(&record.signing_public_key, None)?;
520 reverse.remove(&addr.address);
521
522 if let Some(dht) = &self.dht_client {
524 let key = Self::domain_to_dht_key(&domain);
525 let _ = dht.remove(&key);
526 }
527 }
528 }
529 }
530
531 Ok(count)
532 }
533
534 fn is_valid_dark_domain(domain: &str) -> bool {
536 if !domain.ends_with(".dark") {
538 return false;
539 }
540
541 let subdomain = &domain[..domain.len() - 5];
543
544 subdomain.len() >= 3
550 && subdomain.len() <= 63
551 && !subdomain.starts_with('-')
552 && !subdomain.ends_with('-')
553 && !subdomain.contains("--")
554 && subdomain.chars().all(|c| c.is_alphanumeric() || c == '-')
555 }
556}
557
558#[cfg(test)]
559mod tests {
560 use super::*;
561 use rand::thread_rng;
562
563 struct MockDhtClient {
565 storage: Arc<RwLock<HashMap<Vec<u8>, Vec<u8>>>>,
566 }
567
568 impl MockDhtClient {
569 fn new() -> Self {
570 Self {
571 storage: Arc::new(RwLock::new(HashMap::new())),
572 }
573 }
574 }
575
576 impl DhtClient for MockDhtClient {
577 fn put(&self, key: &[u8], value: &[u8]) -> Result<(), DarkResolverError> {
578 let mut storage = self
579 .storage
580 .write()
581 .map_err(|_| DarkResolverError::StorageError)?;
582 storage.insert(key.to_vec(), value.to_vec());
583 Ok(())
584 }
585
586 fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, DarkResolverError> {
587 let storage = self
588 .storage
589 .read()
590 .map_err(|_| DarkResolverError::StorageError)?;
591 Ok(storage.get(key).cloned())
592 }
593
594 fn remove(&self, key: &[u8]) -> Result<(), DarkResolverError> {
595 let mut storage = self
596 .storage
597 .write()
598 .map_err(|_| DarkResolverError::StorageError)?;
599 storage.remove(key);
600 Ok(())
601 }
602 }
603
604 #[test]
605 fn test_valid_dark_domains() {
606 assert!(DarkResolver::is_valid_dark_domain("test.dark"));
607 assert!(DarkResolver::is_valid_dark_domain("my-domain.dark"));
608 assert!(DarkResolver::is_valid_dark_domain("node123.dark"));
609 assert!(DarkResolver::is_valid_dark_domain("a2b.dark"));
610
611 assert!(!DarkResolver::is_valid_dark_domain("invalid"));
613 assert!(!DarkResolver::is_valid_dark_domain(".dark"));
614 assert!(!DarkResolver::is_valid_dark_domain("test.darknet"));
615 assert!(!DarkResolver::is_valid_dark_domain("-test.dark"));
616 assert!(!DarkResolver::is_valid_dark_domain("test-.dark"));
617 assert!(!DarkResolver::is_valid_dark_domain("test--domain.dark"));
618 assert!(!DarkResolver::is_valid_dark_domain("ab.dark")); assert!(!DarkResolver::is_valid_dark_domain(&format!(
620 "{}.dark",
621 "a".repeat(64)
622 ))); }
624
625 #[test]
626 fn test_dark_address_generation() {
627 let public_key = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
628
629 let addr1 = DarkResolver::generate_dark_address(&public_key, None).unwrap();
631 assert!(addr1.address.len() > 0);
632 assert!(addr1.domain.ends_with(".dark"));
633
634 let addr2 = DarkResolver::generate_dark_address(&public_key, Some("mynode")).unwrap();
636 assert_eq!(addr2.domain, "mynode.dark");
637
638 let addr3 = DarkResolver::generate_dark_address(&public_key, None).unwrap();
640 assert_eq!(addr1.address, addr3.address);
641 }
642
643 #[test]
644 fn test_domain_registration_and_resolution() {
645 let mut rng = thread_rng();
646 let resolver = DarkResolver::with_dht(Arc::new(MockDhtClient::new()));
647 let owner_id = PeerId::random();
648 let addresses = vec![
649 NetworkAddress::new([1, 2, 3, 4], 8080),
650 NetworkAddress::new([5, 6, 7, 8], 9090),
651 ];
652
653 let dark_addr = resolver
655 .register_domain(
656 Some("testnode"),
657 addresses.clone(),
658 Some("Test Node".to_string()),
659 3600, owner_id.clone(),
661 &mut rng,
662 )
663 .unwrap();
664
665 assert_eq!(dark_addr.domain, "testnode.dark");
666
667 let record = resolver.lookup_domain(&dark_addr.domain).unwrap();
669 assert_eq!(record.addresses, addresses);
670 assert_eq!(record.alias, Some("Test Node".to_string()));
671 assert_eq!(record.owner_id, owner_id);
672 assert_eq!(record.ttl, 3600);
673
674 let resolved = resolver.resolve_addresses(&dark_addr.domain).unwrap();
676 assert_eq!(resolved, addresses);
677
678 let result = resolver.register_domain(
680 Some("testnode"),
681 vec![],
682 None,
683 3600,
684 PeerId::random(),
685 &mut rng,
686 );
687 assert!(matches!(result, Err(DarkResolverError::DomainExists)));
688 }
689
690 #[test]
691 fn test_address_book() {
692 let resolver = DarkResolver::new();
693 let dark_addr = DarkAddress {
694 address: "3HGvnkH2VwR3cD8r7shs7V".to_string(),
695 domain: "mynode.dark".to_string(),
696 };
697
698 resolver
700 .add_to_address_book(
701 "Alice's Node".to_string(),
702 dark_addr.clone(),
703 Some("Primary node".to_string()),
704 )
705 .unwrap();
706
707 let entry = resolver.lookup_address_book("Alice's Node").unwrap();
709 assert_eq!(entry.dark_address, dark_addr);
710 assert_eq!(entry.notes, Some("Primary node".to_string()));
711
712 let entries = resolver.list_address_book().unwrap();
714 assert_eq!(entries.len(), 1);
715 }
716
717 #[test]
718 fn test_domain_expiration() {
719 let mut rng = thread_rng();
720 let resolver = DarkResolver::new();
721 let owner_id = PeerId::random();
722
723 let signing_keypair = MlDsaKeyPair::generate(&mut rng).unwrap();
725 let (kem_public, _) = MlKem768::keygen().unwrap();
726
727 let mut record = DarkDomainRecord {
728 signing_public_key: signing_keypair.public_key().to_vec(),
729 encryption_public_key: kem_public.as_bytes().to_vec(),
730 addresses: vec![NetworkAddress::new([1, 2, 3, 4], 8080)],
731 alias: None,
732 ttl: 60,
733 registered_at: 1000,
734 expires_at: 1060, owner_id,
736 signature: vec![],
737 metadata: HashMap::new(),
738 };
739
740 record.sign(&signing_keypair).unwrap();
742
743 {
745 let mut domains = resolver.domains.write().unwrap();
746 domains.insert("expired.dark".to_string(), record);
747 }
748
749 let result = resolver.lookup_domain("expired.dark");
751 assert!(matches!(result, Err(DarkResolverError::DomainExpired)));
752
753 let removed = resolver.cleanup_expired().unwrap();
755 assert_eq!(removed, 1);
756
757 let result = resolver.lookup_domain("expired.dark");
759 assert!(matches!(result, Err(DarkResolverError::DomainNotFound)));
760 }
761
762 #[test]
763 fn test_signature_verification() {
764 let mut rng = thread_rng();
765 let signing_keypair = MlDsaKeyPair::generate(&mut rng).unwrap();
766 let (kem_public, _) = MlKem768::keygen().unwrap();
767 let owner_id = PeerId::random();
768
769 let record = DarkDomainRecord::new(
771 &signing_keypair,
772 kem_public.as_bytes().to_vec(),
773 vec![NetworkAddress::new([1, 2, 3, 4], 8080)],
774 None,
775 3600,
776 owner_id,
777 )
778 .unwrap();
779
780 assert!(record.verify_signature().is_ok());
782
783 let mut tampered = record.clone();
785 tampered.ttl = 7200; assert!(tampered.verify_signature().is_err());
789 }
790}