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