affinidi_did_common/did_method/
resolve.rs1use std::collections::HashMap;
10
11use serde_json::{Value, json};
12
13use affinidi_crypto::ed25519::ed25519_public_to_x25519;
14use affinidi_encoding::{ED25519_PUB, P256_PUB, P384_PUB, SECP256K1_PUB, X25519_PUB};
15
16use super::DIDMethod;
17use super::peer::{PeerNumAlgo, PeerPurpose, PeerService};
18use crate::{
19 DID, DIDError, Document,
20 service::Service,
21 verification_method::{VerificationMethod, VerificationRelationship},
22};
23
24const PUBLIC_KEY_MULTIBASE: &str = "publicKeyMultibase";
25const MULTIKEY_TYPE: &str = "Multikey";
26
27impl DIDMethod {
28 pub fn resolve(&self, did: &DID) -> Result<Document, DIDError> {
33 match self {
34 DIDMethod::Key { identifier, .. } => resolve_key(did, identifier),
35 DIDMethod::Peer {
36 numalgo,
37 identifier,
38 } => resolve_peer(did, numalgo, identifier),
39 _ => Err(DIDError::ResolutionError(format!(
40 "DID method '{}' requires network resolution",
41 self.name()
42 ))),
43 }
44 }
45}
46
47fn resolve_key(did: &DID, identifier: &str) -> Result<Document, DIDError> {
49 let (codec, _) = affinidi_encoding::decode_multikey_with_codec(identifier)
51 .map_err(|e| DIDError::ResolutionError(format!("Invalid multikey: {e}")))?;
52
53 let mut vm_id = did.url();
54 vm_id.set_fragment(Some(identifier));
55
56 let mut vms = Vec::new();
57 let mut key_agreement = Vec::new();
58
59 match codec {
60 ED25519_PUB => {
61 let (x25519_encoded, _) = ed25519_public_to_x25519(identifier).map_err(|e| {
63 DIDError::ResolutionError(format!("Failed to derive X25519 from Ed25519: {e}"))
64 })?;
65
66 let mut x25519_vm_id = did.url();
67 x25519_vm_id.set_fragment(Some(&x25519_encoded));
68
69 vms.push(VerificationMethod {
70 id: x25519_vm_id.clone(),
71 type_: MULTIKEY_TYPE.to_string(),
72 controller: did.url(),
73 expires: None,
74 revoked: None,
75 property_set: HashMap::from([(
76 PUBLIC_KEY_MULTIBASE.to_string(),
77 Value::String(x25519_encoded.to_string()),
78 )]),
79 });
80
81 key_agreement.push(VerificationRelationship::Reference(
82 x25519_vm_id.to_string(),
83 ));
84 }
85 P256_PUB | P384_PUB | SECP256K1_PUB | X25519_PUB => {
86 key_agreement.push(VerificationRelationship::Reference(vm_id.to_string()));
87 }
88 _ => {
89 return Err(DIDError::ResolutionError(format!(
90 "Unsupported key codec: 0x{codec:x}"
91 )));
92 }
93 }
94
95 vms.insert(
97 0,
98 VerificationMethod {
99 id: vm_id.clone(),
100 type_: MULTIKEY_TYPE.to_string(),
101 controller: did.url(),
102 expires: None,
103 revoked: None,
104 property_set: HashMap::from([(
105 PUBLIC_KEY_MULTIBASE.to_string(),
106 Value::String(identifier.to_string()),
107 )]),
108 },
109 );
110
111 let vm_relationship = VerificationRelationship::Reference(vm_id.to_string());
112
113 Ok(Document {
114 id: did.url(),
115 verification_method: vms,
116 authentication: vec![vm_relationship.clone()],
117 assertion_method: vec![vm_relationship.clone()],
118 key_agreement,
119 capability_invocation: vec![vm_relationship.clone()],
120 capability_delegation: vec![vm_relationship],
121 service: vec![],
122 parameters_set: HashMap::from([(
123 "@context".to_string(),
124 json!([
125 "https://www.w3.org/ns/did/v1",
126 "https://w3id.org/security/multikey/v1",
127 ]),
128 )]),
129 })
130}
131
132fn resolve_peer(did: &DID, numalgo: &PeerNumAlgo, identifier: &str) -> Result<Document, DIDError> {
134 match numalgo {
135 PeerNumAlgo::InceptionKey => {
136 let key_multibase = identifier.strip_prefix('0').unwrap_or(identifier);
139 let key_did: DID = format!("did:key:{key_multibase}")
140 .parse()
141 .map_err(|e| DIDError::ResolutionError(format!("Invalid did:peer:0 key: {e}")))?;
142 key_did.resolve()
143 }
144 PeerNumAlgo::MultipleKeys => resolve_peer_2(did, identifier),
145 PeerNumAlgo::GenesisDoc => Err(DIDError::ResolutionError(
146 "did:peer numalgo 1 (genesis doc) is not supported".to_string(),
147 )),
148 }
149}
150
151fn resolve_peer_2(did: &DID, identifier: &str) -> Result<Document, DIDError> {
155 use std::str::FromStr;
156 use url::Url;
157
158 let did_string = did.to_string();
159
160 let content = identifier.strip_prefix('2').unwrap_or(identifier);
162 let parts: Vec<&str> = content.split('.').filter(|s| !s.is_empty()).collect();
163
164 let mut verification_methods: Vec<VerificationMethod> = Vec::new();
165 let mut authentication: Vec<VerificationRelationship> = Vec::new();
166 let mut assertion_method: Vec<VerificationRelationship> = Vec::new();
167 let mut key_agreement: Vec<VerificationRelationship> = Vec::new();
168 let mut capability_delegation: Vec<VerificationRelationship> = Vec::new();
169 let mut capability_invocation: Vec<VerificationRelationship> = Vec::new();
170 let mut services: Vec<Service> = Vec::new();
171
172 let mut key_count: u32 = 0;
173 let mut service_idx: u32 = 0;
174
175 for part in parts {
176 if part.is_empty() {
177 continue;
178 }
179
180 let purpose_char = part
181 .chars()
182 .next()
183 .ok_or_else(|| DIDError::ResolutionError("Empty part in did:peer".to_string()))?;
184
185 let purpose = PeerPurpose::from_char(purpose_char).ok_or_else(|| {
186 DIDError::ResolutionError(format!("Invalid purpose code: {purpose_char}"))
187 })?;
188
189 if purpose == PeerPurpose::Service {
190 let service = PeerService::decode(part)
192 .map_err(|e| DIDError::ResolutionError(format!("Service decode error: {e}")))?;
193
194 let did_service = service
195 .to_did_service(&did_string, service_idx)
196 .map_err(|e| DIDError::ResolutionError(format!("Service conversion error: {e}")))?;
197
198 services.push(did_service);
199 service_idx += 1;
200 } else {
201 key_count += 1;
203 let kid = format!("{did_string}#key-{key_count}");
204 let public_key_multibase = &part[1..]; let vm = VerificationMethod {
207 id: Url::from_str(&kid)
208 .map_err(|e| DIDError::ResolutionError(format!("Invalid key ID: {e}")))?,
209 type_: MULTIKEY_TYPE.to_string(),
210 controller: did.url(),
211 expires: None,
212 revoked: None,
213 property_set: HashMap::from([(
214 PUBLIC_KEY_MULTIBASE.to_string(),
215 Value::String(public_key_multibase.to_string()),
216 )]),
217 };
218
219 verification_methods.push(vm);
220
221 let relationship = VerificationRelationship::Reference(kid);
222
223 match purpose {
224 PeerPurpose::Verification => {
225 authentication.push(relationship.clone());
226 assertion_method.push(relationship);
227 }
228 PeerPurpose::Encryption => {
229 key_agreement.push(relationship);
230 }
231 PeerPurpose::Assertion => {
232 assertion_method.push(relationship);
233 }
234 PeerPurpose::Delegation => {
235 capability_delegation.push(relationship);
236 }
237 PeerPurpose::Invocation => {
238 capability_invocation.push(relationship);
239 }
240 PeerPurpose::Service => unreachable!(),
241 }
242 }
243 }
244
245 Ok(Document {
246 id: did.url(),
247 verification_method: verification_methods,
248 authentication,
249 assertion_method,
250 key_agreement,
251 capability_delegation,
252 capability_invocation,
253 service: services,
254 parameters_set: HashMap::from([(
255 "@context".to_string(),
256 json!(["https://www.w3.org/ns/did/v1.1"]),
257 )]),
258 })
259}
260
261#[cfg(test)]
262mod tests {
263 use crate::DID;
264
265 #[test]
266 fn test_resolve_ed25519() {
267 let did: DID = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
268 .parse()
269 .unwrap();
270 let doc = did.method().resolve(&did).unwrap();
271
272 assert_eq!(
273 doc.id.as_str(),
274 "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
275 );
276 assert_eq!(doc.verification_method.len(), 2);
278 assert_eq!(doc.key_agreement.len(), 1);
279 }
280
281 #[test]
282 fn test_resolve_p256() {
283 let did: DID = "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
284 .parse()
285 .unwrap();
286 let doc = did.method().resolve(&did).unwrap();
287
288 assert_eq!(
289 doc.id.as_str(),
290 "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
291 );
292 assert_eq!(doc.verification_method.len(), 1);
294 assert_eq!(doc.key_agreement.len(), 1);
295 }
296
297 #[test]
298 fn test_resolve_secp256k1() {
299 let did: DID = "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1a7y8xs6zTcNNvoB5e"
300 .parse()
301 .unwrap();
302 let doc = did.method().resolve(&did).unwrap();
303
304 assert_eq!(doc.verification_method.len(), 1);
305 }
306
307 #[test]
308 fn test_resolve_web_requires_network() {
309 let did: DID = "did:web:example.com".parse().unwrap();
310 let result = did.method().resolve(&did);
311
312 assert!(result.is_err());
313 }
314
315 #[test]
316 fn test_resolve_peer_numalgo_0() {
317 let did: DID = "did:peer:0z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
319 .parse()
320 .unwrap();
321 let doc = did.resolve().unwrap();
322
323 assert_eq!(doc.verification_method.len(), 2); assert_eq!(doc.authentication.len(), 1);
326 assert_eq!(doc.key_agreement.len(), 1);
327 }
328
329 #[test]
330 fn test_resolve_peer_numalgo_2() {
331 let did: DID = "did:peer:2.Vz6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK.Ez6LSbysY2xFMRpGMhb7tFTLMpeuPRaqaWM1yECx2AtzE3KCc"
333 .parse()
334 .unwrap();
335 let doc = did.resolve().unwrap();
336
337 assert_eq!(doc.verification_method.len(), 2);
339 assert_eq!(doc.authentication.len(), 1);
341 assert_eq!(doc.assertion_method.len(), 1);
342 assert_eq!(doc.key_agreement.len(), 1);
344 }
345
346 #[test]
347 fn test_resolve_peer_numalgo_2_with_service() {
348 let did: DID = "did:peer:2.Vz6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9kaWRjb21tIn0"
351 .parse()
352 .unwrap();
353 let doc = did.resolve().unwrap();
354
355 assert_eq!(doc.verification_method.len(), 1);
356 assert_eq!(doc.service.len(), 1);
357 assert_eq!(doc.service[0].type_, vec!["DIDCommMessaging".to_string()]);
358 }
359}