agentic_payments/ap2/
did.rs

1//! Decentralized Identifier (DID) Management and Resolution
2
3use super::{Ap2Error, Result};
4use crate::ap2::credentials::VerificationMethod;
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// DID Document as per W3C DID specification
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct DidDocument {
13    #[serde(rename = "@context")]
14    pub context: Vec<String>,
15    pub id: String,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub controller: Option<Vec<String>>,
18    pub verification_method: Vec<VerificationMethod>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub authentication: Option<Vec<String>>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub assertion_method: Option<Vec<String>>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub key_agreement: Option<Vec<String>>,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub capability_invocation: Option<Vec<String>>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub capability_delegation: Option<Vec<String>>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub service: Option<Vec<ServiceEndpoint>>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub created: Option<DateTime<Utc>>,
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub updated: Option<DateTime<Utc>>,
35}
36
37/// Service Endpoint for DID Document
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct ServiceEndpoint {
41    pub id: String,
42    #[serde(rename = "type")]
43    pub service_type: String,
44    pub service_endpoint: String,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub description: Option<String>,
47}
48
49impl DidDocument {
50    /// Create a new DID Document
51    pub fn new(id: String, verification_method: VerificationMethod) -> Self {
52        let verification_method_id = verification_method.id.clone();
53
54        Self {
55            context: vec![
56                "https://www.w3.org/ns/did/v1".to_string(),
57                "https://w3id.org/security/suites/ed25519-2020/v1".to_string(),
58            ],
59            id,
60            controller: None,
61            verification_method: vec![verification_method],
62            authentication: Some(vec![verification_method_id.clone()]),
63            assertion_method: Some(vec![verification_method_id.clone()]),
64            key_agreement: None,
65            capability_invocation: Some(vec![verification_method_id.clone()]),
66            capability_delegation: None,
67            service: None,
68            created: Some(Utc::now()),
69            updated: None,
70        }
71    }
72
73    /// Add a service endpoint
74    pub fn add_service(&mut self, service: ServiceEndpoint) {
75        if let Some(ref mut services) = self.service {
76            services.push(service);
77        } else {
78            self.service = Some(vec![service]);
79        }
80        self.updated = Some(Utc::now());
81    }
82
83    /// Add a verification method
84    pub fn add_verification_method(&mut self, method: VerificationMethod) {
85        self.verification_method.push(method);
86        self.updated = Some(Utc::now());
87    }
88
89    /// Get verification method by ID
90    pub fn get_verification_method(&self, id: &str) -> Option<&VerificationMethod> {
91        self.verification_method.iter().find(|m| m.id == id)
92    }
93
94    /// Get service endpoint by type
95    pub fn get_service_by_type(&self, service_type: &str) -> Option<&ServiceEndpoint> {
96        self.service
97            .as_ref()?
98            .iter()
99            .find(|s| s.service_type == service_type)
100    }
101}
102
103/// DID Resolver - Resolves DIDs to DID Documents
104#[derive(Debug, Clone)]
105pub struct DidResolver {
106    cache: HashMap<String, DidDocument>,
107}
108
109impl DidResolver {
110    pub fn new() -> Self {
111        Self {
112            cache: HashMap::new(),
113        }
114    }
115
116    /// Resolve a DID to its document
117    pub fn resolve(&self, did: &str) -> Result<DidDocument> {
118        // Check cache first
119        if let Some(doc) = self.cache.get(did) {
120            return Ok(doc.clone());
121        }
122
123        // In production, this would query actual DID registries
124        // For now, return error for unregistered DIDs
125        Err(Ap2Error::DidResolutionFailed(format!(
126            "DID not found: {}",
127            did
128        )))
129    }
130
131    /// Register a DID document (for testing/local use)
132    pub fn register(&mut self, did_doc: DidDocument) {
133        self.cache.insert(did_doc.id.clone(), did_doc);
134    }
135
136    /// Check if DID exists
137    pub fn exists(&self, did: &str) -> bool {
138        self.cache.contains_key(did)
139    }
140
141    /// Remove a DID from cache
142    pub fn deregister(&mut self, did: &str) -> bool {
143        self.cache.remove(did).is_some()
144    }
145
146    /// Clear all cached DIDs
147    pub fn clear_cache(&mut self) {
148        self.cache.clear();
149    }
150}
151
152impl Default for DidResolver {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158/// DID Manager - Creates and manages DIDs
159#[derive(Debug)]
160pub struct DidManager {
161    resolver: DidResolver,
162    method: String,
163}
164
165impl DidManager {
166    pub fn new() -> Self {
167        Self {
168            resolver: DidResolver::new(),
169            method: "ap2".to_string(),
170        }
171    }
172
173    pub fn with_method(mut self, method: String) -> Self {
174        self.method = method;
175        self
176    }
177
178    /// Create a new DID with Ed25519 key
179    pub fn create_did(&mut self, identifier: &str, public_key: Vec<u8>) -> Result<String> {
180        let did = format!("did:{}:{}", self.method, identifier);
181
182        let verification_method = VerificationMethod {
183            id: format!("{}#key-1", did),
184            method_type: "Ed25519VerificationKey2020".to_string(),
185            controller: did.clone(),
186            public_key_multibase: base64_url::encode(&public_key),
187        };
188
189        let did_doc = DidDocument::new(did.clone(), verification_method);
190        self.resolver.register(did_doc);
191
192        Ok(did)
193    }
194
195    /// Create DID with custom verification method
196    pub fn create_did_with_method(
197        &mut self,
198        identifier: &str,
199        verification_method: VerificationMethod,
200    ) -> Result<String> {
201        let did = format!("did:{}:{}", self.method, identifier);
202        let did_doc = DidDocument::new(did.clone(), verification_method);
203        self.resolver.register(did_doc);
204
205        Ok(did)
206    }
207
208    /// Update DID document
209    pub fn update_did(&mut self, did: &str, mut did_doc: DidDocument) -> Result<()> {
210        if !self.resolver.exists(did) {
211            return Err(Ap2Error::DidResolutionFailed(format!(
212                "DID not found: {}",
213                did
214            )));
215        }
216
217        did_doc.updated = Some(Utc::now());
218        self.resolver.register(did_doc);
219        Ok(())
220    }
221
222    /// Get DID document
223    pub fn get_did_document(&self, did: &str) -> Result<DidDocument> {
224        self.resolver.resolve(did)
225    }
226
227    /// Add service endpoint to DID
228    pub fn add_service_to_did(&mut self, did: &str, service: ServiceEndpoint) -> Result<()> {
229        let mut did_doc = self.resolver.resolve(did)?;
230        did_doc.add_service(service);
231        self.resolver.register(did_doc);
232        Ok(())
233    }
234
235    /// Deactivate a DID
236    pub fn deactivate_did(&mut self, did: &str) -> Result<()> {
237        if !self.resolver.deregister(did) {
238            return Err(Ap2Error::DidResolutionFailed(format!(
239                "DID not found: {}",
240                did
241            )));
242        }
243        Ok(())
244    }
245
246    /// Get resolver reference
247    pub fn resolver(&self) -> &DidResolver {
248        &self.resolver
249    }
250
251    /// Get mutable resolver reference
252    pub fn resolver_mut(&mut self) -> &mut DidResolver {
253        &mut self.resolver
254    }
255}
256
257impl Default for DidManager {
258    fn default() -> Self {
259        Self::new()
260    }
261}
262
263/// DID URL Parser
264pub struct DidUrlParser;
265
266impl DidUrlParser {
267    /// Parse a DID URL into components
268    pub fn parse(did_url: &str) -> Result<DidUrlComponents> {
269        let parts: Vec<&str> = did_url.split(':').collect();
270
271        if parts.len() < 3 || parts[0] != "did" {
272            return Err(Ap2Error::InvalidCredential(format!(
273                "Invalid DID format: {}",
274                did_url
275            )));
276        }
277
278        let method = parts[1].to_string();
279        let method_specific_id = parts[2..].join(":");
280
281        // Check for fragment
282        let (identifier, fragment) = if let Some(pos) = method_specific_id.find('#') {
283            (
284                method_specific_id[..pos].to_string(),
285                Some(method_specific_id[pos + 1..].to_string()),
286            )
287        } else {
288            (method_specific_id, None)
289        };
290
291        Ok(DidUrlComponents {
292            did: format!("did:{}:{}", method, identifier),
293            method,
294            method_specific_id: identifier,
295            fragment,
296            query: None,
297        })
298    }
299}
300
301/// Components of a parsed DID URL
302#[derive(Debug, Clone)]
303pub struct DidUrlComponents {
304    pub did: String,
305    pub method: String,
306    pub method_specific_id: String,
307    pub fragment: Option<String>,
308    pub query: Option<String>,
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn test_did_creation() {
317        let mut manager = DidManager::new();
318        let public_key = vec![1, 2, 3, 4];
319
320        let did = manager.create_did("test-agent", public_key);
321        assert!(did.is_ok());
322
323        let did_str = did.unwrap();
324        assert!(did_str.starts_with("did:ap2:"));
325    }
326
327    #[test]
328    fn test_did_resolution() {
329        let mut manager = DidManager::new();
330        let public_key = vec![1, 2, 3, 4];
331        let did = manager.create_did("test-agent", public_key).unwrap();
332
333        let did_doc = manager.get_did_document(&did);
334        assert!(did_doc.is_ok());
335
336        let doc = did_doc.unwrap();
337        assert_eq!(doc.id, did);
338        assert!(!doc.verification_method.is_empty());
339    }
340
341    #[test]
342    fn test_service_endpoint_addition() {
343        let mut manager = DidManager::new();
344        let public_key = vec![1, 2, 3, 4];
345        let did = manager.create_did("test-agent", public_key).unwrap();
346
347        let service = ServiceEndpoint {
348            id: format!("{}#payment-service", did),
349            service_type: "PaymentService".to_string(),
350            service_endpoint: "https://payment.example.com".to_string(),
351            description: Some("Agent payment endpoint".to_string()),
352        };
353
354        let result = manager.add_service_to_did(&did, service);
355        assert!(result.is_ok());
356
357        let did_doc = manager.get_did_document(&did).unwrap();
358        assert!(did_doc.service.is_some());
359    }
360
361    #[test]
362    fn test_did_url_parsing() {
363        let did_url = "did:ap2:agent123#key-1";
364        let components = DidUrlParser::parse(did_url).unwrap();
365
366        assert_eq!(components.method, "ap2");
367        assert_eq!(components.method_specific_id, "agent123");
368        assert_eq!(components.fragment, Some("key-1".to_string()));
369    }
370}