ssi_dids_core/
document.rs

1use std::{collections::BTreeMap, str::FromStr};
2
3use iref::IriBuf;
4use serde::{Deserialize, Serialize};
5use ssi_core::one_or_many::OneOrMany;
6use ssi_verification_methods_core::{ProofPurpose, ProofPurposes};
7
8use crate::{DIDBuf, DIDURLReferenceBuf, DID, DIDURL};
9
10pub mod representation;
11pub mod resource;
12pub mod service;
13pub mod verification_method;
14
15pub use representation::Represented;
16pub use resource::{Resource, ResourceRef};
17pub use service::Service;
18pub use verification_method::DIDVerificationMethod;
19
20use self::resource::{ExtractResource, FindResource};
21
22/// A [DID document]
23///
24/// [DID document]: https://www.w3.org/TR/did-core/#dfn-did-documents
25#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
26#[serde(rename_all = "camelCase")]
27pub struct Document {
28    /// DID subject identifier.
29    ///
30    /// See: <https://www.w3.org/TR/did-core/#did-subject>
31    pub id: DIDBuf,
32
33    /// Other URIs for the DID subject.
34    ///
35    /// See: <https://www.w3.org/TR/did-core/#also-known-as>
36    #[serde(default, skip_serializing_if = "Vec::is_empty")]
37    pub also_known_as: Vec<IriBuf>,
38
39    /// Verification relationships.
40    ///
41    /// Properties that express the relationship between the DID subject and a
42    /// verification method using a verification relationship.
43    ///
44    /// See: <https://www.w3.org/TR/did-core/#verification-relationships>
45    #[serde(flatten)]
46    pub verification_relationships: VerificationRelationships,
47
48    /// Controllers(s).
49    ///
50    /// See: <https://www.w3.org/TR/did-core/#did-controller>
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub controller: Option<OneOrMany<DIDBuf>>,
53
54    /// [`verificationMethod`](https://www.w3.org/TR/did-core/#dfn-verificationmethod) property of a
55    /// DID document, expressing [verification
56    /// methods](https://www.w3.org/TR/did-core/#verification-methods).
57    #[serde(default, skip_serializing_if = "Vec::is_empty")]
58    pub verification_method: Vec<DIDVerificationMethod>,
59
60    /// [`publicKey`](https://www.w3.org/TR/did-spec-registries/#publickey) property of a DID
61    /// document (deprecated in favor of `verificationMethod`).
62    #[serde(default, skip_serializing_if = "Vec::is_empty")]
63    pub public_key: Vec<DIDVerificationMethod>,
64
65    /// `service` property of a DID document, expressing
66    /// [services](https://www.w3.org/TR/did-core/#services), generally as endpoints.
67    #[serde(default, skip_serializing_if = "Vec::is_empty")]
68    pub service: Vec<Service>,
69
70    /// Additional properties of a DID document. Some may be registered in [DID Specification
71    /// Registries](https://www.w3.org/TR/did-spec-registries/#did-document-properties).
72    #[serde(flatten)]
73    pub property_set: BTreeMap<String, serde_json::Value>,
74}
75
76impl Document {
77    /// Construct a new DID document with the given id (DID).
78    pub fn new(id: DIDBuf) -> Document {
79        Document {
80            id,
81            also_known_as: Vec::new(),
82            controller: None,
83            verification_method: Vec::new(),
84            verification_relationships: VerificationRelationships::default(),
85            service: Vec::new(),
86            property_set: BTreeMap::new(),
87            public_key: Vec::new(),
88        }
89    }
90
91    /// Construct a DID document from JSON.
92    pub fn from_json(json: &str) -> Result<representation::Json, serde_json::Error> {
93        representation::Json::from_str(json)
94    }
95
96    /// Construct a DID document from JSON bytes.
97    pub fn from_json_bytes(json: &[u8]) -> Result<representation::Json, serde_json::Error> {
98        representation::Json::from_bytes(json)
99    }
100
101    pub fn from_bytes(
102        type_: representation::MediaType,
103        bytes: &[u8],
104    ) -> Result<Represented, InvalidData> {
105        match type_ {
106            representation::MediaType::Json => Ok(Represented::Json(
107                representation::Json::from_bytes(bytes).map_err(InvalidData::Json)?,
108            )),
109            representation::MediaType::JsonLd => Ok(Represented::JsonLd(
110                representation::JsonLd::from_bytes(bytes).map_err(InvalidData::JsonLd)?,
111            )),
112        }
113    }
114
115    /// Select an object in the DID document.
116    ///
117    /// See: <https://w3c-ccg.github.io/did-resolution/#dereferencing-algorithm-secondary>
118    pub fn find_resource(&self, id: &DIDURL) -> Option<ResourceRef> {
119        if self.id == *id {
120            Some(ResourceRef::Document(self))
121        } else {
122            self.verification_method
123                .find_resource(&self.id, id)
124                .or_else(|| self.verification_relationships.find_resource(&self.id, id))
125        }
126    }
127
128    /// Select an object in the DID document.
129    ///
130    /// See: <https://w3c-ccg.github.io/did-resolution/#dereferencing-algorithm-secondary>
131    pub fn into_resource(self, id: &DIDURL) -> Option<Resource> {
132        if self.id == *id {
133            Some(Resource::Document(self))
134        } else {
135            self.verification_method
136                .extract_resource(&self.id, id)
137                .or_else(|| {
138                    self.verification_relationships
139                        .extract_resource(&self.id, id)
140                })
141        }
142    }
143
144    /// Returns the service with the given `id`, if any.
145    pub fn service(&self, id: &str) -> Option<&Service> {
146        self.service
147            .iter()
148            .find(|s| s.id.fragment().is_some_and(|f| f.as_str() == id))
149    }
150
151    pub fn into_representation(self, options: representation::Options) -> Represented {
152        Represented::new(self, options)
153    }
154
155    /// Consumes the document and returns any verification method in contains.
156    ///
157    /// This will return the first verification method found, although users
158    /// should not expect the DID documents to always list verification methods
159    /// in the same order.
160    pub fn into_any_verification_method(self) -> Option<DIDVerificationMethod> {
161        self.verification_method.into_iter().next()
162    }
163}
164
165#[derive(Debug, thiserror::Error)]
166#[error("invalid DID document representation data: {0}")]
167pub enum InvalidData {
168    Json(serde_json::Error),
169    JsonLd(serde_json::Error),
170}
171
172/// Document metadata.
173#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
174#[serde(rename_all = "camelCase")]
175pub struct Metadata {
176    pub deactivated: Option<bool>,
177}
178
179#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
180#[serde(rename_all = "camelCase")]
181pub struct VerificationRelationships {
182    /// [`authentication`](https://www.w3.org/TR/did-core/#dfn-authentication) property of a DID
183    /// document, expressing [verification
184    /// methods](https://www.w3.org/TR/did-core/#verification-methods) for
185    /// [authentication](https://www.w3.org/TR/did-core/#authentication) purposes (e.g. generating verifiable presentations).
186    #[serde(default, skip_serializing_if = "Vec::is_empty")]
187    pub authentication: Vec<verification_method::ValueOrReference>,
188
189    /// [`assertionMethod`](https://www.w3.org/TR/did-core/#dfn-assertionmethod) property of a DID document, expressing [verification
190    /// methods](https://www.w3.org/TR/did-core/#verification-methods) for
191    /// [assertion](https://www.w3.org/TR/did-core/#assertion) purposes (e.g. issuing verifiable credentials).
192    #[serde(default, skip_serializing_if = "Vec::is_empty")]
193    pub assertion_method: Vec<verification_method::ValueOrReference>,
194
195    /// [`keyAgreement`](https://www.w3.org/TR/did-core/#dfn-keyagreement) property of a DID document, expressing [verification
196    /// methods](https://www.w3.org/TR/did-core/#verification-methods) for
197    /// [key agreement](https://www.w3.org/TR/did-core/#key-agreement) purposes.
198    #[serde(default, skip_serializing_if = "Vec::is_empty")]
199    pub key_agreement: Vec<verification_method::ValueOrReference>,
200
201    /// [`capabilityInvocation`](https://www.w3.org/TR/did-core/#dfn-capabilityinvocation) property of a DID document, expressing [verification
202    /// methods](https://www.w3.org/TR/did-core/#verification-methods) for
203    /// [invoking cryptographic capabilities](https://www.w3.org/TR/did-core/#capability-invocation).
204    #[serde(default, skip_serializing_if = "Vec::is_empty")]
205    pub capability_invocation: Vec<verification_method::ValueOrReference>,
206
207    /// [`capabilityDelegation`](https://www.w3.org/TR/did-core/#dfn-capabilitydelegation) property of a DID document, expressing [verification
208    /// methods](https://www.w3.org/TR/did-core/#verification-methods) for
209    /// [delegating cryptographic capabilities](https://www.w3.org/TR/did-core/#capability-delegation).
210    #[serde(default, skip_serializing_if = "Vec::is_empty")]
211    pub capability_delegation: Vec<verification_method::ValueOrReference>,
212}
213
214impl VerificationRelationships {
215    pub fn proof_purpose(&self, purpose: ProofPurpose) -> &[verification_method::ValueOrReference] {
216        match purpose {
217            ProofPurpose::Authentication => &self.authentication,
218            ProofPurpose::Assertion => &self.assertion_method,
219            ProofPurpose::KeyAgreement => &self.key_agreement,
220            ProofPurpose::CapabilityInvocation => &self.capability_invocation,
221            ProofPurpose::CapabilityDelegation => &self.capability_delegation,
222        }
223    }
224
225    pub fn contains(&self, base_did: &DID, id: &DIDURL, proof_purposes: ProofPurposes) -> bool {
226        for p in proof_purposes {
227            for v in self.proof_purpose(p) {
228                if *v.id().resolve(base_did) == *id {
229                    return true;
230                }
231            }
232        }
233
234        false
235    }
236
237    /// Creates verification relationships by putting the given method reference
238    /// into all the relations selected by `proof_purposes`.
239    pub fn from_reference(vm_reference: DIDURLReferenceBuf, proof_purposes: ProofPurposes) -> Self {
240        Self {
241            authentication: if proof_purposes.contains(ProofPurpose::Authentication) {
242                vec![verification_method::ValueOrReference::Reference(
243                    vm_reference.clone(),
244                )]
245            } else {
246                Vec::new()
247            },
248            key_agreement: if proof_purposes.contains(ProofPurpose::KeyAgreement) {
249                vec![verification_method::ValueOrReference::Reference(
250                    vm_reference.clone(),
251                )]
252            } else {
253                Vec::new()
254            },
255            capability_invocation: if proof_purposes.contains(ProofPurpose::CapabilityInvocation) {
256                vec![verification_method::ValueOrReference::Reference(
257                    vm_reference.clone(),
258                )]
259            } else {
260                Vec::new()
261            },
262            capability_delegation: if proof_purposes.contains(ProofPurpose::CapabilityDelegation) {
263                vec![verification_method::ValueOrReference::Reference(
264                    vm_reference.clone(),
265                )]
266            } else {
267                Vec::new()
268            },
269            assertion_method: if proof_purposes.contains(ProofPurpose::Assertion) {
270                vec![verification_method::ValueOrReference::Reference(
271                    vm_reference,
272                )]
273            } else {
274                Vec::new()
275            },
276        }
277    }
278}
279
280impl FindResource for VerificationRelationships {
281    fn find_resource(&self, base_did: &DID, id: &DIDURL) -> Option<ResourceRef> {
282        self.authentication
283            .find_resource(base_did, id)
284            .or_else(|| self.assertion_method.find_resource(base_did, id))
285            .or_else(|| self.key_agreement.find_resource(base_did, id))
286            .or_else(|| self.capability_invocation.find_resource(base_did, id))
287            .or_else(|| self.capability_delegation.find_resource(base_did, id))
288    }
289}
290
291impl ExtractResource for VerificationRelationships {
292    fn extract_resource(self, base_did: &DID, id: &DIDURL) -> Option<Resource> {
293        self.authentication
294            .extract_resource(base_did, id)
295            .or_else(|| self.assertion_method.extract_resource(base_did, id))
296            .or_else(|| self.key_agreement.extract_resource(base_did, id))
297            .or_else(|| self.capability_invocation.extract_resource(base_did, id))
298            .or_else(|| self.capability_delegation.extract_resource(base_did, id))
299    }
300}