1use async_trait::async_trait;
9use curve25519_dalek::edwards::CompressedEdwardsY;
10use didcomm::did::{
11 DIDDoc, DIDResolver, VerificationMaterial, VerificationMethod, VerificationMethodType,
12};
13use didcomm::error::{
14 Error as DidcommError, ErrorKind as DidcommErrorKind, Result as DidcommResult,
15};
16use multibase::{decode, encode, Base};
17
18use std::collections::HashMap;
19use std::fmt::Debug;
20use std::sync::{Arc, RwLock};
21
22use crate::error::{Error, Result};
23
24#[async_trait]
29pub trait SyncDIDResolver: Send + Sync + Debug {
30 async fn resolve(&self, did: &str) -> Result<Option<DIDDoc>>;
38}
39
40#[async_trait]
42pub trait DIDMethodResolver: Send + Sync + Debug {
43 fn method(&self) -> &str;
45
46 async fn resolve_method(&self, did: &str) -> Result<Option<DIDDoc>>;
54}
55
56#[derive(Debug, Default)]
58pub struct KeyResolver;
59
60impl KeyResolver {
61 pub fn new() -> Self {
63 Self
64 }
65
66 fn ed25519_to_x25519(ed25519_pubkey: &[u8]) -> Option<[u8; 32]> {
71 if ed25519_pubkey.len() != 32 {
73 return None;
74 }
75
76 println!("Ed25519 pubkey: {:?}", ed25519_pubkey);
78
79 let edwards_y = match CompressedEdwardsY::try_from(ed25519_pubkey) {
81 Ok(point) => point,
82 Err(e) => {
83 println!("Error converting to CompressedEdwardsY: {:?}", e);
84 return None;
85 }
86 };
87
88 let edwards_point = match edwards_y.decompress() {
90 Some(point) => point,
91 None => {
92 println!("Failed to decompress Edwards point");
93 return None;
94 }
95 };
96
97 let montgomery_point = edwards_point.to_montgomery();
99
100 Some(montgomery_point.to_bytes())
102 }
103}
104
105#[async_trait]
106impl DIDMethodResolver for KeyResolver {
107 fn method(&self) -> &str {
108 "key"
109 }
110
111 async fn resolve_method(&self, did_key: &str) -> Result<Option<DIDDoc>> {
112 if !did_key.starts_with("did:key:") {
114 return Ok(None);
115 }
116
117 let key_id = &did_key[8..]; let (_, key_bytes) = match decode(key_id) {
120 Ok(result) => result,
121 Err(_) => return Ok(None),
122 };
123
124 if key_bytes.len() < 2 {
126 return Ok(None);
127 }
128
129 if key_bytes[0] != 0xED || key_bytes[1] != 0x01 {
131 return Ok(None);
132 }
133
134 let ed25519_public_key = &key_bytes[2..];
136
137 let ed_vm_id = format!("{}#{}", did_key, key_id);
138
139 let ed_verification_method = VerificationMethod {
141 id: ed_vm_id.clone(),
142 type_: VerificationMethodType::Ed25519VerificationKey2018,
143 controller: did_key.to_string(),
144 verification_material: VerificationMaterial::Multibase {
145 public_key_multibase: key_id.to_string(),
146 },
147 };
148
149 let mut verification_methods = vec![ed_verification_method.clone()];
151 let mut key_agreement = Vec::new();
152
153 if let Some(x25519_key) = Self::ed25519_to_x25519(ed25519_public_key) {
154 println!("Successfully converted Ed25519 to X25519!");
155 let mut x25519_bytes = vec![0xEC, 0x01]; x25519_bytes.extend_from_slice(&x25519_key);
158 let x25519_multibase = encode(Base::Base58Btc, x25519_bytes);
159
160 let x25519_vm_id = format!("{}#{}", did_key, x25519_multibase);
162
163 let x25519_verification_method = VerificationMethod {
165 id: x25519_vm_id.clone(),
166 type_: VerificationMethodType::X25519KeyAgreementKey2019,
167 controller: did_key.to_string(),
168 verification_material: VerificationMaterial::Multibase {
169 public_key_multibase: x25519_multibase,
170 },
171 };
172
173 verification_methods.push(x25519_verification_method);
175 key_agreement.push(x25519_vm_id);
176 } else {
177 println!("Failed to convert Ed25519 to X25519!");
178 }
179
180 let did_doc = DIDDoc {
182 id: did_key.to_string(),
183 verification_method: verification_methods,
184 authentication: vec![ed_vm_id],
185 key_agreement,
186 service: Vec::new(),
187 };
188
189 Ok(Some(did_doc))
190 }
191}
192
193#[derive(Debug)]
196pub struct MultiResolver {
197 resolvers: RwLock<HashMap<String, Arc<dyn DIDMethodResolver>>>,
198}
199
200unsafe impl Send for MultiResolver {}
201unsafe impl Sync for MultiResolver {}
202
203impl MultiResolver {
204 pub fn new() -> Self {
206 Self {
207 resolvers: RwLock::new(HashMap::new()),
208 }
209 }
210
211 pub fn new_with_resolvers(resolvers: Vec<Arc<dyn DIDMethodResolver>>) -> Self {
213 let resolver = Self::new();
214
215 if let Ok(mut resolver_map) = resolver.resolvers.write() {
217 for r in resolvers {
218 let method = r.method().to_string();
219 resolver_map.insert(method, r);
220 }
221 }
222
223 resolver
224 }
225
226 pub fn register_method<R>(&mut self, method: &str, resolver: R) -> &mut Self
228 where
229 R: DIDMethodResolver + Send + Sync + 'static,
230 {
231 if let Ok(mut resolvers) = self.resolvers.write() {
232 resolvers.insert(method.to_string(), Arc::new(resolver));
233 }
234 self
235 }
236}
237
238impl Default for MultiResolver {
239 fn default() -> Self {
240 let mut resolver = Self::new();
241 resolver.register_method("key", KeyResolver::new());
242 resolver
243 }
244}
245
246#[async_trait]
247impl SyncDIDResolver for MultiResolver {
248 async fn resolve(&self, did: &str) -> Result<Option<DIDDoc>> {
249 let parts: Vec<&str> = did.split(':').collect();
251 if parts.len() < 3 {
252 return Err(Error::InvalidDID);
253 }
254
255 let method = parts[1];
256
257 let resolver = {
259 let resolver_guard = self
260 .resolvers
261 .read()
262 .map_err(|_| Error::FailedToAcquireResolverReadLock)?;
263 if let Some(resolver) = resolver_guard.get(method) {
264 resolver.clone()
265 } else {
266 return Err(Error::UnsupportedDIDMethod(method.to_string()));
267 }
268 };
270
271 resolver.resolve_method(did).await
273 }
274}
275
276#[async_trait(?Send)]
277impl DIDResolver for MultiResolver {
278 async fn resolve(&self, did: &str) -> DidcommResult<Option<DIDDoc>> {
279 match SyncDIDResolver::resolve(self, did).await {
280 Ok(did_doc) => Ok(did_doc),
281 Err(e) => Err(DidcommError::new(DidcommErrorKind::InvalidState, e)),
282 }
283 }
284}
285
286#[cfg(target_arch = "wasm32")]
287use wasm_bindgen::prelude::*;
288
289#[cfg(target_arch = "wasm32")]
290use js_sys::{Function, Promise};
291
292#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
293use wasm_bindgen_futures::JsFuture;
294
295#[cfg(target_arch = "wasm32")]
296#[wasm_bindgen]
297extern "C" {
298 #[wasm_bindgen(typescript_type = "Promise<string>")]
299 pub type JsPromiseString;
300
301 #[wasm_bindgen(typescript_type = "Promise<string | null>")]
302 pub type JsPromiseStringOrNull;
303}
304
305#[cfg(target_arch = "wasm32")]
306#[wasm_bindgen]
307pub struct JsDIDResolver {
308 method: String,
309 resolve_fn: Function,
310}
311
312#[cfg(target_arch = "wasm32")]
313#[wasm_bindgen]
314impl JsDIDResolver {
315 #[wasm_bindgen(constructor)]
316 pub fn new(resolve_fn: Function) -> Self {
317 Self {
318 method: "".to_string(),
319 resolve_fn,
320 }
321 }
322
323 #[wasm_bindgen]
324 pub fn method(&self) -> String {
325 let this = JsValue::null();
327 let method = self
328 .resolve_fn
329 .call1(&this, &JsValue::from_str("method"))
330 .unwrap_or_else(|_| JsValue::from_str("unknown"));
331
332 method.as_string().unwrap_or_else(|| "unknown".to_string())
333 }
334}
335
336#[cfg(target_arch = "wasm32")]
338#[async_trait(?Send)]
339pub trait WasmDIDMethodResolver: Debug {
340 fn method(&self) -> &str;
342
343 async fn resolve_method(&self, did: &str) -> Result<Option<DIDDoc>>;
351}
352
353#[cfg(target_arch = "wasm32")]
355#[derive(Debug)]
356pub struct JsDIDMethodResolver {
357 method: String,
358 resolve_fn: Function,
359}
360
361#[cfg(target_arch = "wasm32")]
362impl JsDIDMethodResolver {
363 pub fn new(method: &str, resolve_fn: Function) -> Self {
365 Self {
366 method: method.to_string(),
367 resolve_fn,
368 }
369 }
370}
371
372#[cfg(target_arch = "wasm32")]
373#[async_trait(?Send)]
374impl WasmDIDMethodResolver for JsDIDMethodResolver {
375 fn method(&self) -> &str {
376 &self.method
377 }
378
379 #[cfg(feature = "wasm")]
380 async fn resolve_method(&self, did: &str) -> Result<Option<DIDDoc>> {
381 let parts: Vec<&str> = did.split(':').collect();
383 if parts.len() < 3 || parts[1] != self.method {
384 return Err(Error::InvalidDID);
385 }
386
387 let this = JsValue::null();
388 let js_did = JsValue::from_str(did);
389
390 let promise = self
391 .resolve_fn
392 .call1(&this, &js_did)
393 .map_err(|e| Error::JsResolverError(format!("Error calling JS resolver: {:?}", e)))?;
394
395 let promise = Promise::from(promise);
396 let doc_json = JsFuture::from(promise)
397 .await
398 .map_err(|e| Error::JsResolverError(format!("Error from JS promise: {:?}", e)))?;
399
400 if doc_json.is_null() || doc_json.is_undefined() {
401 return Ok(None);
402 }
403
404 let doc_str = doc_json.as_string().ok_or_else(|| {
405 Error::JsResolverError("JS resolver did not return a string".to_string())
406 })?;
407
408 serde_json::from_str(&doc_str)
410 .map(Some)
411 .map_err(|e| Error::SerdeError(e))
412 }
413
414 #[cfg(not(feature = "wasm"))]
415 async fn resolve_method(&self, _did: &str) -> Result<Option<DIDDoc>> {
416 Err(Error::NotImplemented(
417 "JavaScript DID Method resolver is only available with the 'wasm' feature".to_string(),
418 ))
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 #[cfg(feature = "native")]
427 #[tokio::test]
428 async fn test_key_resolver() {
429 let resolver = KeyResolver::new();
430
431 let did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
433 let result = resolver.resolve_method(did).await.unwrap();
434
435 assert!(result.is_some());
436 let doc = result.unwrap();
437
438 assert_eq!(doc.id, did);
439 assert_eq!(doc.verification_method.len(), 2); let ed25519_method = doc
443 .verification_method
444 .iter()
445 .find(|vm| matches!(vm.type_, VerificationMethodType::Ed25519VerificationKey2018))
446 .expect("Should have an Ed25519 verification method");
447
448 let x25519_method = doc
450 .verification_method
451 .iter()
452 .find(|vm| matches!(vm.type_, VerificationMethodType::X25519KeyAgreementKey2019))
453 .expect("Should have an X25519 key agreement method");
454
455 assert!(doc.authentication.contains(&ed25519_method.id));
457
458 assert!(doc.key_agreement.contains(&x25519_method.id));
460 }
461
462 #[cfg(feature = "native")]
463 #[tokio::test]
464 async fn test_multi_resolver() {
465 let resolver = MultiResolver::default();
466
467 let did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
469 let result = <MultiResolver as SyncDIDResolver>::resolve(&resolver, did).await;
470
471 assert!(result.is_ok());
472 let doc_option = result.unwrap();
473 assert!(doc_option.is_some());
474
475 let doc = doc_option.unwrap();
476 assert_eq!(doc.id, did);
477 assert_eq!(doc.verification_method.len(), 2); let did = "did:unsupported:123";
481 let result = <MultiResolver as SyncDIDResolver>::resolve(&resolver, did).await;
482
483 assert!(result.is_err());
485 let err = result.unwrap_err();
486 assert!(err.to_string().contains("Unsupported DID method"));
487 }
488}