Skip to main content

hanzo_did/
document.rs

1//! W3C DID Document implementation
2//!
3//! Based on: <https://www.w3.org/TR/did-core/>#did-documents
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10use crate::did::DID;
11use crate::proof::Proof;
12use crate::service::Service;
13use crate::verification_method::VerificationMethod;
14
15/// W3C DID Document
16///
17/// A DID document is the resource that is associated with a DID
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct DIDDocument {
21    /// JSON-LD context
22    #[serde(rename = "@context")]
23    pub context: Vec<String>,
24
25    /// The DID this document is about
26    pub id: String,
27
28    /// Alternative identifiers for this DID
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub also_known_as: Option<Vec<String>>,
31
32    /// DID controller(s)
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub controller: Option<StringOrVec>,
35
36    /// Verification methods
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub verification_method: Option<Vec<VerificationMethod>>,
39
40    /// Authentication verification relationships
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub authentication: Option<Vec<VerificationRelationship>>,
43
44    /// Assertion method verification relationships
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub assertion_method: Option<Vec<VerificationRelationship>>,
47
48    /// Key agreement verification relationships
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub key_agreement: Option<Vec<VerificationRelationship>>,
51
52    /// Capability invocation verification relationships
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub capability_invocation: Option<Vec<VerificationRelationship>>,
55
56    /// Capability delegation verification relationships
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub capability_delegation: Option<Vec<VerificationRelationship>>,
59
60    /// Service endpoints
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub service: Option<Vec<Service>>,
63
64    /// Document metadata
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub created: Option<DateTime<Utc>>,
67
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub updated: Option<DateTime<Utc>>,
70
71    /// Cryptographic proof
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub proof: Option<OneOrMany<Proof>>,
74
75    /// Additional properties
76    #[serde(flatten)]
77    pub additional_properties: HashMap<String, Value>,
78}
79
80impl DIDDocument {
81    /// Create a new DID Document with default W3C context
82    pub fn new(did: &DID) -> Self {
83        Self {
84            context: vec![
85                "https://www.w3.org/ns/did/v1".to_string(),
86                "https://w3id.org/security/suites/ed25519-2020/v1".to_string(),
87                "https://w3id.org/security/suites/x25519-2020/v1".to_string(),
88            ],
89            id: did.to_string(),
90            also_known_as: None,
91            controller: None,
92            verification_method: None,
93            authentication: None,
94            assertion_method: None,
95            key_agreement: None,
96            capability_invocation: None,
97            capability_delegation: None,
98            service: None,
99            created: Some(Utc::now()),
100            updated: None,
101            proof: None,
102            additional_properties: HashMap::new(),
103        }
104    }
105
106    /// Add a verification method
107    pub fn add_verification_method(&mut self, method: VerificationMethod) {
108        match &mut self.verification_method {
109            Some(methods) => methods.push(method),
110            None => self.verification_method = Some(vec![method]),
111        }
112    }
113
114    /// Add an authentication method
115    pub fn add_authentication(&mut self, auth: VerificationRelationship) {
116        match &mut self.authentication {
117            Some(methods) => methods.push(auth),
118            None => self.authentication = Some(vec![auth]),
119        }
120    }
121
122    /// Add a key agreement method
123    pub fn add_key_agreement(&mut self, agreement: VerificationRelationship) {
124        match &mut self.key_agreement {
125            Some(methods) => methods.push(agreement),
126            None => self.key_agreement = Some(vec![agreement]),
127        }
128    }
129
130    /// Add a service endpoint
131    pub fn add_service(&mut self, service: Service) {
132        match &mut self.service {
133            Some(services) => services.push(service),
134            None => self.service = Some(vec![service]),
135        }
136    }
137
138    /// Set controller(s)
139    pub fn set_controller(&mut self, controller: impl Into<StringOrVec>) {
140        self.controller = Some(controller.into());
141    }
142
143    /// Add an alternative identifier
144    pub fn add_also_known_as(&mut self, identifier: String) {
145        match &mut self.also_known_as {
146            Some(ids) => ids.push(identifier),
147            None => self.also_known_as = Some(vec![identifier]),
148        }
149    }
150
151    /// Find a verification method by ID
152    pub fn find_verification_method(&self, id: &str) -> Option<&VerificationMethod> {
153        self.verification_method
154            .as_ref()?
155            .iter()
156            .find(|m| m.id == id || m.id.ends_with(&format!("#{id}")))
157    }
158
159    /// Get all verification methods for authentication
160    pub fn get_authentication_methods(&self) -> Vec<&VerificationMethod> {
161        let mut methods = Vec::new();
162
163        if let Some(auth_refs) = &self.authentication {
164            for auth_ref in auth_refs {
165                match auth_ref {
166                    VerificationRelationship::Reference(id) => {
167                        if let Some(method) = self.find_verification_method(id) {
168                            methods.push(method);
169                        }
170                    }
171                    VerificationRelationship::Embedded(method) => {
172                        methods.push(method);
173                    }
174                }
175            }
176        }
177
178        methods
179    }
180
181    /// Validate the document structure
182    pub fn validate(&self) -> Result<(), Vec<String>> {
183        let mut errors = Vec::new();
184
185        // Check required fields
186        if self.id.is_empty() {
187            errors.push("DID Document must have an id".to_string());
188        }
189
190        if self.context.is_empty() {
191            errors.push("DID Document must have at least one context".to_string());
192        }
193
194        // Validate DID format
195        if DID::from_str(&self.id).is_err() {
196            errors.push(format!("Invalid DID format: {}", self.id));
197        }
198
199        // Validate verification method references
200        if let Some(auth) = &self.authentication {
201            for auth_ref in auth {
202                if let VerificationRelationship::Reference(id) = auth_ref {
203                    if self.find_verification_method(id).is_none() {
204                        errors.push(format!(
205                            "Authentication references non-existent verification method: {id}"
206                        ));
207                    }
208                }
209            }
210        }
211
212        if errors.is_empty() {
213            Ok(())
214        } else {
215            Err(errors)
216        }
217    }
218}
219
220/// Either a string or a vector of strings
221#[derive(Debug, Clone, Serialize, Deserialize)]
222#[serde(untagged)]
223pub enum StringOrVec {
224    String(String),
225    Vec(Vec<String>),
226}
227
228impl From<String> for StringOrVec {
229    fn from(s: String) -> Self {
230        StringOrVec::String(s)
231    }
232}
233
234impl From<Vec<String>> for StringOrVec {
235    fn from(v: Vec<String>) -> Self {
236        StringOrVec::Vec(v)
237    }
238}
239
240/// Either one or many of something
241#[derive(Debug, Clone, Serialize, Deserialize)]
242#[serde(untagged)]
243pub enum OneOrMany<T> {
244    One(T),
245    Many(Vec<T>),
246}
247
248/// Verification relationship - either a reference or embedded method
249#[derive(Debug, Clone, Serialize, Deserialize)]
250#[serde(untagged)]
251pub enum VerificationRelationship {
252    /// Reference to a verification method by ID
253    Reference(String),
254    /// Embedded verification method
255    Embedded(VerificationMethod),
256}
257
258impl From<String> for VerificationRelationship {
259    fn from(s: String) -> Self {
260        // If it starts with # it's a reference to a fragment in the same document
261        if s.starts_with('#') {
262            VerificationRelationship::Reference(s)
263        } else {
264            VerificationRelationship::Reference(s)
265        }
266    }
267}
268
269impl From<&str> for VerificationRelationship {
270    fn from(s: &str) -> Self {
271        VerificationRelationship::Reference(s.to_string())
272    }
273}
274
275impl From<VerificationMethod> for VerificationRelationship {
276    fn from(method: VerificationMethod) -> Self {
277        VerificationRelationship::Embedded(method)
278    }
279}
280
281use std::str::FromStr;
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286    use crate::verification_method::VerificationMethodType;
287
288    #[test]
289    fn test_did_document_creation() {
290        let did = DID::hanzo_eth("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7");
291        let mut doc = DIDDocument::new(&did);
292
293        assert_eq!(doc.id, did.to_string());
294        assert!(!doc.context.is_empty());
295
296        // Add verification method
297        let vm = VerificationMethod {
298            id: format!("{}#key-1", did),
299            type_: VerificationMethodType::Ed25519VerificationKey2020,
300            controller: did.to_string(),
301            public_key_multibase: Some(
302                "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string(),
303            ),
304            ..Default::default()
305        };
306
307        doc.add_verification_method(vm.clone());
308        doc.add_authentication(format!("{}#key-1", did).into());
309
310        assert!(doc.verification_method.is_some());
311        assert!(doc.authentication.is_some());
312        assert!(doc.validate().is_ok());
313    }
314
315    #[test]
316    fn test_find_verification_method() {
317        let did = DID::hanzo_eth("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb7");
318        let mut doc = DIDDocument::new(&did);
319
320        let vm_id = format!("{}#key-1", did);
321        let vm = VerificationMethod {
322            id: vm_id.clone(),
323            type_: VerificationMethodType::Ed25519VerificationKey2020,
324            controller: did.to_string(),
325            public_key_multibase: Some(
326                "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string(),
327            ),
328            ..Default::default()
329        };
330
331        doc.add_verification_method(vm);
332
333        // Find by full ID
334        assert!(doc.find_verification_method(&vm_id).is_some());
335
336        // Find by fragment
337        assert!(doc.find_verification_method("key-1").is_some());
338    }
339}