did_peer/
lib.rs

1//! # DID Peer Method
2//!
3//! The `did-peer` method is a DID method that is designed to be used for peer-to-peer communication.
4//! It is based on did:key which can be used for Verification (V) and Encryption (E) purposes.
5//! It also supports services (S) which can be used to define endpoints for communication.
6//!
7//! Example:
8//! ```ignore
9//! let peer = DIDPeer;
10//! match peer.resolve(DID::new::<str>("did:peer:2.Vabc...").unwrap()).await {
11//!    Ok(res) => {
12//!        println!("DID DOcument: {:#?}", res.document.into_document()),
13//!    },
14//!    Err(e) => {
15//!      println!("Error: {:?}", e);
16//!   }
17//! }
18//! ```
19//!
20use affinidi_did_common::{
21    Document,
22    one_or_many::OneOrMany,
23    service::{Endpoint, Service},
24    verification_method::{VerificationMethod, VerificationRelationship},
25};
26use affinidi_did_key::DIDKey;
27use affinidi_secrets_resolver::{
28    jwk::Params,
29    secrets::{KeyType, SecretMaterial},
30};
31use base64::prelude::*;
32use serde::{Deserialize, Serialize};
33use serde_json::{Value, json};
34use service::convert_service;
35use std::{
36    collections::{BTreeMap, HashMap},
37    fmt,
38    str::FromStr,
39};
40use thiserror::Error;
41use url::Url;
42use wasm_bindgen::prelude::*;
43
44pub mod service;
45
46#[derive(Error, Debug)]
47pub enum DIDPeerError {
48    #[error("Unsupported key type")]
49    UnsupportedKeyType,
50    #[error("Unsupported curve: {0}")]
51    UnsupportedCurve(String),
52    #[error("Unsupported source")]
53    UnsupportedSource,
54    #[error("Syntax error on Service definition: {0}")]
55    SyntaxErrorServiceDefinition(String),
56    #[error("Unsupported Method. Must be method 2")]
57    MethodNotSupported,
58    #[error("Key Parsing error {0}")]
59    KeyParsingError(String),
60    #[error("DID Document doesn't contain any verificationMethod items")]
61    MissingVerificationMethods,
62    #[error("JSON Parsing error: {0}")]
63    JsonParsingError(String),
64    #[error("Internal error: {0}")]
65    InternalError(String),
66    #[error("Encoding error: {0}")]
67    EncodingError(String),
68}
69
70// Converts DIDPeerError to JsValue which is required for propagating errors to WASM
71impl From<DIDPeerError> for JsValue {
72    fn from(err: DIDPeerError) -> JsValue {
73        JsValue::from(err.to_string())
74    }
75}
76
77pub struct DIDPeer;
78
79/// DID Peer Service supports two formats:
80/// 1. Short format - uses `a` and `r` for accept and routing keys
81/// 2. Long format - uses `accept` and `routing_keys` for accept and routing keys
82#[derive(Debug, Serialize, Deserialize)]
83#[serde(untagged)]
84pub enum PeerServiceEndPoint {
85    Short(PeerServiceEndPointShort),
86    Long(PeerServiceEndPointLong),
87}
88
89impl PeerServiceEndPoint {
90    pub fn to_short(&self) -> PeerServiceEndPointShort {
91        match self {
92            PeerServiceEndPoint::Short(short) => short.clone(),
93            PeerServiceEndPoint::Long(long) => match long {
94                PeerServiceEndPointLong::URI(uri) => PeerServiceEndPointShort::URI(uri.to_string()),
95                PeerServiceEndPointLong::Map(map) => match map {
96                    OneOrMany::One(single) => {
97                        PeerServiceEndPointShort::Map(OneOrMany::One(PeerServiceEndPointShortMap {
98                            uri: single.uri.to_string(),
99                            a: single.accept.clone(),
100                            r: single.routing_keys.clone(),
101                        }))
102                    }
103                    OneOrMany::Many(many) => PeerServiceEndPointShort::Map(OneOrMany::Many(
104                        many.iter()
105                            .map(|m| PeerServiceEndPointShortMap {
106                                uri: m.uri.to_string(),
107                                a: m.accept.clone(),
108                                r: m.routing_keys.clone(),
109                            })
110                            .collect(),
111                    )),
112                },
113            },
114        }
115    }
116
117    pub fn to_long(&self) -> PeerServiceEndPointLong {
118        match self {
119            PeerServiceEndPoint::Short(short) => PeerServiceEndPointLong::from(short.clone()),
120            PeerServiceEndPoint::Long(long) => long.clone(),
121        }
122    }
123}
124
125/// DID serviceEndPoint structure in short format
126#[derive(Debug, Clone, Serialize, Deserialize)]
127#[serde(untagged)]
128pub enum PeerServiceEndPointShort {
129    URI(String),
130    Map(OneOrMany<PeerServiceEndPointShortMap>),
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct PeerServiceEndPointShortMap {
135    pub uri: String,
136    pub a: Vec<String>,
137    pub r: Vec<String>,
138}
139
140/// DID serviceEndPoint structure in long format
141#[derive(Debug, Clone, Serialize, Deserialize)]
142#[serde(untagged)]
143pub enum PeerServiceEndPointLong {
144    URI(String),
145    Map(OneOrMany<PeerServiceEndPointLongMap>),
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct PeerServiceEndPointLongMap {
150    pub uri: String,
151    pub accept: Vec<String>,
152    pub routing_keys: Vec<String>,
153}
154
155impl From<PeerServiceEndPointShort> for PeerServiceEndPointLong {
156    fn from(service: PeerServiceEndPointShort) -> Self {
157        match service {
158            PeerServiceEndPointShort::URI(uri) => PeerServiceEndPointLong::URI(uri),
159            PeerServiceEndPointShort::Map(map) => match map {
160                OneOrMany::One(single) => {
161                    PeerServiceEndPointLong::Map(OneOrMany::One(PeerServiceEndPointLongMap {
162                        uri: single.uri,
163                        accept: single.a,
164                        routing_keys: single.r,
165                    }))
166                }
167                OneOrMany::Many(many) => PeerServiceEndPointLong::Map(OneOrMany::Many(
168                    many.iter()
169                        .map(|m| PeerServiceEndPointLongMap {
170                            uri: m.uri.clone(),
171                            accept: m.a.clone(),
172                            routing_keys: m.r.clone(),
173                        })
174                        .collect(),
175                )),
176            },
177        }
178    }
179}
180
181/// DID Service structure in abbreviated format
182#[derive(Debug, Serialize, Deserialize)]
183pub struct DIDPeerService {
184    #[serde(rename = "t")]
185    #[serde(alias = "t")]
186    pub _type: String,
187    #[serde(rename = "s")]
188    #[serde(alias = "s")]
189    pub service_end_point: PeerServiceEndPoint, // serviceEndPoint
190
191    /// id is optional, if not provided, it will be set to #service, #service-1, #service-2, etc
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub id: Option<String>,
194}
195
196impl DIDPeerService {
197    fn convert(did: &str, service: DIDPeerService) -> Result<Service, DIDPeerError> {
198        let service_endpoint = match serde_json::to_value(PeerServiceEndPoint::to_long(
199            &service.service_end_point,
200        )) {
201            Ok(value) => {
202                if value.is_string() {
203                    if let Some(value) = value.as_str() {
204                        if let Ok(url) = Url::from_str(value) {
205                            Endpoint::Url(url)
206                        } else {
207                            return Err(DIDPeerError::SyntaxErrorServiceDefinition(format!(
208                                "Couldn't convert ServiceEndPoint to a valid URI: {value}",
209                            )));
210                        }
211                    } else {
212                        return Err(DIDPeerError::SyntaxErrorServiceDefinition(format!(
213                            "Service URI appears to be a string, but can't parse as a string: {value}",
214                        )));
215                    }
216                } else {
217                    Endpoint::Map(value)
218                }
219            }
220            Err(err) => {
221                return Err(DIDPeerError::SyntaxErrorServiceDefinition(format!(
222                    "Couldn't convert ServiceEndPoint to a valid representation. Reason: {err}",
223                )));
224            }
225        };
226
227        let id = if let Some(id) = service.id {
228            [did, &id].concat()
229        } else {
230            [did, "#service"].concat()
231        };
232
233        let id = match Url::from_str(&id) {
234            Ok(uri) => uri,
235            Err(e) => {
236                return Err(DIDPeerError::SyntaxErrorServiceDefinition(format!(
237                    "Error parsing service id: {id}. Reason: {e:?}",
238                )));
239            }
240        };
241
242        Ok(Service {
243            id: Some(id),
244            type_: vec!["DIDCommMessaging".to_string()],
245            service_endpoint,
246            property_set: HashMap::new(),
247        })
248    }
249}
250
251#[derive(Clone)]
252#[wasm_bindgen]
253/// DID Peer Key Purpose (used to create a new did:peer: string)
254///   Verification: Keys are referenced in the authentication and assertionMethod fields
255///   Encryption: Keys are referenced in the keyAgreement field
256pub enum DIDPeerKeys {
257    Verification,
258    Encryption,
259}
260
261impl fmt::Display for DIDPeerKeys {
262    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
263        match *self {
264            DIDPeerKeys::Verification => write!(f, "verification"),
265            DIDPeerKeys::Encryption => write!(f, "encryption"),
266        }
267    }
268}
269
270#[derive(Clone)]
271#[wasm_bindgen]
272/// Supported DID Peer Key Types (used to create a new did:peer: string)
273pub enum DIDPeerKeyType {
274    Ed25519,
275    Secp256k1,
276    P256,
277}
278
279impl fmt::Display for DIDPeerKeyType {
280    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281        match *self {
282            DIDPeerKeyType::Ed25519 => write!(f, "ed25519"),
283            DIDPeerKeyType::Secp256k1 => write!(f, "secp256k1"),
284            DIDPeerKeyType::P256 => write!(f, "p256"),
285        }
286    }
287}
288
289#[derive(Clone)]
290#[wasm_bindgen(getter_with_clone)]
291/// Structure to help with creating a new did:peer: string
292///    purpose: ENUM (DIDPeerKeys) - Verification or Encryption
293///    public_key_multibase: String - Optional: Multibase encoded public key (did:key:(.*))
294///                                   If None, then auto-create and return the private key
295pub struct DIDPeerCreateKeys {
296    pub purpose: DIDPeerKeys,
297    pub type_: Option<DIDPeerKeyType>,
298    pub public_key_multibase: Option<String>,
299}
300
301#[wasm_bindgen]
302impl DIDPeerCreateKeys {
303    #[wasm_bindgen(constructor)]
304    pub fn new(
305        purpose: DIDPeerKeys,
306        type_: Option<DIDPeerKeyType>,
307        public_key_multibase: Option<String>,
308    ) -> Self {
309        DIDPeerCreateKeys {
310            purpose,
311            type_,
312            public_key_multibase,
313        }
314    }
315}
316
317/// DIDPeerCreatedKeys, contains information related to any keys that were created
318///
319/// key_multibase: `String`, the multibase_58 encoded key value (e.g. did:key:(.*))
320/// curve: `String`, the elliptic curve method used
321/// d: `String`, private key value in Base64URL_NOPAD
322/// x: `String`, public key value in Base64URL_NOPAD
323/// y: `Option<String>`, Optional: Y coordinate for EC keys in Base64URL_NOPAD
324#[derive(Clone, Debug, Serialize, Deserialize)]
325#[wasm_bindgen(getter_with_clone)]
326pub struct DIDPeerCreatedKeys {
327    pub key_multibase: String,
328    pub curve: String,
329    pub d: String,
330    pub x: String,
331    pub y: Option<String>,
332}
333
334/// Converts a public key into a DID VerificationMethod
335fn process_key(did: &str, kid: &str, public_key: &str) -> VerificationMethod {
336    let mut property_set = HashMap::new();
337
338    property_set.insert(
339        "publicKeyMultibase".to_string(),
340        Value::String(public_key.to_string()),
341    );
342
343    VerificationMethod {
344        id: Url::from_str(kid).unwrap(),
345        type_: "Multikey".to_string(),
346        controller: Url::from_str(did).unwrap(),
347        expires: None,
348        revoked: None,
349        property_set,
350    }
351}
352
353impl DIDPeer {
354    pub async fn resolve(&self, did: &str) -> Result<Document, DIDPeerError> {
355        let Some(method_specific_id) = did.strip_prefix("did:peer:") else {
356            return Err(DIDPeerError::MethodNotSupported);
357        };
358
359        // If did:peer is type 0, then treat it as a did:key
360        if let Some(id) = method_specific_id.strip_prefix('0') {
361            return DIDKey::resolve(&["did:key:", id].concat()).map_err(|e| {
362                DIDPeerError::InternalError(format!(
363                    "Resolving version 0 of did:peer resulted in the following error: {e}"
364                ))
365            });
366        }
367
368        // Only supports method 2 for did:peer
369        if !method_specific_id.starts_with('2') {
370            return Err(DIDPeerError::MethodNotSupported);
371        }
372
373        let mut context = BTreeMap::new();
374        context.insert("@base".to_string(), serde_json::json!(method_specific_id));
375
376        let mut verification_methods: Vec<VerificationMethod> = Vec::new();
377
378        let mut key_agreements: Vec<VerificationRelationship> = Vec::new();
379        let mut key_authentications: Vec<VerificationRelationship> = Vec::new();
380        let mut key_assertion_methods: Vec<VerificationRelationship> = Vec::new();
381        let mut key_capability_delegation: Vec<VerificationRelationship> = Vec::new();
382        let mut key_capability_invocation: Vec<VerificationRelationship> = Vec::new();
383        let mut services: Vec<Service> = Vec::new();
384
385        // Split the DID for peer on '.'s, we skip the first one
386        // did:peer:2.(process from here)
387        let parts: Vec<&str> = method_specific_id[2..].split('.').collect();
388        let mut key_count: u32 = 0;
389        let mut service_idx: u32 = 0;
390
391        for part in parts {
392            key_count += 1;
393            let kid = [did, "#key-", &key_count.to_string()].concat();
394            let ch = part.chars().next();
395            match ch {
396                Some(e) => {
397                    match e {
398                        'A' => {
399                            // Assertion Method
400                            verification_methods.push(process_key(did, &kid, &part[1..]));
401
402                            key_assertion_methods.push(VerificationRelationship::Reference(
403                                Url::from_str(&kid).unwrap(),
404                            ));
405                        }
406                        'D' => {
407                            // Capability Delegation
408                            verification_methods.push(process_key(did, &kid, &part[1..]));
409
410                            key_capability_delegation.push(VerificationRelationship::Reference(
411                                Url::from_str(&kid).unwrap(),
412                            ));
413                        }
414                        'E' => {
415                            // Key Agreement (Encryption)
416                            verification_methods.push(process_key(did, &kid, &part[1..]));
417
418                            key_agreements.push(VerificationRelationship::Reference(
419                                Url::from_str(&kid).unwrap(),
420                            ));
421                        }
422                        'I' => {
423                            // Capability Invocation
424                            verification_methods.push(process_key(did, &kid, &part[1..]));
425
426                            key_capability_invocation.push(VerificationRelationship::Reference(
427                                Url::from_str(&kid).unwrap(),
428                            ));
429                        }
430                        'V' => {
431                            // Authentication (Verification)
432                            verification_methods.push(process_key(did, &kid, &part[1..]));
433
434                            key_authentications.push(VerificationRelationship::Reference(
435                                Url::from_str(&kid).unwrap(),
436                            ));
437
438                            key_assertion_methods.push(VerificationRelationship::Reference(
439                                Url::from_str(&kid).unwrap(),
440                            ));
441                        }
442                        'S' => {
443                            // Service
444                            let service = convert_service(did, part, service_idx)
445                                .map_err(|e| DIDPeerError::InternalError(e.to_string()))?;
446                            services.push(service);
447                            service_idx += 1;
448                        }
449                        other => {
450                            return Err(DIDPeerError::EncodingError(format!(
451                                "An invalid Purpose Code ({other}) was found in the DID",
452                            )));
453                        }
454                    }
455                }
456                None => {
457                    // We shouldn't really get here
458                    // But it is ok if we do, we just skip it
459                }
460            }
461        }
462
463        let mut parameters_set = HashMap::new();
464        parameters_set.insert(
465            "@context".to_string(),
466            json!(["https://www.w3.org/ns/did/v1.1".to_string()]),
467        );
468
469        Ok(Document {
470            id: Url::from_str(["did:peer:", method_specific_id].concat().as_str()).unwrap(),
471            verification_method: verification_methods,
472            assertion_method: key_assertion_methods,
473            authentication: key_authentications,
474            capability_delegation: key_capability_delegation,
475            capability_invocation: key_capability_invocation,
476            key_agreement: key_agreements,
477            service: services,
478            parameters_set,
479        })
480    }
481}
482
483impl DIDPeer {
484    /// Creates a new did:peer DID
485    ///
486    /// This will preserve the order of the keys and services in creating the did:peer string
487    ///
488    /// # Examples
489    /// ```ignore
490    ///
491    /// // Create a did:peer with pre-existing encryption key (Multibase base58-btc e.g: z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK)
492    /// let keys = vec![DIDPeerCreateKeys {
493    ///     type_: Some(DIDPeerKeyType::Ed25519),
494    ///     purpose: DIDPeerKeys::Encryption,
495    ///     public_key_multibase: Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".into()),
496    /// }];
497    /// let did_peer = DIDPeer::create_peer_did(&keys, None).expect("Failed to create did:peer");
498    ///
499    /// // Create a random did:peer with services
500    /// let keys = vec![DIDPeerCreateKeys {
501    ///    type_: Some(DIDPeerKeyType::Secp256k1),
502    ///    purpose: DIDPeerKeys::Encryption,
503    ///    public_key_multibase: Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".into()),
504    ///  }];
505    /// let services: Vec<DIDPeerService> = vec![DIDPeerService {
506    ///    _type: "dm".into(),
507    ///    id: None,
508    ///    service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong {
509    ///        uri: "http://example.com/didcomm".into(),
510    ///        accept: vec!["didcomm/v2".into()],
511    ///        routing_keys: vec!["did:example:123456789abcdefghi#key-1".into()],
512    ///    }),
513    /// }];
514    ///
515    /// let did_peer =
516    ///    DIDPeer::create_peer_did(&keys, Some(&services)).expect("Failed to create did:peer");
517    ///
518    /// // How to create a key prior to creating a did:peer
519    /// let did =
520    ///     DIDKey::generate(&JWK::generate_ed25519().unwrap()).expect("Failed to create did:key");
521    ///
522    /// let keys = vec![DIDPeerCreateKeys {
523    ///     type_: Some(DIDPeerKeyType::Ed25519),
524    ///     purpose: DIDPeerKeys::Verification,
525    ///     public_key_multibase: Some(did[8..].to_string()),
526    /// }];
527    ///
528    /// let did_peer = DIDPeer::create_peer_did(&keys, None).expect("Failed to create did:peer");
529    /// ```
530    pub fn create_peer_did(
531        keys: &Vec<DIDPeerCreateKeys>,
532        services: Option<&Vec<DIDPeerService>>,
533    ) -> Result<(String, Vec<DIDPeerCreatedKeys>), DIDPeerError> {
534        let mut result = String::from("did:peer:2");
535
536        let mut private_keys: Vec<DIDPeerCreatedKeys> = vec![];
537        for key in keys {
538            // Create new keys if not provided
539            let public_key = if let Some(key) = key.public_key_multibase.as_ref() {
540                key.clone()
541            } else {
542                let (did, secret) = match &key.type_ {
543                    Some(type_) => match type_ {
544                        DIDPeerKeyType::Ed25519 => {
545                            DIDKey::generate(KeyType::Ed25519).map_err(|e| {
546                                DIDPeerError::InternalError(format!(
547                                    "Couldn't create Ed25519 did:key reason: {e}"
548                                ))
549                            })?
550                        }
551                        DIDPeerKeyType::Secp256k1 => {
552                            DIDKey::generate(KeyType::Secp256k1).map_err(|e| {
553                                DIDPeerError::InternalError(format!(
554                                    "Couldn't create Secp256k1 did:key reason: {e}"
555                                ))
556                            })?
557                        }
558                        DIDPeerKeyType::P256 => DIDKey::generate(KeyType::P256).map_err(|e| {
559                            DIDPeerError::InternalError(format!(
560                                "Couldn't create P256 did:key reason: {e}"
561                            ))
562                        })?,
563                    },
564                    None => return Err(DIDPeerError::UnsupportedKeyType),
565                };
566
567                if let SecretMaterial::JWK(jwk) = secret.secret_material {
568                    match jwk.params {
569                        Params::OKP(map) => {
570                            let d = if let Some(d) = &map.d {
571                                d
572                            } else {
573                                return Err(DIDPeerError::KeyParsingError(
574                                    "Missing private key".to_string(),
575                                ));
576                            };
577                            private_keys.push(DIDPeerCreatedKeys {
578                                key_multibase: did[8..].to_string(),
579                                curve: map.curve.clone(),
580                                d: d.clone(),
581                                x: map.x.clone(),
582                                y: None,
583                            })
584                        }
585                        Params::EC(map) => {
586                            let d = if let Some(d) = &map.d {
587                                d
588                            } else {
589                                return Err(DIDPeerError::KeyParsingError(
590                                    "Missing private key".to_string(),
591                                ));
592                            };
593
594                            private_keys.push(DIDPeerCreatedKeys {
595                                key_multibase: did[8..].to_string(),
596                                curve: map.curve.clone(),
597                                d: String::from(d),
598                                x: map.x.clone(),
599                                y: Some(map.y.clone()),
600                            })
601                        }
602                    }
603                } else {
604                    return Err(DIDPeerError::InternalError(
605                        "Expected Secret Material to be in JWK format!".to_string(),
606                    ));
607                }
608
609                did[8..].to_string()
610            };
611
612            // Place based on key types
613            match key.purpose {
614                DIDPeerKeys::Verification => {
615                    result.push_str(&format!(".V{public_key}",));
616                }
617                DIDPeerKeys::Encryption => {
618                    result.push_str(&format!(".E{public_key}",));
619                }
620            }
621        }
622
623        if let Some(services) = services {
624            for service in services {
625                let service = serde_json::to_string(&service).map_err(|e| {
626                    DIDPeerError::SyntaxErrorServiceDefinition(format!(
627                        "Error parsing service: {e}",
628                    ))
629                })?;
630                result.push_str(&format!(".S{}", BASE64_URL_SAFE_NO_PAD.encode(service)));
631            }
632        }
633
634        Ok((result, private_keys))
635    }
636
637    /// Expands an existing DID Document from the did:key Multikeys to full JWT keys
638    /// This is useful for when you want to resolve a did:peer DID Document to a full JWT included DID Document
639    /// Converts base58 multi-keys to full JWTs in verificationMethod
640    pub async fn expand_keys(doc: &Document) -> Result<Document, DIDPeerError> {
641        let mut new_doc = doc.clone();
642
643        let mut new_vms: Vec<VerificationMethod> = vec![];
644        for v_method in &doc.verification_method {
645            new_vms.push(Self::_convert_vm(v_method).await?);
646        }
647
648        new_doc.verification_method = new_vms;
649        Ok(new_doc)
650    }
651
652    // Converts
653    async fn _convert_vm(method: &VerificationMethod) -> Result<VerificationMethod, DIDPeerError> {
654        let current_controller = method.controller.clone();
655        let current_id = method.id.clone();
656
657        let did_key = if let Some(key) = method.property_set.get("publicKeyBase58") {
658            ["did:key:", key.as_str().unwrap()].concat()
659        } else if let Some(key) = method.property_set.get("publicKeyMultibase") {
660            ["did:key:", key.as_str().unwrap()].concat()
661        } else {
662            return Err(DIDPeerError::KeyParsingError(
663                "Failed to convert verification_method. Reason: Missing publicKeyBase58"
664                    .to_string(),
665            ));
666        };
667
668        let document = match DIDKey::resolve(&did_key) {
669            Ok(document) => document,
670            Err(e) => {
671                return Err(DIDPeerError::KeyParsingError(format!(
672                    "Failed to resolve key ({did_key}). Reason: {e}",
673                )));
674            }
675        };
676
677        if let Some(vm) = document.verification_method.first() {
678            let mut properties: HashMap<String, Value> = HashMap::new();
679            for (k, v) in vm.property_set.iter() {
680                properties.insert(k.clone(), v.clone());
681            }
682
683            Ok(VerificationMethod {
684                id: current_id,
685                type_: vm.type_.clone(),
686                controller: current_controller,
687                expires: None,
688                revoked: None,
689                property_set: properties,
690            })
691        } else {
692            Err(DIDPeerError::KeyParsingError(
693                "Failed to convert verification_method. Reason: Missing verification_method"
694                    .to_string(),
695            ))
696        }
697    }
698}
699
700#[cfg(test)]
701mod test {
702    use affinidi_did_common::{one_or_many::OneOrMany, verification_method::VerificationMethod};
703    use affinidi_did_key::DIDKey;
704    use affinidi_secrets_resolver::secrets::KeyType;
705
706    use crate::{
707        DIDPeer, DIDPeerCreateKeys, DIDPeerKeyType, DIDPeerKeys, DIDPeerService,
708        PeerServiceEndPoint, PeerServiceEndPointLong, PeerServiceEndPointLongMap,
709    };
710
711    const DID_PEER: &str = "did:peer:2.Vz6MkiToqovww7vYtxm1xNM15u9JzqzUFZ1k7s7MazYJUyAxv.EzQ3shQLqRUza6AMJFbPuMdvFRFWm1wKviQRnQSC1fScovJN4s.SeyJ0IjoiRElEQ29tbU1lc3NhZ2luZyIsInMiOnsidXJpIjoiaHR0cHM6Ly8xMjcuMC4wLjE6NzAzNyIsImEiOlsiZGlkY29tbS92MiJdLCJyIjpbXX19";
712
713    #[should_panic(
714        expected = "Failed to convert verification_method. Reason: Missing publicKeyBase58"
715    )]
716    #[tokio::test]
717    async fn expand_keys_throws_key_parsing_missing_pbk58_error() {
718        let peer = DIDPeer;
719        let output = peer.resolve(DID_PEER).await.unwrap();
720
721        let mut document = output.clone();
722        let mut new_vms: Vec<VerificationMethod> = vec![];
723        for mut vm in document.verification_method {
724            vm.property_set.remove("publicKeyMultibase");
725            new_vms.push(vm);
726        }
727
728        document.verification_method = new_vms;
729        let _expanded_doc = DIDPeer::expand_keys(&document).await.unwrap();
730    }
731
732    #[tokio::test]
733    async fn expand_keys_works() {
734        let peer = DIDPeer;
735        let document = peer.resolve(DID_PEER).await.expect("Couldn't resolve DID");
736
737        let vm_before_expansion = document.verification_method.clone();
738        let expanded_doc = DIDPeer::expand_keys(&document).await.unwrap();
739        let vms_after_expansion = expanded_doc.verification_method;
740
741        for vm in vms_after_expansion.clone() {
742            assert!(vm.id.as_str().starts_with("did:peer"));
743        }
744        assert_eq!(vm_before_expansion.len(), vms_after_expansion.len())
745    }
746
747    #[tokio::test]
748    async fn create_peer_did_without_keys_and_services() {
749        let keys: Vec<DIDPeerCreateKeys> = vec![];
750        let services: Vec<DIDPeerService> = vec![];
751
752        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
753        let parts: Vec<&str> = did.split(":").collect();
754
755        assert_eq!(parts.len(), 3);
756        assert_eq!(parts[1], "peer");
757        assert!(parts[2].len() == 1);
758    }
759
760    #[tokio::test]
761    async fn create_peer_did_without_keys() {
762        let keys: Vec<DIDPeerCreateKeys> = vec![];
763        let services = vec![DIDPeerService {
764            _type: "dm".into(),
765            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
766                OneOrMany::One(PeerServiceEndPointLongMap {
767                    uri: "https://localhost:7037".into(),
768                    accept: vec!["didcomm/v2".into()],
769                    routing_keys: vec![],
770                }),
771            )),
772            id: None,
773        }];
774
775        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
776        let parts: Vec<&str> = did.split(":").collect();
777        let method_ids: Vec<&str> = parts[2].split(".").collect();
778
779        assert_eq!(parts.len(), 3);
780        assert_eq!(parts[1], "peer");
781        assert!(method_ids.len() > 1);
782        assert!(method_ids[1].len() > 1);
783    }
784
785    #[tokio::test]
786    async fn create_peer_did_without_services() {
787        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::Ed25519), true);
788        let services: Vec<DIDPeerService> = vec![];
789
790        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
791        let parts: Vec<&str> = did.split(":").collect();
792        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
793        method_ids = method_ids[1..].to_vec();
794        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
795
796        method_ids.iter().take(2).for_each(|id| {
797            assert!(keys_multibase.contains(&id[1..].to_string()));
798        });
799        assert_eq!(parts.len(), 3);
800        assert_eq!(parts[1], "peer");
801    }
802
803    #[should_panic(expected = "UnsupportedKeyType")]
804    #[tokio::test]
805    async fn create_peer_did_should_throw_unsupported_key_error_p384() {
806        let (_, _, keys) = _get_keys(None, false);
807        // Create a service definition
808        let services = vec![DIDPeerService {
809            _type: "dm".into(),
810            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
811                OneOrMany::One(PeerServiceEndPointLongMap {
812                    uri: "https://localhost:7037".into(),
813                    accept: vec!["didcomm/v2".into()],
814                    routing_keys: vec![],
815                }),
816            )),
817            id: None,
818        }];
819
820        DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
821    }
822
823    #[tokio::test]
824    async fn create_peer_did_works_ed25519_without_passing_pub_key() {
825        let (_, _, keys) = _get_keys(Some(DIDPeerKeyType::Ed25519), false);
826
827        // Create a service definition
828        let services = vec![DIDPeerService {
829            _type: "dm".into(),
830            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
831                OneOrMany::One(PeerServiceEndPointLongMap {
832                    uri: "https://localhost:7037".into(),
833                    accept: vec!["didcomm/v2".into()],
834                    routing_keys: vec![],
835                }),
836            )),
837            id: None,
838        }];
839
840        let (did, keys) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
841        let parts: Vec<&str> = did.split(":").collect();
842        let method_ids: Vec<&str> = parts[2].split(".").collect();
843
844        assert_eq!(keys.len(), 2);
845        assert_eq!(parts.len(), 3);
846        assert_eq!(parts[1], "peer");
847        assert_eq!(method_ids.first().unwrap().parse::<i32>().unwrap(), 2);
848        assert_eq!(method_ids.len(), 4);
849    }
850
851    #[tokio::test]
852    async fn create_peer_did_works_p256_without_passing_pub_key() {
853        let (_, _, keys) = _get_keys(Some(DIDPeerKeyType::P256), false);
854
855        // Create a service definition
856        let services = vec![DIDPeerService {
857            _type: "dm".into(),
858            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
859                OneOrMany::One(PeerServiceEndPointLongMap {
860                    uri: "https://localhost:7037".into(),
861                    accept: vec!["didcomm/v2".into()],
862                    routing_keys: vec![],
863                }),
864            )),
865            id: None,
866        }];
867
868        let (did, keys) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
869        let parts: Vec<&str> = did.split(":").collect();
870        let method_ids: Vec<&str> = parts[2].split(".").collect();
871
872        assert_eq!(keys.len(), 2);
873        assert_eq!(parts.len(), 3);
874        assert_eq!(parts[1], "peer");
875        assert_eq!(method_ids.first().unwrap().parse::<i32>().unwrap(), 2);
876        assert_eq!(method_ids.len(), 4);
877    }
878
879    #[tokio::test]
880    async fn create_peer_did_works_secp256k1_without_passing_pub_key() {
881        let (_, _, keys) = _get_keys(Some(DIDPeerKeyType::Secp256k1), false);
882
883        // Create a service definition
884        let services = vec![DIDPeerService {
885            _type: "dm".into(),
886            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
887                OneOrMany::One(PeerServiceEndPointLongMap {
888                    uri: "https://localhost:7037".into(),
889                    accept: vec!["didcomm/v2".into()],
890                    routing_keys: vec![],
891                }),
892            )),
893            id: None,
894        }];
895
896        let (did, keys) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
897
898        let parts: Vec<&str> = did.split(":").collect();
899        let method_ids: Vec<&str> = parts[2].split(".").collect();
900
901        assert_eq!(keys.len(), 2);
902        assert_eq!(parts.len(), 3);
903        assert_eq!(parts[1], "peer");
904        assert_eq!(method_ids.first().unwrap().parse::<i32>().unwrap(), 2);
905        assert_eq!(method_ids.len(), 4);
906    }
907
908    #[tokio::test]
909    async fn create_peer_did_works_ed25519() {
910        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::Ed25519), true);
911
912        // Create a service definition
913        let services = vec![DIDPeerService {
914            _type: "dm".into(),
915            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
916                OneOrMany::One(PeerServiceEndPointLongMap {
917                    uri: "https://localhost:7037".into(),
918                    accept: vec!["didcomm/v2".into()],
919                    routing_keys: vec![],
920                }),
921            )),
922            id: None,
923        }];
924
925        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
926        let parts: Vec<&str> = did.split(":").collect();
927        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
928        method_ids = method_ids[1..].to_vec();
929        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
930
931        method_ids.iter().take(2).for_each(|id| {
932            assert!(keys_multibase.contains(&id[1..].to_string()));
933        });
934
935        assert_eq!(parts.len(), 3);
936        assert_eq!(parts[1], "peer");
937    }
938
939    #[tokio::test]
940    async fn create_peer_did_works_p256() {
941        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::P256), true);
942        // Create a service definition
943        let services = vec![DIDPeerService {
944            _type: "dm".into(),
945            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
946                OneOrMany::One(PeerServiceEndPointLongMap {
947                    uri: "https://localhost:7037".into(),
948                    accept: vec!["didcomm/v2".into()],
949                    routing_keys: vec![],
950                }),
951            )),
952            id: None,
953        }];
954
955        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
956        let parts: Vec<&str> = did.split(":").collect();
957        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
958        method_ids = method_ids[1..].to_vec();
959        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
960
961        method_ids.iter().take(2).for_each(|id| {
962            assert!(keys_multibase.contains(&id[1..].to_string()));
963        });
964        assert_eq!(parts.len(), 3);
965        assert_eq!(parts[1], "peer");
966    }
967
968    #[tokio::test]
969    async fn create_peer_did_works_secp256k1() {
970        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::Secp256k1), true);
971        // Create a service definition
972        let services = vec![DIDPeerService {
973            _type: "dm".into(),
974            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
975                OneOrMany::One(PeerServiceEndPointLongMap {
976                    uri: "https://localhost:7037".into(),
977                    accept: vec!["didcomm/v2".into()],
978                    routing_keys: vec![],
979                }),
980            )),
981            id: None,
982        }];
983
984        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
985        let parts: Vec<&str> = did.split(":").collect();
986        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
987        method_ids = method_ids[1..].to_vec();
988        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
989
990        method_ids.iter().take(2).for_each(|id| {
991            assert!(keys_multibase.contains(&id[1..].to_string()));
992        });
993        assert_eq!(parts.len(), 3);
994        assert_eq!(parts[1], "peer");
995    }
996
997    fn _get_keys(
998        key_type: Option<DIDPeerKeyType>,
999        with_pub_key: bool,
1000    ) -> (String, String, Vec<DIDPeerCreateKeys>) {
1001        let (e_did_key, _) = match key_type {
1002            Some(DIDPeerKeyType::Ed25519) => {
1003                DIDKey::generate(KeyType::Ed25519).expect("Failed to create did:key")
1004            }
1005            Some(DIDPeerKeyType::P256) => {
1006                DIDKey::generate(KeyType::P256).expect("Failed to create did:key")
1007            }
1008            Some(DIDPeerKeyType::Secp256k1) => {
1009                DIDKey::generate(KeyType::Secp256k1).expect("Failed to create did:key")
1010            }
1011            None => DIDKey::generate(KeyType::P384).expect("Failed to create did:key"),
1012        };
1013        let (v_did_key, _) = match key_type {
1014            Some(DIDPeerKeyType::Ed25519) => {
1015                DIDKey::generate(KeyType::Ed25519).expect("Failed to create did:key")
1016            }
1017            Some(DIDPeerKeyType::P256) => {
1018                DIDKey::generate(KeyType::P256).expect("Failed to create did:key")
1019            }
1020            Some(DIDPeerKeyType::Secp256k1) => {
1021                DIDKey::generate(KeyType::Secp256k1).expect("Failed to create did:key")
1022            }
1023            None => DIDKey::generate(KeyType::P384).expect("Failed to create did:key"),
1024        };
1025
1026        // Put these keys in order and specify the type of each key (we strip the did:key: from the front)
1027        let keys = vec![
1028            DIDPeerCreateKeys {
1029                purpose: DIDPeerKeys::Verification,
1030                type_: key_type.clone(),
1031                public_key_multibase: if with_pub_key {
1032                    Some(v_did_key[8..].to_string())
1033                } else {
1034                    None
1035                },
1036            },
1037            DIDPeerCreateKeys {
1038                purpose: DIDPeerKeys::Encryption,
1039                type_: key_type.clone(),
1040                public_key_multibase: if with_pub_key {
1041                    Some(e_did_key[8..].to_string())
1042                } else {
1043                    None
1044                },
1045            },
1046        ];
1047
1048        (e_did_key, v_did_key, keys)
1049    }
1050}
1051
1052// **********************************************************************************************************************************
1053// WASM Specific structs and code
1054// **********************************************************************************************************************************
1055
1056/// DIDService structure, input into the DidPeerCreate structure
1057///
1058/// DIDService {
1059///         _type: `Option<String>` (Optional: If not specified, defaults to 'DIDCommMessaging')
1060///           uri: `String`         (Required: Service endpoint URI. E.g. https://localhost:7130/)
1061///        accept: `Vec<String>`    (Array of possible message types this service accepts)
1062///  routing_keys: `Vec<String>`    (Array of possible keys this Service endpoint can use)
1063///            id: `Option<String>` (Optional: ID of the service. If not specified, defaults to #service)
1064/// }
1065#[wasm_bindgen(getter_with_clone)]
1066#[derive(Clone, Serialize, Deserialize)]
1067pub struct DIDService {
1068    pub _type: Option<String>,
1069    pub uri: String,
1070    pub accept: Vec<String>,
1071    pub routing_keys: Vec<String>,
1072    pub id: Option<String>,
1073}
1074
1075#[wasm_bindgen]
1076impl DIDService {
1077    #[wasm_bindgen(constructor)]
1078    pub fn new(
1079        uri: String,
1080        accept: Vec<String>,
1081        routing_keys: Vec<String>,
1082        id: Option<String>,
1083    ) -> Self {
1084        DIDService {
1085            _type: None,
1086            uri,
1087            accept,
1088            routing_keys,
1089            id,
1090        }
1091    }
1092}
1093
1094impl From<DIDService> for DIDPeerService {
1095    fn from(service: DIDService) -> Self {
1096        DIDPeerService {
1097            _type: service._type.unwrap_or("DIDCommMessaging".into()),
1098            service_end_point: PeerServiceEndPoint::Short(PeerServiceEndPointShort::Map(
1099                OneOrMany::One(PeerServiceEndPointShortMap {
1100                    uri: service.uri,
1101                    a: service.accept,
1102                    r: service.routing_keys,
1103                }),
1104            )),
1105            id: service.id,
1106        }
1107    }
1108}
1109
1110impl From<&DIDService> for DIDPeerService {
1111    fn from(service: &DIDService) -> Self {
1112        service.clone().into()
1113    }
1114}
1115
1116/// DidPeerCreate structure,  input from JS into [create_did_peer] call
1117/// Contains the required keys and optional services to create a new did:peer DID
1118///
1119/// DIDPeerCreate {
1120///       keys: Vec<[DIDPeerCreateKeys]> (Required: Must contain at least one key for Encryption and another key for Verification)
1121///   services: Option<Vec<[DIDService]> (Optional: Array of DIDService structs to add to the DID Document)
1122/// }
1123#[derive(Clone)]
1124#[wasm_bindgen(getter_with_clone)]
1125pub struct DidPeerCreate {
1126    pub keys: Vec<DIDPeerCreateKeys>,
1127    pub services: Option<Vec<DIDService>>,
1128}
1129
1130#[wasm_bindgen]
1131impl DidPeerCreate {
1132    #[wasm_bindgen(constructor)]
1133    pub fn new(keys: Vec<DIDPeerCreateKeys>, services: Option<Vec<DIDService>>) -> Self {
1134        DidPeerCreate { keys, services }
1135    }
1136}
1137
1138#[derive(Serialize, Deserialize)]
1139#[wasm_bindgen(getter_with_clone)]
1140pub struct DIDPeerResult {
1141    pub did: String,
1142    pub keys: Vec<DIDPeerCreatedKeys>,
1143}
1144
1145#[wasm_bindgen]
1146/// create_did_peer() wasm wrapper for [DIDPeer::create_peer_did]
1147/// Input: reference to [DidPeerCreate] struct
1148/// Returns: Error or String of the newly created did:peer DID
1149///
1150/// Notes:
1151///   [DidPeerCreate] contains an array of keys and an optional array of Services
1152///   These arrays are processed in order (as in element 0 is processed first, then element 1, etc)
1153///   This means the key and service identifiers are auto-generated in the order they are provided
1154///   i.e. #service, #service-1, #service-2 and #key-1, #key-2, #key-3 ...
1155pub fn create_did_peer(input: &DidPeerCreate) -> Result<DIDPeerResult, DIDPeerError> {
1156    // Convert DIDService to DIDPeerService
1157    let mut new_services: Vec<DIDPeerService> = vec![];
1158    if let Some(services) = input.services.as_ref() {
1159        for service in services {
1160            new_services.push(service.into());
1161        }
1162    }
1163
1164    // Create the did:peer DID
1165    let response = DIDPeer::create_peer_did(&input.keys, Some(&new_services));
1166
1167    if let Ok((did, keys)) = response {
1168        Ok(DIDPeerResult { did, keys })
1169    } else {
1170        Err(response.unwrap_err())
1171    }
1172}
1173
1174#[wasm_bindgen]
1175/// resolve_did_peer() resolves a DID Peer method DID to a full DID Document represented by a JS object
1176/// Input: String of the DID Peer method DID (did:peer:2...)
1177/// Returns: Error or JSON String of the resolved DID Document
1178///
1179/// NOTE: This is an async call, so you must await the result
1180pub async fn resolve_did_peer(did: &str) -> Result<String, DIDPeerError> {
1181    let peer = DIDPeer;
1182
1183    match peer.resolve(did).await {
1184        Ok(document) => match serde_json::to_string_pretty(&document) {
1185            Ok(json) => Ok(json),
1186            Err(e) => Err(DIDPeerError::JsonParsingError(format!(
1187                "Couldn't convert DID Document to JSON. Reason: {e}",
1188            ))),
1189        },
1190        Err(e) => Err(DIDPeerError::KeyParsingError(format!(
1191            "Failed to resolve key ({did}). Reason: {e}",
1192        ))),
1193    }
1194}