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, id: &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(&["did:peer:", did, id].concat()).unwrap(),
345        type_: "Multikey".to_string(),
346        controller: Url::from_str(&["did:peer:", did].concat()).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        let did = ["did:peer:", method_specific_id].concat();
359
360        // If did:peer is type 0, then treat it as a did:key
361        if let Some(id) = method_specific_id.strip_prefix('0') {
362            return DIDKey::resolve(&["did:key:", id].concat()).map_err(|e| {
363                DIDPeerError::InternalError(format!(
364                    "Resolving version 0 of did:peer resulted in the following error: {e}"
365                ))
366            });
367        }
368
369        // Only supports method 2 for did:peer
370        if !method_specific_id.starts_with('2') {
371            return Err(DIDPeerError::MethodNotSupported);
372        }
373
374        let mut context = BTreeMap::new();
375        context.insert("@base".to_string(), serde_json::json!(method_specific_id));
376
377        let mut verification_methods: Vec<VerificationMethod> = Vec::new();
378
379        let mut key_agreements: Vec<VerificationRelationship> = Vec::new();
380        let mut key_authentications: Vec<VerificationRelationship> = Vec::new();
381        let mut key_assertion_methods: Vec<VerificationRelationship> = Vec::new();
382        let mut key_capability_delegation: Vec<VerificationRelationship> = Vec::new();
383        let mut key_capability_invocation: Vec<VerificationRelationship> = Vec::new();
384        let mut services: Vec<Service> = Vec::new();
385
386        // Split the DID for peer on '.'s, we skip the first one
387        // did:peer:2.(process from here)
388        let parts: Vec<&str> = method_specific_id[2..].split('.').collect();
389        let mut key_count: u32 = 1;
390        let mut service_idx: u32 = 0;
391
392        for part in parts {
393            let ch = part.chars().next();
394            match ch {
395                Some(e) => {
396                    match e {
397                        'A' => {
398                            // Assertion Method
399                            verification_methods.push(process_key(
400                                method_specific_id,
401                                &["#key-", &key_count.to_string()].concat(),
402                                &part[1..],
403                            ));
404
405                            key_assertion_methods.push(VerificationRelationship::Reference(
406                                Url::from_str(&["#key-", &key_count.to_string()].concat()).unwrap(),
407                            ));
408
409                            key_count += 1;
410                        }
411                        'D' => {
412                            // Capability Delegation
413                            verification_methods.push(process_key(
414                                method_specific_id,
415                                &["#key-", &key_count.to_string()].concat(),
416                                &part[1..],
417                            ));
418
419                            key_capability_delegation.push(VerificationRelationship::Reference(
420                                Url::from_str(&["did:peer:#key-", &key_count.to_string()].concat())
421                                    .unwrap(),
422                            ));
423
424                            key_count += 1;
425                        }
426                        'E' => {
427                            // Key Agreement (Encryption)
428                            verification_methods.push(process_key(
429                                method_specific_id,
430                                &["#key-", &key_count.to_string()].concat(),
431                                &part[1..],
432                            ));
433
434                            key_agreements.push(VerificationRelationship::Reference(
435                                Url::from_str(&["did:peer:#key-", &key_count.to_string()].concat())
436                                    .unwrap(),
437                            ));
438
439                            key_count += 1;
440                        }
441                        'I' => {
442                            // Capability Invocation
443                            verification_methods.push(process_key(
444                                method_specific_id,
445                                &["#key-", &key_count.to_string()].concat(),
446                                &part[1..],
447                            ));
448
449                            key_capability_invocation.push(VerificationRelationship::Reference(
450                                Url::from_str(&["did:peer:#key-", &key_count.to_string()].concat())
451                                    .unwrap(),
452                            ));
453
454                            key_count += 1;
455                        }
456                        'V' => {
457                            // Authentication (Verification)
458                            verification_methods.push(process_key(
459                                method_specific_id,
460                                &["#key-", &key_count.to_string()].concat(),
461                                &part[1..],
462                            ));
463
464                            key_authentications.push(VerificationRelationship::Reference(
465                                Url::from_str(&["did:peer:#key-", &key_count.to_string()].concat())
466                                    .unwrap(),
467                            ));
468
469                            key_assertion_methods.push(VerificationRelationship::Reference(
470                                Url::from_str(&["did:peer:#key-", &key_count.to_string()].concat())
471                                    .unwrap(),
472                            ));
473
474                            key_count += 1;
475                        }
476                        'S' => {
477                            // Service
478                            let service = convert_service(&did, part, service_idx)
479                                .map_err(|e| DIDPeerError::InternalError(e.to_string()))?;
480                            services.push(service);
481                            service_idx += 1;
482                        }
483                        other => {
484                            return Err(DIDPeerError::EncodingError(format!(
485                                "An invalid Purpose Code ({other}) was found in the DID",
486                            )));
487                        }
488                    }
489                }
490                None => {
491                    // We shouldn't really get here
492                    // But it is ok if we do, we just skip it
493                }
494            }
495        }
496
497        let mut parameters_set = HashMap::new();
498        parameters_set.insert(
499            "@context".to_string(),
500            json!(["https://www.w3.org/ns/did/v1.1".to_string()]),
501        );
502
503        Ok(Document {
504            id: Url::from_str(["did:peer:", method_specific_id].concat().as_str()).unwrap(),
505            verification_method: verification_methods,
506            assertion_method: key_assertion_methods,
507            authentication: key_authentications,
508            capability_delegation: key_capability_delegation,
509            capability_invocation: key_capability_invocation,
510            key_agreement: key_agreements,
511            service: services,
512            parameters_set,
513        })
514    }
515}
516
517impl DIDPeer {
518    /// Creates a new did:peer DID
519    ///
520    /// This will preserve the order of the keys and services in creating the did:peer string
521    ///
522    /// # Examples
523    /// ```ignore
524    ///
525    /// // Create a did:peer with pre-existing encryption key (Multibase base58-btc e.g: z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK)
526    /// let keys = vec![DIDPeerCreateKeys {
527    ///     type_: Some(DIDPeerKeyType::Ed25519),
528    ///     purpose: DIDPeerKeys::Encryption,
529    ///     public_key_multibase: Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".into()),
530    /// }];
531    /// let did_peer = DIDPeer::create_peer_did(&keys, None).expect("Failed to create did:peer");
532    ///
533    /// // Create a random did:peer with services
534    /// let keys = vec![DIDPeerCreateKeys {
535    ///    type_: Some(DIDPeerKeyType::Secp256k1),
536    ///    purpose: DIDPeerKeys::Encryption,
537    ///    public_key_multibase: Some("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".into()),
538    ///  }];
539    /// let services: Vec<DIDPeerService> = vec![DIDPeerService {
540    ///    _type: "dm".into(),
541    ///    id: None,
542    ///    service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong {
543    ///        uri: "http://example.com/didcomm".into(),
544    ///        accept: vec!["didcomm/v2".into()],
545    ///        routing_keys: vec!["did:example:123456789abcdefghi#key-1".into()],
546    ///    }),
547    /// }];
548    ///
549    /// let did_peer =
550    ///    DIDPeer::create_peer_did(&keys, Some(&services)).expect("Failed to create did:peer");
551    ///
552    /// // How to create a key prior to creating a did:peer
553    /// let did =
554    ///     DIDKey::generate(&JWK::generate_ed25519().unwrap()).expect("Failed to create did:key");
555    ///
556    /// let keys = vec![DIDPeerCreateKeys {
557    ///     type_: Some(DIDPeerKeyType::Ed25519),
558    ///     purpose: DIDPeerKeys::Verification,
559    ///     public_key_multibase: Some(did[8..].to_string()),
560    /// }];
561    ///
562    /// let did_peer = DIDPeer::create_peer_did(&keys, None).expect("Failed to create did:peer");
563    /// ```
564    pub fn create_peer_did(
565        keys: &Vec<DIDPeerCreateKeys>,
566        services: Option<&Vec<DIDPeerService>>,
567    ) -> Result<(String, Vec<DIDPeerCreatedKeys>), DIDPeerError> {
568        let mut result = String::from("did:peer:2");
569
570        let mut private_keys: Vec<DIDPeerCreatedKeys> = vec![];
571        for key in keys {
572            // Create new keys if not provided
573            let public_key = if let Some(key) = key.public_key_multibase.as_ref() {
574                key.clone()
575            } else {
576                let (did, secret) = match &key.type_ {
577                    Some(type_) => match type_ {
578                        DIDPeerKeyType::Ed25519 => {
579                            DIDKey::generate(KeyType::Ed25519).map_err(|e| {
580                                DIDPeerError::InternalError(format!(
581                                    "Couldn't create Ed25519 did:key reason: {e}"
582                                ))
583                            })?
584                        }
585                        DIDPeerKeyType::Secp256k1 => {
586                            DIDKey::generate(KeyType::Secp256k1).map_err(|e| {
587                                DIDPeerError::InternalError(format!(
588                                    "Couldn't create Secp256k1 did:key reason: {e}"
589                                ))
590                            })?
591                        }
592                        DIDPeerKeyType::P256 => DIDKey::generate(KeyType::P256).map_err(|e| {
593                            DIDPeerError::InternalError(format!(
594                                "Couldn't create P256 did:key reason: {e}"
595                            ))
596                        })?,
597                    },
598                    None => return Err(DIDPeerError::UnsupportedKeyType),
599                };
600
601                if let SecretMaterial::JWK(jwk) = secret.secret_material {
602                    match jwk.params {
603                        Params::OKP(map) => {
604                            let d = if let Some(d) = &map.d {
605                                d
606                            } else {
607                                return Err(DIDPeerError::KeyParsingError(
608                                    "Missing private key".to_string(),
609                                ));
610                            };
611                            private_keys.push(DIDPeerCreatedKeys {
612                                key_multibase: did[8..].to_string(),
613                                curve: map.curve.clone(),
614                                d: d.clone(),
615                                x: map.x.clone(),
616                                y: None,
617                            })
618                        }
619                        Params::EC(map) => {
620                            let d = if let Some(d) = &map.d {
621                                d
622                            } else {
623                                return Err(DIDPeerError::KeyParsingError(
624                                    "Missing private key".to_string(),
625                                ));
626                            };
627
628                            private_keys.push(DIDPeerCreatedKeys {
629                                key_multibase: did[8..].to_string(),
630                                curve: map.curve.clone(),
631                                d: String::from(d),
632                                x: map.x.clone(),
633                                y: Some(map.y.clone()),
634                            })
635                        }
636                    }
637                } else {
638                    return Err(DIDPeerError::InternalError(
639                        "Expected Secret Material to be in JWK format!".to_string(),
640                    ));
641                }
642
643                did[8..].to_string()
644            };
645
646            // Place based on key types
647            match key.purpose {
648                DIDPeerKeys::Verification => {
649                    result.push_str(&format!(".V{public_key}",));
650                }
651                DIDPeerKeys::Encryption => {
652                    result.push_str(&format!(".E{public_key}",));
653                }
654            }
655        }
656
657        if let Some(services) = services {
658            for service in services {
659                let service = serde_json::to_string(&service).map_err(|e| {
660                    DIDPeerError::SyntaxErrorServiceDefinition(format!(
661                        "Error parsing service: {e}",
662                    ))
663                })?;
664                result.push_str(&format!(".S{}", BASE64_URL_SAFE_NO_PAD.encode(service)));
665            }
666        }
667
668        Ok((result, private_keys))
669    }
670
671    /// Expands an existing DID Document from the did:key Multikeys to full JWT keys
672    /// This is useful for when you want to resolve a did:peer DID Document to a full JWT included DID Document
673    /// Converts base58 multi-keys to full JWTs in verificationMethod
674    pub async fn expand_keys(doc: &Document) -> Result<Document, DIDPeerError> {
675        let mut new_doc = doc.clone();
676
677        let mut new_vms: Vec<VerificationMethod> = vec![];
678        for v_method in &doc.verification_method {
679            new_vms.push(Self::_convert_vm(v_method).await?);
680        }
681
682        new_doc.verification_method = new_vms;
683        Ok(new_doc)
684    }
685
686    // Converts
687    async fn _convert_vm(method: &VerificationMethod) -> Result<VerificationMethod, DIDPeerError> {
688        let current_controller = method.controller.clone();
689        let current_id = method.id.clone();
690
691        let did_key = if let Some(key) = method.property_set.get("publicKeyBase58") {
692            ["did:key:", key.as_str().unwrap()].concat()
693        } else if let Some(key) = method.property_set.get("publicKeyMultibase") {
694            ["did:key:", key.as_str().unwrap()].concat()
695        } else {
696            return Err(DIDPeerError::KeyParsingError(
697                "Failed to convert verification_method. Reason: Missing publicKeyBase58"
698                    .to_string(),
699            ));
700        };
701
702        let document = match DIDKey::resolve(&did_key) {
703            Ok(document) => document,
704            Err(e) => {
705                return Err(DIDPeerError::KeyParsingError(format!(
706                    "Failed to resolve key ({did_key}). Reason: {e}",
707                )));
708            }
709        };
710
711        if let Some(vm) = document.verification_method.first() {
712            let mut properties: HashMap<String, Value> = HashMap::new();
713            for (k, v) in vm.property_set.iter() {
714                properties.insert(k.clone(), v.clone());
715            }
716
717            Ok(VerificationMethod {
718                id: current_id,
719                type_: vm.type_.clone(),
720                controller: current_controller,
721                expires: None,
722                revoked: None,
723                property_set: properties,
724            })
725        } else {
726            Err(DIDPeerError::KeyParsingError(
727                "Failed to convert verification_method. Reason: Missing verification_method"
728                    .to_string(),
729            ))
730        }
731    }
732}
733
734#[cfg(test)]
735mod test {
736    use affinidi_did_common::{one_or_many::OneOrMany, verification_method::VerificationMethod};
737    use affinidi_did_key::DIDKey;
738    use affinidi_secrets_resolver::secrets::KeyType;
739
740    use crate::{
741        DIDPeer, DIDPeerCreateKeys, DIDPeerKeyType, DIDPeerKeys, DIDPeerService,
742        PeerServiceEndPoint, PeerServiceEndPointLong, PeerServiceEndPointLongMap,
743    };
744
745    const DID_PEER: &str = "did:peer:2.Vz6MkiToqovww7vYtxm1xNM15u9JzqzUFZ1k7s7MazYJUyAxv.EzQ3shQLqRUza6AMJFbPuMdvFRFWm1wKviQRnQSC1fScovJN4s.SeyJ0IjoiRElEQ29tbU1lc3NhZ2luZyIsInMiOnsidXJpIjoiaHR0cHM6Ly8xMjcuMC4wLjE6NzAzNyIsImEiOlsiZGlkY29tbS92MiJdLCJyIjpbXX19";
746
747    #[should_panic(
748        expected = "Failed to convert verification_method. Reason: Missing publicKeyBase58"
749    )]
750    #[tokio::test]
751    async fn expand_keys_throws_key_parsing_missing_pbk58_error() {
752        let peer = DIDPeer;
753        let output = peer.resolve(DID_PEER).await.unwrap();
754
755        let mut document = output.clone();
756        let mut new_vms: Vec<VerificationMethod> = vec![];
757        for mut vm in document.verification_method {
758            vm.property_set.remove("publicKeyMultibase");
759            new_vms.push(vm);
760        }
761
762        document.verification_method = new_vms;
763        let _expanded_doc = DIDPeer::expand_keys(&document).await.unwrap();
764    }
765
766    #[tokio::test]
767    async fn expand_keys_works() {
768        let peer = DIDPeer;
769        let document = peer.resolve(DID_PEER).await.expect("Couldn't resolve DID");
770
771        let vm_before_expansion = document.verification_method.clone();
772        let expanded_doc = DIDPeer::expand_keys(&document).await.unwrap();
773        let vms_after_expansion = expanded_doc.verification_method;
774
775        for vm in vms_after_expansion.clone() {
776            assert!(vm.id.as_str().starts_with("did:peer"));
777        }
778        assert_eq!(vm_before_expansion.len(), vms_after_expansion.len())
779    }
780
781    #[tokio::test]
782    async fn create_peer_did_without_keys_and_services() {
783        let keys: Vec<DIDPeerCreateKeys> = vec![];
784        let services: Vec<DIDPeerService> = vec![];
785
786        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
787        let parts: Vec<&str> = did.split(":").collect();
788
789        assert_eq!(parts.len(), 3);
790        assert_eq!(parts[1], "peer");
791        assert!(parts[2].len() == 1);
792    }
793
794    #[tokio::test]
795    async fn create_peer_did_without_keys() {
796        let keys: Vec<DIDPeerCreateKeys> = vec![];
797        let services = vec![DIDPeerService {
798            _type: "dm".into(),
799            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
800                OneOrMany::One(PeerServiceEndPointLongMap {
801                    uri: "https://localhost:7037".into(),
802                    accept: vec!["didcomm/v2".into()],
803                    routing_keys: vec![],
804                }),
805            )),
806            id: None,
807        }];
808
809        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
810        let parts: Vec<&str> = did.split(":").collect();
811        let method_ids: Vec<&str> = parts[2].split(".").collect();
812
813        assert_eq!(parts.len(), 3);
814        assert_eq!(parts[1], "peer");
815        assert!(method_ids.len() > 1);
816        assert!(method_ids[1].len() > 1);
817    }
818
819    #[tokio::test]
820    async fn create_peer_did_without_services() {
821        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::Ed25519), true);
822        let services: Vec<DIDPeerService> = vec![];
823
824        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
825        let parts: Vec<&str> = did.split(":").collect();
826        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
827        method_ids = method_ids[1..].to_vec();
828        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
829
830        method_ids.iter().take(2).for_each(|id| {
831            assert!(keys_multibase.contains(&id[1..].to_string()));
832        });
833        assert_eq!(parts.len(), 3);
834        assert_eq!(parts[1], "peer");
835    }
836
837    #[should_panic(expected = "UnsupportedKeyType")]
838    #[tokio::test]
839    async fn create_peer_did_should_throw_unsupported_key_error_p384() {
840        let (_, _, keys) = _get_keys(None, false);
841        // Create a service definition
842        let services = vec![DIDPeerService {
843            _type: "dm".into(),
844            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
845                OneOrMany::One(PeerServiceEndPointLongMap {
846                    uri: "https://localhost:7037".into(),
847                    accept: vec!["didcomm/v2".into()],
848                    routing_keys: vec![],
849                }),
850            )),
851            id: None,
852        }];
853
854        DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
855    }
856
857    #[tokio::test]
858    async fn create_peer_did_works_ed25519_without_passing_pub_key() {
859        let (_, _, keys) = _get_keys(Some(DIDPeerKeyType::Ed25519), false);
860
861        // Create a service definition
862        let services = vec![DIDPeerService {
863            _type: "dm".into(),
864            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
865                OneOrMany::One(PeerServiceEndPointLongMap {
866                    uri: "https://localhost:7037".into(),
867                    accept: vec!["didcomm/v2".into()],
868                    routing_keys: vec![],
869                }),
870            )),
871            id: None,
872        }];
873
874        let (did, keys) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
875        let parts: Vec<&str> = did.split(":").collect();
876        let method_ids: Vec<&str> = parts[2].split(".").collect();
877
878        assert_eq!(keys.len(), 2);
879        assert_eq!(parts.len(), 3);
880        assert_eq!(parts[1], "peer");
881        assert_eq!(method_ids.first().unwrap().parse::<i32>().unwrap(), 2);
882        assert_eq!(method_ids.len(), 4);
883    }
884
885    #[tokio::test]
886    async fn create_peer_did_works_p256_without_passing_pub_key() {
887        let (_, _, keys) = _get_keys(Some(DIDPeerKeyType::P256), false);
888
889        // Create a service definition
890        let services = vec![DIDPeerService {
891            _type: "dm".into(),
892            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
893                OneOrMany::One(PeerServiceEndPointLongMap {
894                    uri: "https://localhost:7037".into(),
895                    accept: vec!["didcomm/v2".into()],
896                    routing_keys: vec![],
897                }),
898            )),
899            id: None,
900        }];
901
902        let (did, keys) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
903        let parts: Vec<&str> = did.split(":").collect();
904        let method_ids: Vec<&str> = parts[2].split(".").collect();
905
906        assert_eq!(keys.len(), 2);
907        assert_eq!(parts.len(), 3);
908        assert_eq!(parts[1], "peer");
909        assert_eq!(method_ids.first().unwrap().parse::<i32>().unwrap(), 2);
910        assert_eq!(method_ids.len(), 4);
911    }
912
913    #[tokio::test]
914    async fn create_peer_did_works_secp256k1_without_passing_pub_key() {
915        let (_, _, keys) = _get_keys(Some(DIDPeerKeyType::Secp256k1), false);
916
917        // Create a service definition
918        let services = vec![DIDPeerService {
919            _type: "dm".into(),
920            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
921                OneOrMany::One(PeerServiceEndPointLongMap {
922                    uri: "https://localhost:7037".into(),
923                    accept: vec!["didcomm/v2".into()],
924                    routing_keys: vec![],
925                }),
926            )),
927            id: None,
928        }];
929
930        let (did, keys) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
931
932        let parts: Vec<&str> = did.split(":").collect();
933        let method_ids: Vec<&str> = parts[2].split(".").collect();
934
935        assert_eq!(keys.len(), 2);
936        assert_eq!(parts.len(), 3);
937        assert_eq!(parts[1], "peer");
938        assert_eq!(method_ids.first().unwrap().parse::<i32>().unwrap(), 2);
939        assert_eq!(method_ids.len(), 4);
940    }
941
942    #[tokio::test]
943    async fn create_peer_did_works_ed25519() {
944        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::Ed25519), true);
945
946        // Create a service definition
947        let services = vec![DIDPeerService {
948            _type: "dm".into(),
949            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
950                OneOrMany::One(PeerServiceEndPointLongMap {
951                    uri: "https://localhost:7037".into(),
952                    accept: vec!["didcomm/v2".into()],
953                    routing_keys: vec![],
954                }),
955            )),
956            id: None,
957        }];
958
959        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
960        let parts: Vec<&str> = did.split(":").collect();
961        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
962        method_ids = method_ids[1..].to_vec();
963        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
964
965        method_ids.iter().take(2).for_each(|id| {
966            assert!(keys_multibase.contains(&id[1..].to_string()));
967        });
968
969        assert_eq!(parts.len(), 3);
970        assert_eq!(parts[1], "peer");
971    }
972
973    #[tokio::test]
974    async fn create_peer_did_works_p256() {
975        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::P256), true);
976        // Create a service definition
977        let services = vec![DIDPeerService {
978            _type: "dm".into(),
979            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
980                OneOrMany::One(PeerServiceEndPointLongMap {
981                    uri: "https://localhost:7037".into(),
982                    accept: vec!["didcomm/v2".into()],
983                    routing_keys: vec![],
984                }),
985            )),
986            id: None,
987        }];
988
989        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
990        let parts: Vec<&str> = did.split(":").collect();
991        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
992        method_ids = method_ids[1..].to_vec();
993        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
994
995        method_ids.iter().take(2).for_each(|id| {
996            assert!(keys_multibase.contains(&id[1..].to_string()));
997        });
998        assert_eq!(parts.len(), 3);
999        assert_eq!(parts[1], "peer");
1000    }
1001
1002    #[tokio::test]
1003    async fn create_peer_did_works_secp256k1() {
1004        let (e_did_key, v_did_key, keys) = _get_keys(Some(DIDPeerKeyType::Secp256k1), true);
1005        // Create a service definition
1006        let services = vec![DIDPeerService {
1007            _type: "dm".into(),
1008            service_end_point: PeerServiceEndPoint::Long(PeerServiceEndPointLong::Map(
1009                OneOrMany::One(PeerServiceEndPointLongMap {
1010                    uri: "https://localhost:7037".into(),
1011                    accept: vec!["didcomm/v2".into()],
1012                    routing_keys: vec![],
1013                }),
1014            )),
1015            id: None,
1016        }];
1017
1018        let (did, _) = DIDPeer::create_peer_did(&keys, Some(&services)).unwrap();
1019        let parts: Vec<&str> = did.split(":").collect();
1020        let mut method_ids: Vec<&str> = parts[2].split(".").collect();
1021        method_ids = method_ids[1..].to_vec();
1022        let keys_multibase = [v_did_key[8..].to_string(), e_did_key[8..].to_string()];
1023
1024        method_ids.iter().take(2).for_each(|id| {
1025            assert!(keys_multibase.contains(&id[1..].to_string()));
1026        });
1027        assert_eq!(parts.len(), 3);
1028        assert_eq!(parts[1], "peer");
1029    }
1030
1031    fn _get_keys(
1032        key_type: Option<DIDPeerKeyType>,
1033        with_pub_key: bool,
1034    ) -> (String, String, Vec<DIDPeerCreateKeys>) {
1035        let (e_did_key, _) = match key_type {
1036            Some(DIDPeerKeyType::Ed25519) => {
1037                DIDKey::generate(KeyType::Ed25519).expect("Failed to create did:key")
1038            }
1039            Some(DIDPeerKeyType::P256) => {
1040                DIDKey::generate(KeyType::P256).expect("Failed to create did:key")
1041            }
1042            Some(DIDPeerKeyType::Secp256k1) => {
1043                DIDKey::generate(KeyType::Secp256k1).expect("Failed to create did:key")
1044            }
1045            None => DIDKey::generate(KeyType::P384).expect("Failed to create did:key"),
1046        };
1047        let (v_did_key, _) = match key_type {
1048            Some(DIDPeerKeyType::Ed25519) => {
1049                DIDKey::generate(KeyType::Ed25519).expect("Failed to create did:key")
1050            }
1051            Some(DIDPeerKeyType::P256) => {
1052                DIDKey::generate(KeyType::P256).expect("Failed to create did:key")
1053            }
1054            Some(DIDPeerKeyType::Secp256k1) => {
1055                DIDKey::generate(KeyType::Secp256k1).expect("Failed to create did:key")
1056            }
1057            None => DIDKey::generate(KeyType::P384).expect("Failed to create did:key"),
1058        };
1059
1060        // Put these keys in order and specify the type of each key (we strip the did:key: from the front)
1061        let keys = vec![
1062            DIDPeerCreateKeys {
1063                purpose: DIDPeerKeys::Verification,
1064                type_: key_type.clone(),
1065                public_key_multibase: if with_pub_key {
1066                    Some(v_did_key[8..].to_string())
1067                } else {
1068                    None
1069                },
1070            },
1071            DIDPeerCreateKeys {
1072                purpose: DIDPeerKeys::Encryption,
1073                type_: key_type.clone(),
1074                public_key_multibase: if with_pub_key {
1075                    Some(e_did_key[8..].to_string())
1076                } else {
1077                    None
1078                },
1079            },
1080        ];
1081
1082        (e_did_key, v_did_key, keys)
1083    }
1084}
1085
1086// **********************************************************************************************************************************
1087// WASM Specific structs and code
1088// **********************************************************************************************************************************
1089
1090/// DIDService structure, input into the DidPeerCreate structure
1091///
1092/// DIDService {
1093///         _type: `Option<String>` (Optional: If not specified, defaults to 'DIDCommMessaging')
1094///           uri: `String`         (Required: Service endpoint URI. E.g. https://localhost:7130/)
1095///        accept: `Vec<String>`    (Array of possible message types this service accepts)
1096///  routing_keys: `Vec<String>`    (Array of possible keys this Service endpoint can use)
1097///            id: `Option<String>` (Optional: ID of the service. If not specified, defaults to #service)
1098/// }
1099#[wasm_bindgen(getter_with_clone)]
1100#[derive(Clone, Serialize, Deserialize)]
1101pub struct DIDService {
1102    pub _type: Option<String>,
1103    pub uri: String,
1104    pub accept: Vec<String>,
1105    pub routing_keys: Vec<String>,
1106    pub id: Option<String>,
1107}
1108
1109#[wasm_bindgen]
1110impl DIDService {
1111    #[wasm_bindgen(constructor)]
1112    pub fn new(
1113        uri: String,
1114        accept: Vec<String>,
1115        routing_keys: Vec<String>,
1116        id: Option<String>,
1117    ) -> Self {
1118        DIDService {
1119            _type: None,
1120            uri,
1121            accept,
1122            routing_keys,
1123            id,
1124        }
1125    }
1126}
1127
1128impl From<DIDService> for DIDPeerService {
1129    fn from(service: DIDService) -> Self {
1130        DIDPeerService {
1131            _type: service._type.unwrap_or("DIDCommMessaging".into()),
1132            service_end_point: PeerServiceEndPoint::Short(PeerServiceEndPointShort::Map(
1133                OneOrMany::One(PeerServiceEndPointShortMap {
1134                    uri: service.uri,
1135                    a: service.accept,
1136                    r: service.routing_keys,
1137                }),
1138            )),
1139            id: service.id,
1140        }
1141    }
1142}
1143
1144impl From<&DIDService> for DIDPeerService {
1145    fn from(service: &DIDService) -> Self {
1146        service.clone().into()
1147    }
1148}
1149
1150/// DidPeerCreate structure,  input from JS into [create_did_peer] call
1151/// Contains the required keys and optional services to create a new did:peer DID
1152///
1153/// DIDPeerCreate {
1154///       keys: Vec<[DIDPeerCreateKeys]> (Required: Must contain at least one key for Encryption and another key for Verification)
1155///   services: Option<Vec<[DIDService]> (Optional: Array of DIDService structs to add to the DID Document)
1156/// }
1157#[derive(Clone)]
1158#[wasm_bindgen(getter_with_clone)]
1159pub struct DidPeerCreate {
1160    pub keys: Vec<DIDPeerCreateKeys>,
1161    pub services: Option<Vec<DIDService>>,
1162}
1163
1164#[wasm_bindgen]
1165impl DidPeerCreate {
1166    #[wasm_bindgen(constructor)]
1167    pub fn new(keys: Vec<DIDPeerCreateKeys>, services: Option<Vec<DIDService>>) -> Self {
1168        DidPeerCreate { keys, services }
1169    }
1170}
1171
1172#[derive(Serialize, Deserialize)]
1173#[wasm_bindgen(getter_with_clone)]
1174pub struct DIDPeerResult {
1175    pub did: String,
1176    pub keys: Vec<DIDPeerCreatedKeys>,
1177}
1178
1179#[wasm_bindgen]
1180/// create_did_peer() wasm wrapper for [DIDPeer::create_peer_did]
1181/// Input: reference to [DidPeerCreate] struct
1182/// Returns: Error or String of the newly created did:peer DID
1183///
1184/// Notes:
1185///   [DidPeerCreate] contains an array of keys and an optional array of Services
1186///   These arrays are processed in order (as in element 0 is processed first, then element 1, etc)
1187///   This means the key and service identifiers are auto-generated in the order they are provided
1188///   i.e. #service, #service-1, #service-2 and #key-1, #key-2, #key-3 ...
1189pub fn create_did_peer(input: &DidPeerCreate) -> Result<DIDPeerResult, DIDPeerError> {
1190    // Convert DIDService to DIDPeerService
1191    let mut new_services: Vec<DIDPeerService> = vec![];
1192    if let Some(services) = input.services.as_ref() {
1193        for service in services {
1194            new_services.push(service.into());
1195        }
1196    }
1197
1198    // Create the did:peer DID
1199    let response = DIDPeer::create_peer_did(&input.keys, Some(&new_services));
1200
1201    if let Ok((did, keys)) = response {
1202        Ok(DIDPeerResult { did, keys })
1203    } else {
1204        Err(response.unwrap_err())
1205    }
1206}
1207
1208#[wasm_bindgen]
1209/// resolve_did_peer() resolves a DID Peer method DID to a full DID Document represented by a JS object
1210/// Input: String of the DID Peer method DID (did:peer:2...)
1211/// Returns: Error or JSON String of the resolved DID Document
1212///
1213/// NOTE: This is an async call, so you must await the result
1214pub async fn resolve_did_peer(did: &str) -> Result<String, DIDPeerError> {
1215    let peer = DIDPeer;
1216
1217    match peer.resolve(did).await {
1218        Ok(document) => match serde_json::to_string_pretty(&document) {
1219            Ok(json) => Ok(json),
1220            Err(e) => Err(DIDPeerError::JsonParsingError(format!(
1221                "Couldn't convert DID Document to JSON. Reason: {e}",
1222            ))),
1223        },
1224        Err(e) => Err(DIDPeerError::KeyParsingError(format!(
1225            "Failed to resolve key ({did}). Reason: {e}",
1226        ))),
1227    }
1228}