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