1use multibase::Base::{Base58Btc, Base64Url};
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4
5use super::{
6 errors::DIDPeerMethodError,
7 util::{abbreviate_service_for_did_peer_2, validate_input_document},
8};
9
10use crate::{
11 crypto::{
12 alg::decode_multikey,
13 sha256_multihash, Algorithm, Ed25519KeyPair, PublicKeyFormat, {Generate, KeyMaterial},
14 },
15 didcore::{self, Document as DIDDocument, KeyFormat, Service, VerificationMethod},
16 ldmodel::Context,
17 methods::{errors::DIDResolutionError, peer::util, traits::DIDMethod, DidKey},
18};
19
20lazy_static::lazy_static!(
21 pub static ref DID_PEER_0_REGEX: Regex = Regex::new("^did:peer:(0(z)([1-9a-km-zA-HJ-NP-Z]+))$").unwrap();
22 pub static ref DID_PEER_2_REGEX: Regex = Regex::new("^did:peer:(2((\\.[AEVID](z)([1-9a-km-zA-HJ-NP-Z]+))+(\\.(S)[0-9a-zA-Z]*)*))$").unwrap();
23);
24
25const MULTICODEC_JSON: [u8; 2] = [0x80, 0x04];
26
27#[derive(Default)]
28pub struct DidPeer {
29 key_format: PublicKeyFormat,
31}
32
33#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
34#[serde(rename_all = "camelCase")]
35pub enum Purpose {
36 Assertion,
37 Encryption, Verification, CapabilityInvocation,
40 CapabilityDelegation,
41 Service,
42}
43
44#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
45#[serde(rename_all = "camelCase")]
46pub struct PurposedKey {
47 pub purpose: Purpose,
48 pub public_key_multibase: String,
49}
50
51impl DIDMethod for DidPeer {
52 fn name() -> String {
53 "did:peer".to_string()
54 }
55}
56
57impl Purpose {
58 pub fn code(&self) -> char {
60 match self {
61 Purpose::Assertion => 'A',
62 Purpose::Encryption => 'E',
63 Purpose::Verification => 'V',
64 Purpose::CapabilityInvocation => 'I',
65 Purpose::CapabilityDelegation => 'D',
66 Purpose::Service => 'S',
67 }
68 }
69
70 pub fn from_code(c: &char) -> Result<Self, DIDPeerMethodError> {
72 match c {
73 'A' => Ok(Purpose::Assertion),
74 'E' => Ok(Purpose::Encryption),
75 'V' => Ok(Purpose::Verification),
76 'I' => Ok(Purpose::CapabilityInvocation),
77 'D' => Ok(Purpose::CapabilityDelegation),
78 'S' => Ok(Purpose::Service),
79 _ => Err(DIDPeerMethodError::InvalidPurposeCode),
80 }
81 }
82}
83
84impl DidPeer {
85 pub fn new() -> Self {
87 Self::default()
88 }
89
90 pub fn with_format(key_format: PublicKeyFormat) -> Self {
92 Self { key_format }
93 }
94
95 pub fn create_did_peer_0_from_ed25519_keypair(keypair: &Ed25519KeyPair) -> Result<String, DIDPeerMethodError> {
103 let did_key = DidKey::from_ed25519_keypair(keypair)?;
104
105 Ok(did_key.replace("did:key:", "did:peer:0"))
106 }
107
108 pub fn create_did_peer_0_from_raw_public_key(alg: Algorithm, bytes: &[u8]) -> Result<String, DIDPeerMethodError> {
112 let did_key = DidKey::from_raw_public_key(alg, bytes)?;
113
114 Ok(did_key.replace("did:key:", "did:peer:0"))
115 }
116
117 pub fn create_did_peer_1_from_stored_variant(diddoc: &DIDDocument) -> Result<String, DIDPeerMethodError> {
121 if !diddoc.id.is_empty() {
122 return Err(DIDPeerMethodError::InvalidStoredVariant);
123 }
124
125 let json = json_canon::to_string(diddoc)?;
126 let multihash = sha256_multihash(json.as_bytes());
127
128 Ok(format!("did:peer:1{multihash}"))
129 }
130
131 pub fn create_did_peer_2(keys: &[PurposedKey], services: &[Service]) -> Result<String, DIDPeerMethodError> {
135 if keys.is_empty() && services.is_empty() {
136 return Err(DIDPeerMethodError::EmptyArguments);
137 }
138
139 let mut chain = vec![];
141
142 for key in keys {
144 if matches!(key.purpose, Purpose::Service) {
145 return Err(DIDPeerMethodError::UnexpectedPurpose);
146 }
147
148 chain.push(format!(".{}{}", key.purpose.code(), key.public_key_multibase));
149 }
150
151 for service in services {
153 let abbreviated_service = abbreviate_service_for_did_peer_2(service)?;
154 let encoded_service = Base64Url.encode(abbreviated_service);
155
156 chain.push(format!(".{}{}", Purpose::Service.code(), encoded_service));
157 }
158
159 Ok(format!("did:peer:2{}", chain.join("")))
160 }
161
162 pub fn create_did_peer_3(did: &str) -> Result<String, DIDPeerMethodError> {
166 let stripped = match did.strip_prefix("did:peer:2") {
167 Some(stripped) => stripped,
168 None => return Err(DIDPeerMethodError::IllegalArgument),
169 };
170
171 let multihash = sha256_multihash(stripped.as_bytes());
173
174 Ok(format!("did:peer:3{multihash}"))
175 }
176
177 pub fn create_did_peer_4_from_stored_variant(diddoc: &DIDDocument) -> Result<String, DIDPeerMethodError> {
181 validate_input_document(diddoc)?;
183
184 let json = json_canon::to_string(diddoc)?;
186 let encoded = multibase::encode(Base58Btc, [&MULTICODEC_JSON, json.as_bytes()].concat());
187
188 let hash = sha256_multihash(encoded.as_bytes());
190
191 Ok(format!("did:peer:4{hash}:{encoded}"))
192 }
193
194 pub fn shorten_did_peer_4(did: &str) -> Result<String, DIDPeerMethodError> {
198 let stripped = match did.strip_prefix("did:peer:4") {
199 Some(stripped) => stripped,
200 None => return Err(DIDPeerMethodError::IllegalArgument),
201 };
202
203 let segments: Vec<_> = stripped.split(':').collect();
205 if segments.len() != 2 || segments[1].is_empty() {
206 return Err(DIDPeerMethodError::MalformedLongPeerDID);
207 }
208
209 let (hash, encoded) = (segments[0], segments[1]);
210
211 if hash != sha256_multihash(encoded.as_bytes()) {
213 return Err(DIDPeerMethodError::InvalidHash);
214 }
215
216 Ok(format!("did:peer:4{hash}"))
217 }
218
219 pub fn expand(&self, did: &str) -> Result<DIDDocument, DIDResolutionError> {
225 if !did.starts_with("did:peer:") {
226 return Err(DIDResolutionError::InvalidDid);
227 }
228
229 match did {
230 s if s.starts_with("did:peer:0") => self.expand_did_peer_0(did).map_err(Into::into),
231 s if s.starts_with("did:peer:2") => self.expand_did_peer_2(did).map_err(Into::into),
232 s if s.starts_with("did:peer:4") => self.expand_did_peer_4(did).map_err(Into::into),
233 _ => Err(DIDResolutionError::MethodNotSupported),
234 }
235}
236
237
238 pub fn expand_did_peer_0(&self, did: &str) -> Result<DIDDocument, DIDPeerMethodError> {
242 if !DID_PEER_0_REGEX.is_match(did) {
243 return Err(DIDPeerMethodError::RegexMismatch);
244 }
245
246 let multikey = did.strip_prefix("did:peer:0").unwrap();
248 let (alg, key) = decode_multikey(multikey).map_err(|_| DIDPeerMethodError::MalformedPeerDID)?;
249
250 let signature_verification_method = self.derive_verification_method(did, multikey, alg, &key)?;
252
253 let mut diddoc = DIDDocument {
255 context: Context::SetOfString(vec![
256 String::from("https://www.w3.org/ns/did/v1"),
257 match self.key_format {
258 PublicKeyFormat::Multikey => String::from("https://w3id.org/security/multikey/v1"),
259 PublicKeyFormat::Jwk => String::from("https://w3id.org/security/suites/jws-2020/v1"),
260 },
261 ]),
262 id: did.to_string(),
263 controller: None,
264 also_known_as: None,
265 verification_method: Some(vec![signature_verification_method.clone()]),
266 authentication: Some(vec![didcore::VerificationMethodType::Reference(
267 signature_verification_method.id.clone(), )]),
269 assertion_method: Some(vec![didcore::VerificationMethodType::Reference(
270 signature_verification_method.id.clone(), )]),
272 capability_delegation: Some(vec![didcore::VerificationMethodType::Reference(
273 signature_verification_method.id.clone(), )]),
275 capability_invocation: Some(vec![didcore::VerificationMethodType::Reference(
276 signature_verification_method.id.clone(), )]),
278 key_agreement: None,
279 service: None,
280 additional_properties: None,
281 proof: None,
282 };
283
284 if alg == Algorithm::Ed25519 {
286 let encryption_verification_method = self.derive_encryption_verification_method(did, &key)?;
288
289 let verification_method = diddoc.verification_method.as_mut().unwrap();
291 verification_method.push(encryption_verification_method.clone());
292 diddoc.key_agreement = Some(vec![didcore::VerificationMethodType::Reference(
293 encryption_verification_method.id.clone(), )]);
295 }
296
297 Ok(diddoc)
299 }
300
301 fn derive_verification_method(&self, did: &str, multikey: &str, alg: Algorithm, key: &[u8]) -> Result<VerificationMethod, DIDPeerMethodError> {
303 if let Some(required_length) = alg.public_key_length() {
304 if required_length != key.len() {
305 return Err(DIDResolutionError::InvalidPublicKeyLength.into());
306 }
307 }
308
309 Ok(VerificationMethod {
310 id: format!("#{multikey}"),
311 key_type: String::from(match self.key_format {
312 PublicKeyFormat::Multikey => "Multikey",
313 PublicKeyFormat::Jwk => "JsonWebKey2020",
314 }),
315 controller: did.to_string(),
316 public_key: Some(match self.key_format {
317 PublicKeyFormat::Multikey => KeyFormat::Multibase(String::from(multikey)),
318 PublicKeyFormat::Jwk => KeyFormat::Jwk(alg.build_jwk(key).map_err(|_| DIDResolutionError::InternalError)?),
319 }),
320 ..Default::default()
321 })
322 }
323
324 fn derive_encryption_verification_method(&self, did: &str, key: &[u8]) -> Result<VerificationMethod, DIDPeerMethodError> {
326 let key: [u8; 32] = key.try_into().map_err(|_| DIDResolutionError::InvalidPublicKeyLength)?;
327 let ed25519_keypair = Ed25519KeyPair::from_public_key(&key).map_err(|_| DIDResolutionError::InternalError)?;
328 let x25519_keypair = ed25519_keypair.get_x25519().map_err(|_| DIDResolutionError::InternalError)?;
329
330 let alg = Algorithm::X25519;
331 let enc_key = &x25519_keypair.public_key_bytes().unwrap()[..];
332 let enc_multikey = multibase::encode(Base58Btc, [&alg.muticodec_prefix(), enc_key].concat());
333
334 self.derive_verification_method(did, &enc_multikey, alg, enc_key)
335 }
336
337 pub fn expand_did_peer_2(&self, did: &str) -> Result<DIDDocument, DIDPeerMethodError> {
341 if !DID_PEER_2_REGEX.is_match(did) {
342 return Err(DIDPeerMethodError::RegexMismatch);
343 }
344
345 let alias = Self::create_did_peer_3(did)?;
348
349 let chain = did.strip_prefix("did:peer:2.").unwrap();
352 let chain: Vec<(Purpose, &str)> = chain
353 .split('.')
354 .map(|e| {
355 (
356 Purpose::from_code(&e.chars().nth(0).unwrap()).expect("invalid purpose prefix bypasses regex check"),
357 &e[1..],
358 )
359 })
360 .collect();
361
362 let context = Context::SetOfString(vec![
365 String::from("https://www.w3.org/ns/did/v1"),
366 match self.key_format {
367 PublicKeyFormat::Multikey => String::from("https://w3id.org/security/multikey/v1"),
368 PublicKeyFormat::Jwk => String::from("https://w3id.org/security/suites/jws-2020/v1"),
369 },
370 ]);
371
372 let mut authentication = vec![];
375 let mut assertion_method = vec![];
376 let mut key_agreement = vec![];
377 let mut capability_delegation = vec![];
378 let mut capability_invocation = vec![];
379
380 let key_chain = chain.iter().filter(|(purpose, _)| purpose != &Purpose::Service);
383
384 let mut methods: Vec<VerificationMethod> = vec![];
385
386 for (method_current_id, (purpose, multikey)) in key_chain.enumerate() {
387 let id = format!("#key-{}", method_current_id + 1);
388
389 match purpose {
390 Purpose::Assertion => assertion_method.push(didcore::VerificationMethodType::Reference(id.clone())),
391 Purpose::Encryption => key_agreement.push(didcore::VerificationMethodType::Reference(id.clone())),
392 Purpose::Verification => authentication.push(didcore::VerificationMethodType::Reference(id.clone())),
393 Purpose::CapabilityDelegation => capability_delegation.push(didcore::VerificationMethodType::Reference(id.clone())),
394 Purpose::CapabilityInvocation => capability_invocation.push(didcore::VerificationMethodType::Reference(id.clone())),
395 Purpose::Service => unreachable!(),
396 }
397
398 let method = VerificationMethod {
399 id,
400 key_type: String::from(match self.key_format {
401 PublicKeyFormat::Multikey => "Multikey",
402 PublicKeyFormat::Jwk => "JsonWebKey2020",
403 }),
404 controller: did.to_string(),
405 public_key: Some(match self.key_format {
406 PublicKeyFormat::Multikey => KeyFormat::Multibase(multikey.to_string()),
407 PublicKeyFormat::Jwk => {
408 let (alg, key) = decode_multikey(multikey).map_err(|_| DIDPeerMethodError::MalformedPeerDID)?;
409 KeyFormat::Jwk(alg.build_jwk(&key).map_err(|_| DIDResolutionError::InternalError)?)
410 }
411 }),
412 ..Default::default()
413 };
414
415 methods.push(method);
416 }
417
418 let service_chain = chain
421 .iter()
422 .filter_map(|(purpose, multikey)| (purpose == &Purpose::Service).then_some(multikey));
423
424 let mut services: Vec<Service> = vec![];
425 let mut service_next_id = 0;
426
427 for encoded_service in service_chain {
428 let decoded_service_bytes = Base64Url.decode(encoded_service).map_err(|_| DIDPeerMethodError::DIDParseError)?;
429 let decoded_service = String::from_utf8(decoded_service_bytes).map_err(|_| DIDPeerMethodError::DIDParseError)?;
430
431 let mut service = util::reverse_abbreviate_service_for_did_peer_2(&decoded_service)?;
433
434 if service.id.is_empty() {
435 service.id = if service_next_id == 0 {
436 String::from("#service")
437 } else {
438 format!("#service-{service_next_id}")
439 };
440 service_next_id += 1;
441 }
442
443 services.push(service);
444 }
445
446 let diddoc = DIDDocument {
449 context,
450 id: did.to_string(),
451 controller: None,
452 also_known_as: Some(vec![alias]),
453 verification_method: Some(methods),
454 authentication: (!authentication.is_empty()).then_some(authentication),
455 assertion_method: (!assertion_method.is_empty()).then_some(assertion_method),
456 capability_delegation: (!capability_delegation.is_empty()).then_some(capability_delegation),
457 capability_invocation: (!capability_invocation.is_empty()).then_some(capability_invocation),
458 key_agreement: (!key_agreement.is_empty()).then_some(key_agreement),
459 service: (!services.is_empty()).then_some(services),
460 additional_properties: None,
461 proof: None,
462 };
463
464 Ok(diddoc)
467 }
468
469 pub fn expand_did_peer_4(&self, did: &str) -> Result<DIDDocument, DIDPeerMethodError> {
473 let alias = Self::shorten_did_peer_4(did)?;
476
477 let encoded_document = did.split(':').nth(3).ok_or(DIDPeerMethodError::DIDParseError)?;
479
480 let (base, decoded_bytes) = multibase::decode(encoded_document).map_err(|_| DIDPeerMethodError::DIDParseError)?;
482
483 if Base58Btc != base || decoded_bytes.len() < 2 || decoded_bytes[..2] != MULTICODEC_JSON {
484 return Err(DIDPeerMethodError::MalformedLongPeerDID);
485 }
486 let mut diddoc: DIDDocument = serde_json::from_slice(&decoded_bytes[2..]).map_err(DIDPeerMethodError::SerdeError)?;
488
489 diddoc.id = did.to_string();
491
492 if diddoc.also_known_as.is_none() {
493 diddoc.also_known_as = Some(vec![alias]);
494 }
495
496 diddoc.verification_method = diddoc.verification_method.map(|arr| {
497 arr.into_iter()
498 .map(|vm| VerificationMethod {
499 controller: if !vm.controller.is_empty() { vm.controller } else { did.to_string() },
500 ..vm
501 })
502 .collect()
503 });
504
505 Ok(diddoc)
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use serde_json::Value;
513
514 use super::*;
515 use crate::jwk::Jwk;
516
517 #[test]
518 fn test_did_peer_0_generation_from_given_jwk() {
519 let jwk: Jwk = serde_json::from_str(
520 r#"{
521 "kty": "OKP",
522 "crv": "Ed25519",
523 "x": "O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik"
524 }"#,
525 )
526 .unwrap();
527 let keypair: Ed25519KeyPair = jwk.try_into().unwrap();
528
529 let did = DidPeer::create_did_peer_0_from_ed25519_keypair(&keypair);
530 assert_eq!(did.unwrap(), "did:peer:0z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp");
531 }
532
533 #[test]
534 fn test_did_peer_0_generation_from_given_raw_public_key_bytes() {
535 let entries = [
536 (
537 Algorithm::Ed25519,
538 hex::decode("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29").unwrap(),
539 "did:peer:0z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
540 ),
541 (
542 Algorithm::X25519,
543 hex::decode("2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74").unwrap(),
544 "did:peer:0z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
545 ),
546 ];
547
548 for entry in entries {
549 let (alg, bytes, expected) = entry;
550 let did = DidPeer::create_did_peer_0_from_raw_public_key(alg, &bytes);
551 assert_eq!(did.unwrap(), expected);
552 }
553 }
554
555 #[test]
556 fn test_did_peer_1_generation_from_did_document() {
557 let diddoc = _stored_variant_v0();
558 let did = DidPeer::create_did_peer_1_from_stored_variant(&diddoc);
559 assert_eq!(did.unwrap(), "did:peer:1zQmbEB1EqP7PnNVaHiSpXhkatAA6kNyQK9mWkvrMx2eckgq");
560 }
561
562 #[test]
563 fn test_did_peer_1_generation_fails_from_did_document_with_id() {
564 let diddoc = _invalid_stored_variant_v0();
565 let did = DidPeer::create_did_peer_1_from_stored_variant(&diddoc);
566 assert!(matches!(did.unwrap_err(), DIDPeerMethodError::InvalidStoredVariant));
567 }
568
569 #[test]
570 fn test_did_peer_2_generation() {
571 let keys = vec![
572 PurposedKey {
573 purpose: Purpose::Verification,
574 public_key_multibase: String::from("z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"),
575 },
576 PurposedKey {
577 purpose: Purpose::Encryption,
578 public_key_multibase: String::from("z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR"),
579 },
580 ];
581
582 let did = DidPeer::create_did_peer_2(&keys, &[]).unwrap();
583 assert_eq!(
584 &did,
585 "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR"
586 );
587 }
588
589 #[test]
590 fn test_did_peer_2_generation_with_service() {
591 let keys = vec![PurposedKey {
592 purpose: Purpose::Verification,
593 public_key_multibase: String::from("z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"),
594 }];
595
596 let services = vec![Service {
597 id: String::from("#didcomm"),
598 service_type: String::from("DIDCommMessaging"),
599 service_endpoint: Value::String(String::from("http://example.com/didcomm")),
600 additional_properties: None,
601 }];
602
603 assert_eq!(
604 &DidPeer::create_did_peer_2(&keys, &services).unwrap(),
605 concat!(
606 "did:peer:2",
607 ".Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc",
608 ".SeyJpZCI6IiNkaWRjb21tIiwicyI6Imh0dHA6Ly9leGFtcGxlLmNvbS9kaWRjb21tIiwidCI6ImRtIn0"
609 )
610 );
611 }
612
613 #[test]
614 fn test_did_peer_2_generation_with_services() {
615 let keys = vec![PurposedKey {
616 purpose: Purpose::Verification,
617 public_key_multibase: String::from("z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"),
618 }];
619
620 let services = vec![
621 Service {
622 id: String::from("#didcomm-1"),
623 service_type: String::from("DIDCommMessaging"),
624 service_endpoint: Value::String(String::from("http://example.com/didcomm-1")),
625 additional_properties: None,
626 },
627 Service {
628 id: String::from("#didcomm-2"),
629 service_type: String::from("DIDCommMessaging"),
630 service_endpoint: Value::String(String::from("http://example.com/didcomm-2")),
631 additional_properties: None,
632 },
633 ];
634
635 assert_eq!(
636 &DidPeer::create_did_peer_2(&keys, &services).unwrap(),
637 concat!(
638 "did:peer:2",
639 ".Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc",
640 ".SeyJpZCI6IiNkaWRjb21tLTEiLCJzIjoiaHR0cDovL2V4YW1wbGUuY29tL2RpZGNvbW0tMSIsInQiOiJkbSJ9",
641 ".SeyJpZCI6IiNkaWRjb21tLTIiLCJzIjoiaHR0cDovL2V4YW1wbGUuY29tL2RpZGNvbW0tMiIsInQiOiJkbSJ9"
642 )
643 );
644 }
645
646 #[test]
647 fn test_did_peer_2_generation_should_err_on_key_associated_with_service_purpose() {
648 let keys = vec![PurposedKey {
649 purpose: Purpose::Service,
650 public_key_multibase: String::from("z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"),
651 }];
652
653 assert!(matches!(
654 DidPeer::create_did_peer_2(&keys, &[]).unwrap_err(),
655 DIDPeerMethodError::UnexpectedPurpose
656 ));
657 }
658
659 #[test]
660 fn test_did_peer_2_generation_should_err_on_empty_key_and_service_args() {
661 assert!(matches!(
662 DidPeer::create_did_peer_2(&[], &[]).unwrap_err(),
663 DIDPeerMethodError::EmptyArguments
664 ));
665 }
666
667 #[test]
668 fn test_did_peer_3_generation() {
669 let did = concat!(
670 "did:peer:2.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc.Vz6MkqRYqQi",
671 "SgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V.Vz6MkgoLTnTypo3tDRwCkZXSccTPHRLhF",
672 "4ZnjhueYAFpEX6vg.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2",
673 "ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaW",
674 "Rjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0",
675 );
676
677 assert_eq!(
678 &DidPeer::create_did_peer_3(did).unwrap(),
679 "did:peer:3zQmS19jtYDvGtKVrJhQnRFpBQAx3pJ9omx2HpNrcXFuRCz9"
680 );
681 }
682
683 #[test]
684 fn test_did_peer_3_generation_fails_on_non_did_peer_2_arg() {
685 let dids = [
686 "",
687 "did:peer:0z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
688 "did:peer:1zQmbEB1EqP7PnNVaHiSpXhkatAA6kNyQK9mWkvrMx2eckgq",
689 ];
690
691 for did in dids {
692 assert!(matches!(
693 DidPeer::create_did_peer_3(did).unwrap_err(),
694 DIDPeerMethodError::IllegalArgument
695 ));
696 }
697 }
698
699 #[test]
700 fn test_did_peer_4_generation() {
701 let diddoc = _stored_variant_v0();
702 assert_eq!(
703 &DidPeer::create_did_peer_4_from_stored_variant(&diddoc).unwrap(),
704 concat!(
705 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:z2yS424R5nAoSu",
706 "CezPTvBHybrvByZRD9g8L4oMe4ctq9UwPksVskxJFiars33RRyKz3z7RbwwQRAo9ByoXmBhg",
707 "7UCMkvmSHBeXWF44tQJfLjiXieCtXgxASzPJ5UsgPLAWX2vdjNFfmiLVh1WLe3RdBPvQoMuM",
708 "EiPLFGiKhbzX66dT21qDwZusRC4uDzQa7XpsLBS7rBjZZ9sLMRzjpG4rYpjgLUmUF2D1ixeW",
709 "ZFMqy7fVfPUUGyt4N6R4aLAjMLgcJzAQKb1uFiBYe2ZCTmsjtazWkHypgJetLysv7AwasYDV",
710 "4MMNPY5AbM4p3TGtdpJZaxaXzSKRZexuQ4tWsfGuHXEDiaABj5YtjbNjWh4f5M4sn7D9AAAS",
711 "StG593VkLFaPxG4VnFR4tKPiWeN9AJXRWPQ2XRnsD7U3mCHpRSb2f1HT5KeSHTU8zNAn6vFc",
712 "4fstgf2j71Uo8tngcUBkxdqkHKmpvZ1Fs27sWh7JvWAeiehsW3aBe4CbU4WGjzmusaKVb2HS",
713 "7iY5hbYngYrpwcZ5Sse",
714 )
715 );
716 }
717
718 #[test]
719 fn test_did_peer_4_generation_fails_from_did_document_with_id() {
720 let diddoc = _invalid_stored_variant_v0();
721 let did = DidPeer::create_did_peer_4_from_stored_variant(&diddoc);
722 assert!(matches!(did.unwrap_err(), DIDPeerMethodError::InvalidStoredVariant));
723 }
724
725 #[test]
726 fn test_did_peer_4_shortening() {
727 let did = concat!(
728 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:z2yS424R5nAoSu",
729 "CezPTvBHybrvByZRD9g8L4oMe4ctq9UwPksVskxJFiars33RRyKz3z7RbwwQRAo9ByoXmBhg",
730 "7UCMkvmSHBeXWF44tQJfLjiXieCtXgxASzPJ5UsgPLAWX2vdjNFfmiLVh1WLe3RdBPvQoMuM",
731 "EiPLFGiKhbzX66dT21qDwZusRC4uDzQa7XpsLBS7rBjZZ9sLMRzjpG4rYpjgLUmUF2D1ixeW",
732 "ZFMqy7fVfPUUGyt4N6R4aLAjMLgcJzAQKb1uFiBYe2ZCTmsjtazWkHypgJetLysv7AwasYDV",
733 "4MMNPY5AbM4p3TGtdpJZaxaXzSKRZexuQ4tWsfGuHXEDiaABj5YtjbNjWh4f5M4sn7D9AAAS",
734 "StG593VkLFaPxG4VnFR4tKPiWeN9AJXRWPQ2XRnsD7U3mCHpRSb2f1HT5KeSHTU8zNAn6vFc",
735 "4fstgf2j71Uo8tngcUBkxdqkHKmpvZ1Fs27sWh7JvWAeiehsW3aBe4CbU4WGjzmusaKVb2HS",
736 "7iY5hbYngYrpwcZ5Sse",
737 );
738
739 assert_eq!(
740 &DidPeer::shorten_did_peer_4(did).unwrap(),
741 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ"
742 );
743 }
744
745 #[test]
746 fn test_did_peer_4_shortening_fails_on_non_did_peer_4_arg() {
747 let dids = [
748 "",
749 "did:peer:0z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
750 "did:peer:1zQmbEB1EqP7PnNVaHiSpXhkatAA6kNyQK9mWkvrMx2eckgq",
751 "did:peer:3zQmS19jtYDvGtKVrJhQnRFpBQAx3pJ9omx2HpNrcXFuRCz9",
752 ];
753
754 for did in dids {
755 assert!(matches!(
756 DidPeer::shorten_did_peer_4(did).unwrap_err(),
757 DIDPeerMethodError::IllegalArgument
758 ));
759 }
760 }
761
762 #[test]
763 fn test_did_peer_4_shortening_fails_on_malformed_long_peer_did() {
764 let dids = [
765 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZz2yS424R5nAoSu",
766 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:z2yS424:R5nAoSu",
767 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:",
768 ];
769
770 for did in dids {
771 assert!(matches!(
772 DidPeer::shorten_did_peer_4(did).unwrap_err(),
773 DIDPeerMethodError::MalformedLongPeerDID
774 ));
775 }
776 }
777
778 #[test]
779 fn test_did_peer_4_shortening_fails_on_invalid_hash_in_long_peer_did() {
780 let valid_did = concat!(
781 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:z2yS424R5nAoSu",
782 "CezPTvBHybrvByZRD9g8L4oMe4ctq9UwPksVskxJFiars33RRyKz3z7RbwwQRAo9ByoXmBhg",
783 "7UCMkvmSHBeXWF44tQJfLjiXieCtXgxASzPJ5UsgPLAWX2vdjNFfmiLVh1WLe3RdBPvQoMuM",
784 "EiPLFGiKhbzX66dT21qDwZusRC4uDzQa7XpsLBS7rBjZZ9sLMRzjpG4rYpjgLUmUF2D1ixeW",
785 "ZFMqy7fVfPUUGyt4N6R4aLAjMLgcJzAQKb1uFiBYe2ZCTmsjtazWkHypgJetLysv7AwasYDV",
786 "4MMNPY5AbM4p3TGtdpJZaxaXzSKRZexuQ4tWsfGuHXEDiaABj5YtjbNjWh4f5M4sn7D9AAAS",
787 "StG593VkLFaPxG4VnFR4tKPiWeN9AJXRWPQ2XRnsD7U3mCHpRSb2f1HT5KeSHTU8zNAn6vFc",
788 "4fstgf2j71Uo8tngcUBkxdqkHKmpvZ1Fs27sWh7JvWAeiehsW3aBe4CbU4WGjzmusaKVb2HS",
789 "7iY5hbYngYrpwcZ5Sse",
790 );
791
792 let mut did = valid_did.to_string();
794 did.insert_str(20, "blurg");
795
796 assert!(matches!(DidPeer::shorten_did_peer_4(&did).unwrap_err(), DIDPeerMethodError::InvalidHash));
797
798 let did = format!("{valid_did}blurg");
800
801 assert!(matches!(DidPeer::shorten_did_peer_4(&did).unwrap_err(), DIDPeerMethodError::InvalidHash));
802 }
803
804 #[test]
805 fn test_expand_fails_on_non_did_peer() {
806 let did_method = DidPeer::default();
807
808 let did = "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F";
809 assert!(matches!(did_method.expand(did).unwrap_err(), DIDResolutionError::InvalidDid));
810 }
811
812 #[test]
813 fn test_expand_fails_on_unsupported_did_peer() {
814 let did_method = DidPeer::default();
815
816 let did = "did:peer:1zQmbEB1EqP7PnNVaHiSpXhkatAA6kNyQK9mWkvrMx2eckgq";
817 assert!(matches!(
818 did_method.expand(did).unwrap_err(),
819 DIDResolutionError::MethodNotSupported
820 ));
821 }
822
823 #[test]
824 fn test_expand_did_peer_0_v1() {
825 let did_method = DidPeer::default();
826
827 let did = "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
828 let expected: Value = serde_json::from_str(
829 r##"{
830 "@context": [
831 "https://www.w3.org/ns/did/v1",
832 "https://w3id.org/security/multikey/v1"
833 ],
834 "id": "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
835 "verificationMethod": [
836 {
837 "id": "#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
838 "type": "Multikey",
839 "controller": "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
840 "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
841 },
842 {
843 "id": "#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
844 "type": "Multikey",
845 "controller": "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
846 "publicKeyMultibase": "z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p"
847 }
848 ],
849 "authentication": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
850 "assertionMethod": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
851 "capabilityDelegation": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
852 "capabilityInvocation": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
853 "keyAgreement": ["#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p"]
854 }"##,
855 )
856 .unwrap();
857
858 let diddoc = did_method.expand(did).unwrap();
859
860 assert_eq!(
861 json_canon::to_string(&diddoc).unwrap(), json_canon::to_string(&expected).unwrap(), );
864 }
865
866 #[test]
867 fn test_expand_did_peer_0_v2() {
868 let did_method = DidPeer::default();
869
870 let did = "did:peer:0z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F";
871 let expected: Value = serde_json::from_str(
872 r##"{
873 "@context": [
874 "https://www.w3.org/ns/did/v1",
875 "https://w3id.org/security/multikey/v1"
876 ],
877 "id": "did:peer:0z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
878 "verificationMethod": [
879 {
880 "id": "#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
881 "type": "Multikey",
882 "controller": "did:peer:0z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
883 "publicKeyMultibase": "z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"
884 }
885 ],
886 "authentication": ["#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"],
887 "assertionMethod": ["#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"],
888 "capabilityDelegation": ["#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"],
889 "capabilityInvocation": ["#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"]
890 }"##,
891 )
892 .unwrap();
893
894 let diddoc = did_method.expand(did).unwrap();
895
896 assert_eq!(
897 json_canon::to_string(&diddoc).unwrap(), json_canon::to_string(&expected).unwrap(), );
900 }
901
902 #[test]
903 fn test_expand_did_peer_0_jwk_format() {
904 let did_method = DidPeer {
905 key_format: PublicKeyFormat::Jwk,
906 };
907
908 let did = "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
909 let expected: Value = serde_json::from_str(
910 r##"{
911 "@context": [
912 "https://www.w3.org/ns/did/v1",
913 "https://w3id.org/security/suites/jws-2020/v1"
914 ],
915 "id": "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
916 "verificationMethod": [
917 {
918 "id": "#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
919 "type": "JsonWebKey2020",
920 "controller": "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
921 "publicKeyJwk": {
922 "kty": "OKP",
923 "crv": "Ed25519",
924 "x": "Lm_M42cB3HkUiODQsXRcweM6TByfzEHGO9ND274JcOY"
925 }
926 },
927 {
928 "id": "#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p",
929 "type": "JsonWebKey2020",
930 "controller": "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
931 "publicKeyJwk": {
932 "kty": "OKP",
933 "crv": "X25519",
934 "x": "bl_3kgKpz9jgsg350CNuHa_kQL3B60Gi-98WmdQW2h8"
935 }
936 }
937 ],
938 "authentication": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
939 "assertionMethod": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
940 "capabilityDelegation": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
941 "capabilityInvocation": ["#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"],
942 "keyAgreement": ["#z6LSj72tK8brWgZja8NLRwPigth2T9QRiG1uH9oKZuKjdh9p"]
943 }"##,
944 )
945 .unwrap();
946
947 let diddoc = did_method.expand(did).unwrap();
948
949 assert_eq!(
950 json_canon::to_string(&diddoc).unwrap(), json_canon::to_string(&expected).unwrap(), );
953 }
954
955 #[test]
956 fn test_expand_did_peer_0_fails_for_regex_mismatch() {
957 let did_method = DidPeer::default();
958
959 let dids = [
960 "did:peer:0Z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
962 "did:peer:0z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDoo###",
964 ];
965
966 for did in dids {
967 assert!(matches!(did_method.expand(did).unwrap_err(), DIDResolutionError::InvalidDid));
968 }
969 }
970
971 #[test]
972 fn test_expand_did_peer_0_fails_on_malformed_dids() {
973 let did_method = DidPeer::default();
974
975 let dids = ["did:peer:0z6", "did:peer:0z7MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWpd"];
976
977 for did in dids {
978 assert!(matches!(did_method.expand(did).unwrap_err(), DIDResolutionError::InvalidDid));
979 }
980 }
981
982 #[test]
983 fn test_expand_did_peer_0_fails_on_too_long_did() {
984 let did_method = DidPeer::default();
985 let did = "did:peer:0zQebt6zPwbE4Vw5GFAjjARHrNXFALofERVv4q6Z4db8cnDRQm";
986 assert!(matches!(
987 did_method.expand(did).unwrap_err(),
988 DIDResolutionError::InvalidPublicKeyLength
989 ));
990 }
991
992 #[test]
993 fn test_expand_did_peer_2() {
994 let did_method = DidPeer::default();
995
996 let did = concat!(
997 "did:peer:2",
998 ".Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc",
999 ".Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR",
1000 ".SeyJpZCI6IiNkaWRjb21tIiwicyI6Imh0dHA6Ly9leGFtcGxlLmNvbS8xMjMiLCJ0IjoiZG0ifQ",
1001 ".SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0",
1002 ".SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20veHl6IiwidCI6ImRtIn0",
1003 );
1004
1005 let expected: Value = serde_json::from_str(
1006 r##"{
1007 "@context": [
1008 "https://www.w3.org/ns/did/v1",
1009 "https://w3id.org/security/multikey/v1"
1010 ],
1011 "id": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJpZCI6IiNkaWRjb21tIiwicyI6Imh0dHA6Ly9leGFtcGxlLmNvbS8xMjMiLCJ0IjoiZG0ifQ.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20veHl6IiwidCI6ImRtIn0",
1012 "alsoKnownAs": ["did:peer:3zQmR9j6bEaydJuXDfzYaW4f3EEPQFxz2Zy1iPZuchgeF63h"],
1013 "verificationMethod": [
1014 {
1015 "id": "#key-1",
1016 "type": "Multikey",
1017 "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJpZCI6IiNkaWRjb21tIiwicyI6Imh0dHA6Ly9leGFtcGxlLmNvbS8xMjMiLCJ0IjoiZG0ifQ.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20veHl6IiwidCI6ImRtIn0",
1018 "publicKeyMultibase": "z6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc"
1019 },
1020 {
1021 "id": "#key-2",
1022 "type": "Multikey",
1023 "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJpZCI6IiNkaWRjb21tIiwicyI6Imh0dHA6Ly9leGFtcGxlLmNvbS8xMjMiLCJ0IjoiZG0ifQ.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20veHl6IiwidCI6ImRtIn0",
1024 "publicKeyMultibase": "z6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR"
1025 }
1026 ],
1027 "authentication": ["#key-1"],
1028 "keyAgreement": ["#key-2"],
1029 "service": [
1030 {
1031 "id": "#didcomm",
1032 "type": "DIDCommMessaging",
1033 "serviceEndpoint": "http://example.com/123"
1034 },
1035 {
1036 "id": "#service",
1037 "type": "DIDCommMessaging",
1038 "serviceEndpoint": "http://example.com/abc"
1039 },
1040 {
1041 "id": "#service-1",
1042 "type": "DIDCommMessaging",
1043 "serviceEndpoint": "http://example.com/xyz"
1044 }
1045 ]
1046 }"##,
1047 )
1048 .unwrap();
1049
1050 let diddoc = did_method.expand(did).unwrap();
1051 assert_eq!(
1052 json_canon::to_string(&diddoc).unwrap(), json_canon::to_string(&expected).unwrap(), );
1055 }
1056
1057 #[test]
1058 fn test_expand_did_peer_2_jwk_format() {
1059 let did_method = DidPeer {
1060 key_format: PublicKeyFormat::Jwk,
1061 };
1062
1063 let did = concat!(
1064 "did:peer:2",
1065 ".Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc",
1066 ".Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR",
1067 ".SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0",
1068 );
1069
1070 let expected: Value = serde_json::from_str(
1071 r##"{
1072 "@context": [
1073 "https://www.w3.org/ns/did/v1",
1074 "https://w3id.org/security/suites/jws-2020/v1"
1075 ],
1076 "id": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0",
1077 "alsoKnownAs": ["did:peer:3zQmWdmF5Lgads1v6qeV9x6PJEWrfUaKQ5D8tf7up9a5xwDi"],
1078 "verificationMethod": [
1079 {
1080 "id": "#key-1",
1081 "type": "JsonWebKey2020",
1082 "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0",
1083 "publicKeyJwk": {
1084 "kty": "OKP",
1085 "crv": "Ed25519",
1086 "x": "RCzl6iYBsyD4aK8Yzmo8r_6eBriu0XmnDj64xOQ3d6M"
1087 }
1088 },
1089 {
1090 "id": "#key-2",
1091 "type": "JsonWebKey2020",
1092 "controller": "did:peer:2.Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc.Ez6LSg8zQom395jKLrGiBNruB9MM6V8PWuf2FpEy4uRFiqQBR.SeyJpZCI6IiIsInMiOiJodHRwOi8vZXhhbXBsZS5jb20vYWJjIiwidCI6ImRtIn0",
1093 "publicKeyJwk": {
1094 "kty": "OKP",
1095 "crv": "X25519",
1096 "x": "Qk1FMFvAv5Ihlgjm_SJIqNRU3kqhb_RWQZrPUh3mNWg"
1097 }
1098 }
1099 ],
1100 "authentication": ["#key-1"],
1101 "keyAgreement": ["#key-2"],
1102 "service": [{
1103 "id": "#service",
1104 "type": "DIDCommMessaging",
1105 "serviceEndpoint": "http://example.com/abc"
1106 }]
1107 }"##,
1108 )
1109 .unwrap();
1110
1111 let diddoc = did_method.expand(did).unwrap();
1112 assert_eq!(
1113 json_canon::to_string(&diddoc).unwrap(), json_canon::to_string(&expected).unwrap(), );
1116 }
1117
1118 #[test]
1119 fn test_expand_did_peer_2_fails_on_malformed_encoded_service() {
1120 let did_method = DidPeer::default();
1121 let did = concat!(
1122 "did:peer:2",
1123 ".Vz6Mkj3PUd1WjvaDhNZhhhXQdz5UnZXmS7ehtx8bsPpD47kKc",
1124 ".SeyJzIjoiaHR0cDovL2V4YW1wbGUuY29tL3h5eiIsInQiOiJkbSI",
1126 );
1127
1128 assert!(matches!(did_method.expand(did).unwrap_err(), DIDResolutionError::InvalidDid));
1129 }
1130
1131 #[test]
1132 fn test_expand_did_peer_4() {
1133 let did_method = DidPeer::new();
1134
1135 let did = concat!(
1136 "did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ:z2yS424R5nAoSu",
1137 "CezPTvBHybrvByZRD9g8L4oMe4ctq9UwPksVskxJFiars33RRyKz3z7RbwwQRAo9ByoXmBhg",
1138 "7UCMkvmSHBeXWF44tQJfLjiXieCtXgxASzPJ5UsgPLAWX2vdjNFfmiLVh1WLe3RdBPvQoMuM",
1139 "EiPLFGiKhbzX66dT21qDwZusRC4uDzQa7XpsLBS7rBjZZ9sLMRzjpG4rYpjgLUmUF2D1ixeW",
1140 "ZFMqy7fVfPUUGyt4N6R4aLAjMLgcJzAQKb1uFiBYe2ZCTmsjtazWkHypgJetLysv7AwasYDV",
1141 "4MMNPY5AbM4p3TGtdpJZaxaXzSKRZexuQ4tWsfGuHXEDiaABj5YtjbNjWh4f5M4sn7D9AAAS",
1142 "StG593VkLFaPxG4VnFR4tKPiWeN9AJXRWPQ2XRnsD7U3mCHpRSb2f1HT5KeSHTU8zNAn6vFc",
1143 "4fstgf2j71Uo8tngcUBkxdqkHKmpvZ1Fs27sWh7JvWAeiehsW3aBe4CbU4WGjzmusaKVb2HS",
1144 "7iY5hbYngYrpwcZ5Sse",
1145 );
1146
1147 let expected = DIDDocument {
1148 id: did.to_string(),
1149 also_known_as: Some(vec![String::from("did:peer:4zQmePYVawceZsPSxpLRp54z4Q5DCZXeyyGKwoDMc2NqgZXZ")]),
1150 .._stored_variant_v0()
1151 };
1152
1153 let diddoc = did_method.expand(did).unwrap();
1154 assert_eq!(
1155 json_canon::to_string(&diddoc).unwrap(), json_canon::to_string(&expected).unwrap(), );
1158 }
1159
1160 fn _stored_variant_v0() -> DIDDocument {
1161 serde_json::from_str(
1162 r##"{
1163 "@context": [
1164 "https://www.w3.org/ns/did/v1",
1165 "https://w3id.org/security/suites/ed25519-2020/v1"
1166 ],
1167 "verificationMethod": [{
1168 "id": "#key1",
1169 "type": "Ed25519VerificationKey2020",
1170 "controller": "#id",
1171 "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
1172 }],
1173 "authentication": ["#key1"],
1174 "assertionMethod": ["#key1"],
1175 "capabilityDelegation": ["#key1"],
1176 "capabilityInvocation": ["#key1"]
1177 }"##,
1178 )
1179 .unwrap()
1180 }
1181
1182 fn _invalid_stored_variant_v0() -> DIDDocument {
1183 serde_json::from_str(
1184 r##"{
1185 "@context": [
1186 "https://www.w3.org/ns/did/v1",
1187 "https://w3id.org/security/suites/ed25519-2020/v1"
1188 ],
1189 "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
1190 "verificationMethod": [{
1191 "id": "#key1",
1192 "type": "Ed25519VerificationKey2020",
1193 "controller": "#id",
1194 "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
1195 }],
1196 "authentication": ["#key1"],
1197 "assertionMethod": ["#key1"],
1198 "capabilityDelegation": ["#key1"],
1199 "capabilityInvocation": ["#key1"]
1200 }"##,
1201 )
1202 .unwrap()
1203 }
1204}