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