ndn_security/did/resolver/
key.rs1use std::{future::Future, pin::Pin};
10
11use crate::did::{
12 document::{DidDocument, VerificationMethod, VerificationRef},
13 metadata::{DidResolutionError, DidResolutionResult},
14 resolver::DidResolver,
15};
16
17const ED25519_MULTICODEC: [u8; 2] = [0xed, 0x01];
19
20pub struct KeyDidResolver;
22
23impl DidResolver for KeyDidResolver {
24 fn method(&self) -> &str {
25 "key"
26 }
27
28 fn resolve<'a>(
29 &'a self,
30 did: &'a str,
31 ) -> Pin<Box<dyn Future<Output = DidResolutionResult> + Send + 'a>> {
32 let did = did.to_string();
33 Box::pin(async move {
34 match resolve_key_did(&did) {
35 Ok(doc) => DidResolutionResult::ok(doc),
36 Err(e) => DidResolutionResult::err(
37 match &e {
38 s if s.contains("invalid") || s.contains("unsupported") => {
39 DidResolutionError::InvalidDid
40 }
41 _ => DidResolutionError::InternalError,
42 },
43 e,
44 ),
45 }
46 })
47 }
48}
49
50fn resolve_key_did(did: &str) -> Result<DidDocument, String> {
51 let key_str = did
52 .strip_prefix("did:key:")
53 .ok_or_else(|| format!("not a did:key DID: {did}"))?;
54
55 let public_key = decode_multibase_key(key_str)?;
56 let key_id = format!("{did}#{key_str}");
57
58 let vm = VerificationMethod::ed25519_jwk(&key_id, did, &public_key);
59
60 Ok(DidDocument {
61 context: vec![
62 "https://www.w3.org/ns/did/v1".to_string(),
63 "https://w3id.org/security/suites/jws-2020/v1".to_string(),
64 ],
65 id: did.to_string(),
66 controller: None,
67 verification_methods: vec![vm],
68 authentication: vec![VerificationRef::Reference(key_id.clone())],
69 assertion_method: vec![VerificationRef::Reference(key_id.clone())],
70 key_agreement: vec![],
71 capability_invocation: vec![VerificationRef::Reference(key_id.clone())],
72 capability_delegation: vec![VerificationRef::Reference(key_id)],
73 service: vec![],
74 also_known_as: vec![],
75 })
76}
77
78fn decode_multibase_key(encoded: &str) -> Result<Vec<u8>, String> {
79 let b58 = encoded
80 .strip_prefix('z')
81 .ok_or_else(|| format!("unsupported multibase prefix in {encoded}"))?;
82
83 let bytes = bs58_decode(b58).map_err(|_| format!("invalid base58 in {encoded}"))?;
84
85 if bytes.len() < 2 {
86 return Err("key too short".to_string());
87 }
88
89 if bytes[0] == ED25519_MULTICODEC[0] && bytes[1] == ED25519_MULTICODEC[1] {
90 Ok(bytes[2..].to_vec())
91 } else {
92 Err(format!(
93 "unsupported key type (multicodec prefix {:02x}{:02x})",
94 bytes[0], bytes[1]
95 ))
96 }
97}
98
99fn bs58_decode(s: &str) -> Result<Vec<u8>, ()> {
101 const ALPHABET: &[u8] = b"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
102 let mut result: Vec<u8> = vec![0];
103 for c in s.bytes() {
104 let digit = ALPHABET.iter().position(|&b| b == c).ok_or(())? as u64;
105 let mut carry = digit;
106 for byte in result.iter_mut().rev() {
107 carry += (*byte as u64) * 58;
108 *byte = (carry & 0xFF) as u8;
109 carry >>= 8;
110 }
111 while carry > 0 {
112 result.insert(0, (carry & 0xFF) as u8);
113 carry >>= 8;
114 }
115 }
116 let leading_zeros = s.bytes().take_while(|&b| b == b'1').count();
117 let mut out = vec![0u8; leading_zeros];
118 let trimmed: Vec<u8> = result.into_iter().skip_while(|&b| b == 0).collect();
119 out.extend(trimmed);
120 Ok(out)
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[tokio::test]
128 async fn resolve_known_key_did() {
129 let did = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
130 let resolver = KeyDidResolver;
131 let result = resolver.resolve(did).await;
132 assert!(result.did_resolution_metadata.error.is_none(), "{result:?}");
133 let doc = result.did_document.unwrap();
134 assert_eq!(doc.id, did);
135 assert!(!doc.verification_methods.is_empty());
136 assert!(!doc.authentication.is_empty());
137 assert!(!doc.capability_invocation.is_empty());
138 }
139
140 #[tokio::test]
141 async fn invalid_did_returns_error_result() {
142 let resolver = KeyDidResolver;
143 let result = resolver.resolve("did:key:invalid").await;
144 assert!(result.did_resolution_metadata.error.is_some());
145 assert!(result.did_document.is_none());
146 }
147}