ssi_data_integrity_core/
options.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use ssi_core::Lexical;
5use ssi_verification_methods::{ProofPurpose, ReferenceOrOwned};
6
7use crate::{suite::ConfigurationError, CryptographicSuite, ProofConfiguration};
8
9/// Proof options.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct ProofOptions<M, T> {
13    #[serde(rename = "@context", skip_serializing_if = "Option::is_none")]
14    pub context: Option<ssi_json_ld::syntax::Context>,
15
16    /// Date a creation of the proof.
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub created: Option<Lexical<xsd_types::DateTimeStamp>>,
19
20    /// Verification method.
21    pub verification_method: Option<ReferenceOrOwned<M>>,
22
23    /// Purpose of the proof.
24    #[serde(default)]
25    pub proof_purpose: ProofPurpose,
26
27    /// Specifies when the proof expires.
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub expires: Option<Lexical<xsd_types::DateTimeStamp>>,
30
31    #[allow(rustdoc::bare_urls)]
32    /// Conveys one or more security domains in which the proof is meant to be
33    /// used.
34    ///
35    /// A verifier SHOULD use the value to ensure that the proof was intended to
36    /// be used in the security domain in which the verifier is operating. The
37    /// specification of the domain parameter is useful in challenge-response
38    /// protocols where the verifier is operating from within a security domain
39    /// known to the creator of the proof.
40    ///
41    /// Example domain values include: `domain.example`` (DNS domain),
42    /// `https://domain.example:8443` (Web origin), `mycorp-intranet` (bespoke
43    /// text string), and `b31d37d4-dd59-47d3-9dd8-c973da43b63a` (UUID).
44    #[serde(
45        default,
46        with = "crate::value_or_array",
47        skip_serializing_if = "Vec::is_empty",
48        rename = "domain"
49    )]
50    pub domains: Vec<String>,
51
52    /// Used to mitigate replay attacks.
53    ///
54    /// Used once for a particular domain and window of time. Examples of a
55    /// challenge value include: `1235abcd6789`,
56    /// `79d34551-ae81-44ae-823b-6dadbab9ebd4`, and `ruby`.
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub challenge: Option<String>,
59
60    /// Arbitrary string supplied by the proof creator.
61    ///
62    /// One use of this field is to increase privacy by decreasing linkability
63    /// that is the result of deterministically generated signatures.
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub nonce: Option<String>,
66
67    /// Additional proof options required by the cryptographic suite.
68    ///
69    /// For instance, tezos cryptosuites requires the public key associated with
70    /// the verification method, which is a blockchain account id.
71    #[serde(flatten)]
72    pub options: T,
73
74    /// Extra properties.
75    #[serde(flatten)]
76    pub extra_properties: BTreeMap<String, json_syntax::Value>,
77}
78
79impl<M, T: Default> Default for ProofOptions<M, T> {
80    fn default() -> Self {
81        Self {
82            context: None,
83            created: Some(xsd_types::DateTimeStamp::now_ms().into()),
84            verification_method: None,
85            proof_purpose: ProofPurpose::default(),
86            expires: None,
87            domains: Vec::new(),
88            challenge: None,
89            nonce: None,
90            options: Default::default(),
91            extra_properties: BTreeMap::new(),
92        }
93    }
94}
95
96impl<M, T> ProofOptions<M, T> {
97    pub fn new(
98        created: Lexical<xsd_types::DateTimeStamp>,
99        verification_method: ReferenceOrOwned<M>,
100        proof_purpose: ProofPurpose,
101        options: T,
102    ) -> Self {
103        Self {
104            context: None,
105            created: Some(created),
106            verification_method: Some(verification_method),
107            proof_purpose,
108            expires: None,
109            domains: Vec::new(),
110            challenge: None,
111            nonce: None,
112            options,
113            extra_properties: BTreeMap::new(),
114        }
115    }
116
117    pub fn from_method_and_options(verification_method: ReferenceOrOwned<M>, options: T) -> Self {
118        Self {
119            context: None,
120            created: Some(xsd_types::DateTimeStamp::now_ms().into()),
121            verification_method: Some(verification_method),
122            proof_purpose: ProofPurpose::default(),
123            expires: None,
124            domains: Vec::new(),
125            challenge: None,
126            nonce: None,
127            options,
128            extra_properties: BTreeMap::new(),
129        }
130    }
131
132    pub fn from_method(verification_method: ReferenceOrOwned<M>) -> Self
133    where
134        T: Default,
135    {
136        Self::from_method_and_options(verification_method, Default::default())
137    }
138
139    pub fn map<N, U>(
140        self,
141        map_verification_method: impl FnOnce(M) -> N,
142        map_options: impl FnOnce(T) -> U,
143    ) -> ProofOptions<N, U> {
144        ProofOptions {
145            context: self.context,
146            created: self.created,
147            verification_method: self
148                .verification_method
149                .map(|m| m.map(map_verification_method)),
150            proof_purpose: self.proof_purpose,
151            expires: self.expires,
152            domains: self.domains,
153            challenge: self.challenge,
154            nonce: self.nonce,
155            options: map_options(self.options),
156            extra_properties: self.extra_properties,
157        }
158    }
159
160    pub fn cast<N, U>(self) -> ProofOptions<N, U>
161    where
162        M: Into<N>,
163        T: Into<U>,
164    {
165        self.map(Into::into, Into::into)
166    }
167
168    pub fn try_map<N, U, E>(
169        self,
170        map_verification_method: impl FnOnce(M) -> Result<N, E>,
171        map_options: impl FnOnce(T) -> Result<U, E>,
172    ) -> Result<ProofOptions<N, U>, E> {
173        Ok(ProofOptions {
174            context: self.context,
175            created: self.created,
176            verification_method: self
177                .verification_method
178                .map(|m| m.try_map(map_verification_method))
179                .transpose()?,
180            proof_purpose: self.proof_purpose,
181            expires: self.expires,
182            domains: self.domains,
183            challenge: self.challenge,
184            nonce: self.nonce,
185            options: map_options(self.options)?,
186            extra_properties: self.extra_properties,
187        })
188    }
189
190    pub fn into_configuration_with<S>(
191        self,
192        type_: S,
193        f: impl FnOnce(T) -> S::ProofOptions,
194    ) -> Result<ProofConfiguration<S>, ConfigurationError>
195    where
196        S: CryptographicSuite<VerificationMethod = M>,
197    {
198        Ok(ProofConfiguration {
199            context: self.context,
200            type_,
201            created: self.created,
202            verification_method: self
203                .verification_method
204                .ok_or(ConfigurationError::MissingVerificationMethod)?,
205            proof_purpose: self.proof_purpose,
206            expires: self.expires,
207            domains: self.domains,
208            challenge: self.challenge,
209            nonce: self.nonce,
210            options: f(self.options),
211            extra_properties: self.extra_properties,
212        })
213    }
214
215    pub fn into_configuration<S>(
216        self,
217        type_: S,
218    ) -> Result<ProofConfiguration<S>, ConfigurationError>
219    where
220        S: CryptographicSuite<VerificationMethod = M, ProofOptions = T>,
221    {
222        self.into_configuration_with(type_, |o| o)
223    }
224}