1use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
9use sha2::{Digest, Sha256};
10
11use baseid_core::error::DidError;
12use baseid_crypto::PublicKey;
13
14use crate::document::{
15 DidDocument, Service, ServiceEndpoint, VerificationMethod, VerificationRelationship,
16};
17use crate::resolution::{DidResolver, ResolutionMetadata, ResolutionResult};
18
19const DID_CONTEXT: &str = "https://www.w3.org/ns/did/v1";
20const MULTIKEY_CONTEXT: &str = "https://w3id.org/security/multikey/v1";
21
22pub struct DidPeerResolver;
24
25impl DidResolver for DidPeerResolver {
26 fn method(&self) -> &str {
27 "peer"
28 }
29
30 async fn resolve(&self, did: &str) -> baseid_core::Result<ResolutionResult> {
31 if !did.starts_with("did:peer:") {
32 return Err(DidError::UnsupportedMethod.into());
33 }
34
35 let method_id = &did["did:peer:".len()..];
36 if method_id.is_empty() {
37 return Err(DidError::InvalidDid.into());
38 }
39
40 let numalgo = method_id.chars().next().unwrap();
41 let document = match numalgo {
42 '0' => resolve_numalgo_0(did, &method_id[1..])?,
43 '2' => resolve_numalgo_2(did, &method_id[1..])?,
44 '3' => {
45 return Err(DidError::NotFound.into());
47 }
48 '4' => resolve_numalgo_4(did, &method_id[1..])?,
49 _ => return Err(DidError::InvalidDid.into()),
50 };
51
52 Ok(ResolutionResult {
53 document: Some(document),
54 metadata: ResolutionMetadata {
55 content_type: Some("application/did+ld+json".to_string()),
56 error: None,
57 },
58 })
59 }
60}
61
62impl DidPeerResolver {
63 pub fn create_peer_0(public_key: &PublicKey) -> baseid_core::Result<DidDocument> {
65 let multibase = public_key.to_multibase();
66 let did = format!("did:peer:0{multibase}");
67 resolve_numalgo_0(&did, &multibase)
68 }
69
70 pub fn create_peer_2(
79 keys: &[(char, &PublicKey)],
80 services: &[serde_json::Value],
81 ) -> baseid_core::Result<(String, DidDocument)> {
82 let mut did = "did:peer:2".to_string();
83
84 for (purpose, key) in keys {
85 let multibase = key.to_multibase();
86 did.push('.');
87 did.push(*purpose);
88 did.push_str(&multibase);
89 }
90
91 for svc in services {
92 let json_str = serde_json::to_string(&abbreviate_service(svc))
93 .map_err(|_| DidError::InvalidDid)?;
94 let encoded = URL_SAFE_NO_PAD.encode(json_str.as_bytes());
95 did.push_str(".S");
96 did.push_str(&encoded);
97 }
98
99 let method_id = &did["did:peer:2".len()..];
100 let doc = resolve_numalgo_2(&did, method_id)?;
101 Ok((did, doc))
102 }
103
104 pub fn create_peer_3(peer2_did: &str) -> baseid_core::Result<String> {
106 if !peer2_did.starts_with("did:peer:2") {
107 return Err(DidError::InvalidDid.into());
108 }
109 let method_id = &peer2_did["did:peer:2".len()..];
110 let hash = Sha256::digest(method_id.as_bytes());
111 let mut multihash = vec![0x12, 0x20];
113 multihash.extend_from_slice(&hash);
114 let multibase = format!("z{}", bs58::encode(&multihash).into_string());
115 Ok(format!("did:peer:3{multibase}"))
116 }
117
118 pub fn create_peer_4(doc: &DidDocument) -> baseid_core::Result<(String, String)> {
120 let mut doc_json = serde_json::to_value(doc).map_err(|_| DidError::InvalidDid)?;
122 if let Some(obj) = doc_json.as_object_mut() {
123 obj.remove("id");
124 }
125 let doc_bytes = serde_json::to_vec(&doc_json).map_err(|_| DidError::InvalidDid)?;
126
127 let mut encoded = vec![0x02, 0x00];
129 encoded.extend_from_slice(&doc_bytes);
130 let encoded_multibase = format!("z{}", bs58::encode(&encoded).into_string());
131
132 let hash = Sha256::digest(&encoded);
134 let mut multihash = vec![0x12, 0x20];
135 multihash.extend_from_slice(&hash);
136 let hash_multibase = format!("z{}", bs58::encode(&multihash).into_string());
137
138 let long_form = format!("did:peer:4{hash_multibase}:{encoded_multibase}");
139 let short_form = format!("did:peer:4{hash_multibase}");
140
141 Ok((long_form, short_form))
142 }
143}
144
145fn resolve_numalgo_0(did: &str, multibase_key: &str) -> baseid_core::Result<DidDocument> {
148 let _public_key =
149 PublicKey::from_multibase(multibase_key).map_err(|_| DidError::ResolutionFailed)?;
150
151 let vm_id = format!("{did}#{multibase_key}");
152 let vm = VerificationMethod {
153 id: vm_id.clone(),
154 r#type: "Multikey".to_string(),
155 controller: did.to_string(),
156 public_key_jwk: None,
157 public_key_multibase: Some(multibase_key.to_string()),
158 };
159
160 Ok(DidDocument {
161 id: did.to_string(),
162 context: vec![DID_CONTEXT.to_string(), MULTIKEY_CONTEXT.to_string()],
163 verification_method: vec![vm],
164 authentication: vec![VerificationRelationship::Reference(vm_id.clone())],
165 assertion_method: vec![VerificationRelationship::Reference(vm_id)],
166 key_agreement: vec![],
167 service: vec![],
168 })
169}
170
171fn resolve_numalgo_2(did: &str, elements_str: &str) -> baseid_core::Result<DidDocument> {
174 let mut verification_methods = Vec::new();
175 let mut authentication = Vec::new();
176 let mut assertion_method = Vec::new();
177 let mut key_agreement = Vec::new();
178 let mut services = Vec::new();
179 let mut key_index = 1u32;
180 let mut svc_index = 0u32;
181
182 let elements: Vec<&str> = elements_str.split('.').filter(|s| !s.is_empty()).collect();
184
185 for element in &elements {
186 if element.is_empty() {
187 continue;
188 }
189
190 let purpose = element.chars().next().unwrap();
191 let value = &element[1..];
192
193 if purpose == 'S' {
194 let decoded = URL_SAFE_NO_PAD
196 .decode(value.as_bytes())
197 .map_err(|_| DidError::ResolutionFailed)?;
198 let svc_json: serde_json::Value =
199 serde_json::from_slice(&decoded).map_err(|_| DidError::ResolutionFailed)?;
200 let expanded = expand_service(&svc_json);
201
202 let svc_id = if svc_index == 0 {
203 format!("{did}#service")
204 } else {
205 format!("{did}#service-{svc_index}")
206 };
207 svc_index += 1;
208
209 let svc_type = expanded
210 .get("type")
211 .and_then(|v| v.as_str())
212 .unwrap_or("DIDCommMessaging")
213 .to_string();
214 let endpoint = expanded
215 .get("serviceEndpoint")
216 .and_then(|v| v.as_str())
217 .unwrap_or("")
218 .to_string();
219
220 services.push(Service {
221 id: svc_id,
222 r#type: svc_type,
223 service_endpoint: ServiceEndpoint::Uri(endpoint),
224 });
225 } else {
226 let vm_id = format!("{did}#key-{key_index}");
228 key_index += 1;
229
230 let vm = VerificationMethod {
231 id: vm_id.clone(),
232 r#type: "Multikey".to_string(),
233 controller: did.to_string(),
234 public_key_jwk: None,
235 public_key_multibase: Some(value.to_string()),
236 };
237 verification_methods.push(vm);
238
239 let rel = VerificationRelationship::Reference(vm_id);
240 match purpose {
241 'V' => authentication.push(rel),
242 'E' => key_agreement.push(rel),
243 'A' => assertion_method.push(rel),
244 'I' | 'D' => { }
245 _ => {}
246 }
247 }
248 }
249
250 Ok(DidDocument {
251 id: did.to_string(),
252 context: vec![DID_CONTEXT.to_string(), MULTIKEY_CONTEXT.to_string()],
253 verification_method: verification_methods,
254 authentication,
255 assertion_method,
256 key_agreement,
257 service: services,
258 })
259}
260
261fn resolve_numalgo_4(did: &str, rest: &str) -> baseid_core::Result<DidDocument> {
264 let parts: Vec<&str> = rest.splitn(2, ':').collect();
266 if parts.len() != 2 {
267 return Err(DidError::NotFound.into());
269 }
270
271 let hash_multibase = parts[0];
272 let encoded_multibase = parts[1];
273
274 if !encoded_multibase.starts_with('z') {
276 return Err(DidError::InvalidDid.into());
277 }
278 let encoded_bytes = bs58::decode(&encoded_multibase[1..])
279 .into_vec()
280 .map_err(|_| DidError::InvalidDid)?;
281
282 if encoded_bytes.len() < 2 || encoded_bytes[0] != 0x02 || encoded_bytes[1] != 0x00 {
284 return Err(DidError::InvalidDid.into());
285 }
286 let doc_bytes = &encoded_bytes[2..];
287
288 let computed_hash = Sha256::digest(&encoded_bytes);
290 let mut expected_multihash = vec![0x12, 0x20];
291 expected_multihash.extend_from_slice(&computed_hash);
292 let expected_multibase = format!("z{}", bs58::encode(&expected_multihash).into_string());
293
294 if hash_multibase != expected_multibase {
295 return Err(DidError::ResolutionFailed.into());
296 }
297
298 let mut doc_json: serde_json::Value =
301 serde_json::from_slice(doc_bytes).map_err(|_| DidError::ResolutionFailed)?;
302
303 if let Some(obj) = doc_json.as_object_mut() {
304 obj.insert("id".to_string(), serde_json::Value::String(did.to_string()));
305 }
306
307 let doc: DidDocument =
308 serde_json::from_value(doc_json).map_err(|_| DidError::ResolutionFailed)?;
309
310 Ok(doc)
311}
312
313fn abbreviate_service(svc: &serde_json::Value) -> serde_json::Value {
316 let mut result = serde_json::Map::new();
317 if let Some(obj) = svc.as_object() {
318 for (k, v) in obj {
319 let key = match k.as_str() {
320 "type" => "t",
321 "serviceEndpoint" => "s",
322 "routingKeys" => "r",
323 "accept" => "a",
324 other => other,
325 };
326 let val = if k == "type" {
327 match v.as_str() {
328 Some("DIDCommMessaging") => serde_json::Value::String("dm".to_string()),
329 _ => v.clone(),
330 }
331 } else {
332 v.clone()
333 };
334 result.insert(key.to_string(), val);
335 }
336 }
337 serde_json::Value::Object(result)
338}
339
340fn expand_service(svc: &serde_json::Value) -> serde_json::Value {
341 let mut result = serde_json::Map::new();
342 if let Some(obj) = svc.as_object() {
343 for (k, v) in obj {
344 let key = match k.as_str() {
345 "t" => "type",
346 "s" => "serviceEndpoint",
347 "r" => "routingKeys",
348 "a" => "accept",
349 other => other,
350 };
351 let val = if key == "type" {
352 match v.as_str() {
353 Some("dm") => serde_json::Value::String("DIDCommMessaging".to_string()),
354 _ => v.clone(),
355 }
356 } else {
357 v.clone()
358 };
359 result.insert(key.to_string(), val);
360 }
361 }
362 serde_json::Value::Object(result)
363}
364
365mod bs58 {
368 const ALPHABET: &[u8; 58] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
369
370 pub struct Encoder(Vec<u8>);
371
372 pub fn encode(data: &[u8]) -> Encoder {
373 Encoder(data.to_vec())
374 }
375
376 impl Encoder {
377 pub fn into_string(self) -> String {
378 let mut result = Vec::new();
379 let mut data = self.0;
380
381 let leading_zeros = data.iter().take_while(|&&b| b == 0).count();
383
384 while !data.is_empty() {
386 let mut remainder = 0u32;
387 let mut new_data = Vec::new();
388 for &byte in &data {
389 let acc = (remainder << 8) | byte as u32;
390 let digit = acc / 58;
391 remainder = acc % 58;
392 if !new_data.is_empty() || digit > 0 {
393 new_data.push(digit as u8);
394 }
395 }
396 result.push(ALPHABET[remainder as usize]);
397 data = new_data;
398 }
399
400 result.extend(std::iter::repeat_n(b'1', leading_zeros));
402
403 result.reverse();
404 String::from_utf8(result).unwrap_or_default()
405 }
406 }
407
408 pub struct Decoder(String);
409
410 pub fn decode(s: &str) -> Decoder {
411 Decoder(s.to_string())
412 }
413
414 impl Decoder {
415 pub fn into_vec(self) -> Result<Vec<u8>, String> {
416 let mut result: Vec<u8> = Vec::new();
417 let leading_ones = self.0.bytes().take_while(|&b| b == b'1').count();
418
419 for ch in self.0.bytes() {
420 let pos = ALPHABET
421 .iter()
422 .position(|&a| a == ch)
423 .ok_or_else(|| format!("invalid base58 char: {}", ch as char))?;
424 let mut carry = pos as u32;
425 for byte in result.iter_mut().rev() {
426 carry += *byte as u32 * 58;
427 *byte = (carry & 0xff) as u8;
428 carry >>= 8;
429 }
430 while carry > 0 {
431 result.insert(0, (carry & 0xff) as u8);
432 carry >>= 8;
433 }
434 }
435
436 let mut final_result = vec![0u8; leading_ones];
438 final_result.extend(result);
439 Ok(final_result)
440 }
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use super::*;
447 use baseid_core::types::KeyType;
448 use baseid_crypto::KeyPair;
449 use serde_json::json;
450
451 #[test]
452 fn peer_0_create_and_resolve() {
453 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
454 let doc = DidPeerResolver::create_peer_0(&kp.public).unwrap();
455 assert!(doc.id.starts_with("did:peer:0z6Mk"));
456 assert_eq!(doc.verification_method.len(), 1);
457 assert_eq!(doc.authentication.len(), 1);
458 }
459
460 #[tokio::test]
461 async fn peer_0_resolve_roundtrip() {
462 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
463 let doc = DidPeerResolver::create_peer_0(&kp.public).unwrap();
464 let resolver = DidPeerResolver;
465 let result = resolver.resolve(&doc.id).await.unwrap();
466 let resolved = result.document.unwrap();
467 assert_eq!(resolved.id, doc.id);
468 assert_eq!(resolved.verification_method.len(), 1);
469 }
470
471 #[test]
472 fn peer_2_create_with_keys_and_service() {
473 let auth_kp = KeyPair::generate(KeyType::Ed25519).unwrap();
474 let enc_kp = KeyPair::generate(KeyType::P256).unwrap();
475
476 let service = json!({
477 "type": "DIDCommMessaging",
478 "serviceEndpoint": "https://example.com/didcomm",
479 "routingKeys": [],
480 "accept": ["didcomm/v2"]
481 });
482
483 let (did, doc) = DidPeerResolver::create_peer_2(
484 &[('V', &auth_kp.public), ('E', &enc_kp.public)],
485 &[service],
486 )
487 .unwrap();
488
489 assert!(did.starts_with("did:peer:2.V"));
490 assert!(did.contains(".E"));
491 assert!(did.contains(".S"));
492 assert_eq!(doc.verification_method.len(), 2);
493 assert_eq!(doc.authentication.len(), 1);
494 assert_eq!(doc.key_agreement.len(), 1);
495 assert_eq!(doc.service.len(), 1);
496 assert_eq!(doc.service[0].r#type, "DIDCommMessaging");
497 }
498
499 #[tokio::test]
500 async fn peer_2_resolve_roundtrip() {
501 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
502 let svc = json!({"type": "DIDCommMessaging", "serviceEndpoint": "https://example.com"});
503 let (did, _) = DidPeerResolver::create_peer_2(&[('V', &kp.public)], &[svc]).unwrap();
504
505 let resolver = DidPeerResolver;
506 let result = resolver.resolve(&did).await.unwrap();
507 let doc = result.document.unwrap();
508 assert_eq!(doc.verification_method.len(), 1);
509 assert_eq!(doc.service.len(), 1);
510 }
511
512 #[test]
513 fn peer_2_service_abbreviation() {
514 let svc = json!({"type": "DIDCommMessaging", "serviceEndpoint": "https://x.com", "accept": ["didcomm/v2"]});
515 let abbreviated = abbreviate_service(&svc);
516 assert_eq!(abbreviated["t"], "dm");
517 assert_eq!(abbreviated["s"], "https://x.com");
518 assert_eq!(abbreviated["a"], json!(["didcomm/v2"]));
519
520 let expanded = expand_service(&abbreviated);
521 assert_eq!(expanded["type"], "DIDCommMessaging");
522 assert_eq!(expanded["serviceEndpoint"], "https://x.com");
523 }
524
525 #[test]
526 fn peer_3_from_peer_2() {
527 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
528 let (peer2, _) = DidPeerResolver::create_peer_2(&[('V', &kp.public)], &[]).unwrap();
529 let peer3 = DidPeerResolver::create_peer_3(&peer2).unwrap();
530 assert!(peer3.starts_with("did:peer:3z"));
531 let kp2 = KeyPair::generate(KeyType::Ed25519).unwrap();
533 let (peer2b, _) = DidPeerResolver::create_peer_2(&[('V', &kp2.public)], &[]).unwrap();
534 let peer3b = DidPeerResolver::create_peer_3(&peer2b).unwrap();
535 assert_ne!(peer3, peer3b);
536 }
537
538 #[test]
539 fn peer_4_create_and_resolve() {
540 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
541 let multibase = kp.public.to_multibase();
542 let doc = DidDocument {
543 id: String::new(),
544 context: vec![DID_CONTEXT.to_string()],
545 verification_method: vec![VerificationMethod {
546 id: "#key-1".to_string(),
547 r#type: "Multikey".to_string(),
548 controller: String::new(),
549 public_key_jwk: None,
550 public_key_multibase: Some(multibase),
551 }],
552 authentication: vec![VerificationRelationship::Reference("#key-1".to_string())],
553 assertion_method: vec![],
554 key_agreement: vec![],
555 service: vec![],
556 };
557
558 let (long_form, short_form) = DidPeerResolver::create_peer_4(&doc).unwrap();
559 assert!(long_form.starts_with("did:peer:4z"));
560 assert!(long_form.contains(':'));
561 assert!(short_form.starts_with("did:peer:4z"));
562 assert!(!short_form[10..].contains(':'));
564 }
565
566 #[tokio::test]
567 async fn peer_4_resolve_long_form() {
568 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
569 let multibase = kp.public.to_multibase();
570 let doc = DidDocument {
571 id: String::new(),
572 context: vec![DID_CONTEXT.to_string()],
573 verification_method: vec![VerificationMethod {
574 id: "#key-1".to_string(),
575 r#type: "Multikey".to_string(),
576 controller: String::new(),
577 public_key_jwk: None,
578 public_key_multibase: Some(multibase),
579 }],
580 authentication: vec![],
581 assertion_method: vec![],
582 key_agreement: vec![],
583 service: vec![],
584 };
585
586 let (long_form, _) = DidPeerResolver::create_peer_4(&doc).unwrap();
587 let resolver = DidPeerResolver;
588 let result = resolver.resolve(&long_form).await.unwrap();
589 let resolved = result.document.unwrap();
590 assert_eq!(resolved.verification_method.len(), 1);
591 }
592
593 #[tokio::test]
594 async fn peer_4_short_form_requires_stored() {
595 let resolver = DidPeerResolver;
596 let result = resolver.resolve("did:peer:4zQmSomeShortFormHash").await;
597 assert!(result.is_err());
599 }
600
601 #[tokio::test]
602 async fn reject_invalid_peer_did() {
603 let resolver = DidPeerResolver;
604 assert!(resolver.resolve("did:peer:9invalid").await.is_err());
605 assert!(resolver.resolve("did:peer:").await.is_err());
606 assert!(resolver.resolve("did:key:z6Mk123").await.is_err());
607 }
608
609 #[test]
610 fn peer_2_multiple_services() {
611 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
612 let svc1 = json!({"type": "DIDCommMessaging", "serviceEndpoint": "https://a.com"});
613 let svc2 = json!({"type": "LinkedDomains", "serviceEndpoint": "https://b.com"});
614 let (_, doc) = DidPeerResolver::create_peer_2(&[('V', &kp.public)], &[svc1, svc2]).unwrap();
615 assert_eq!(doc.service.len(), 2);
616 assert_eq!(doc.service[0].id, format!("{}#service", doc.id));
617 assert_eq!(doc.service[1].id, format!("{}#service-1", doc.id));
618 }
619}