ssi_data_integrity_core/proof/de/
mod.rs

1//! Proof deserialization primitives.
2//!
3//! Deserializing a Data-Integrity proof while preserving type information can
4//! be tricky, in particular when the considered cryptographic suite type
5//! abstracts multiple actual cryptographic suite implementations.
6//! In this case, it may be necessary to know the proof `type`
7//! (and `cryptosuite`) field *before* deserializing the other fields of the
8//! proof.
9use crate::{
10    suite::bounds::{OptionsOf, SignatureOf, VerificationMethodOf},
11    CryptosuiteString, DeserializeCryptographicSuite, Proof, Type,
12};
13use serde::{
14    de::{DeserializeSeed, MapAccess},
15    Deserialize,
16};
17use ssi_core::{de::WithType, Lexical, OneOrMany};
18use std::{collections::BTreeMap, marker::PhantomData};
19
20mod field;
21pub use field::*;
22
23mod ref_or_value;
24pub use ref_or_value::*;
25
26mod replay_map;
27pub use replay_map::*;
28
29mod configuration;
30
31/// Converts an XSD dateTime into a XSD dateTimeStamp while preserving the
32/// lexical representation.
33///
34/// If no offset is given in the dateTime, the UTC offset (`Z`) is added.
35fn datetime_to_utc_datetimestamp(
36    value: Option<Lexical<xsd_types::DateTime>>,
37) -> Option<Lexical<xsd_types::DateTimeStamp>> {
38    value.map(|lexical_dt| {
39        let (dt, mut representation) = lexical_dt.into_parts();
40
41        let dts = xsd_types::DateTimeStamp::new(
42            dt.date_time,
43            dt.offset.unwrap_or_else(|| {
44                if let Some(r) = &mut representation {
45                    // Keep most of the lexical representation, just add the
46                    // offset.
47                    r.push('Z');
48                }
49
50                chrono::FixedOffset::east_opt(0).unwrap()
51            }),
52        );
53
54        Lexical::from_parts(dts, representation)
55    })
56}
57
58impl<'de, T: DeserializeCryptographicSuite<'de>> Proof<T> {
59    fn deserialize_with_type<S>(type_: Type, mut deserializer: S) -> Result<Self, S::Error>
60    where
61        S: serde::de::MapAccess<'de>,
62    {
63        let suite: T = type_
64            .try_into()
65            .map_err(|_| serde::de::Error::custom("unexpected cryptosuite"))?;
66
67        let mut context = None;
68        let mut created: Option<Lexical<xsd_types::DateTime>> = None;
69        let mut verification_method = None;
70        let mut proof_purpose = None;
71        let mut expires: Option<Lexical<xsd_types::DateTime>> = None;
72        let mut domains: Option<OneOrMany<String>> = None;
73        let mut challenge = None;
74        let mut nonce = None;
75
76        let mut other = Vec::new();
77
78        while let Some(key) = deserializer.next_key::<Field>()? {
79            match key {
80                Field::Context => context = Some(deserializer.next_value()?),
81                Field::Created => created = Some(deserializer.next_value()?),
82                Field::VerificationMethod => {
83                    verification_method = Some({
84                        deserializer
85                            .next_value_seed(
86                                WithType::<T, RefOrValue<VerificationMethodOf<T>>>::new(&suite),
87                            )?
88                    })
89                }
90                Field::ProofPurpose => proof_purpose = Some(deserializer.next_value()?),
91                Field::Expires => expires = Some(deserializer.next_value()?),
92                Field::Domains => domains = Some(deserializer.next_value()?),
93                Field::Challenge => challenge = Some(deserializer.next_value()?),
94                Field::Nonce => nonce = Some(deserializer.next_value()?),
95                Field::Other(key) => other.push(Some((key, deserializer.next_value()?))),
96            }
97        }
98
99        let options = WithType::<T, OptionsOf<T>>::new(&suite)
100            .deserialize(serde::__private::de::FlatMapDeserializer(
101                &mut other,
102                PhantomData,
103            ))?
104            .0;
105
106        let signature = WithType::<T, SignatureOf<T>>::new(&suite)
107            .deserialize(serde::__private::de::FlatMapDeserializer(
108                &mut other,
109                PhantomData,
110            ))?
111            .0;
112
113        Ok(Self {
114            context,
115            type_: suite,
116            created: datetime_to_utc_datetimestamp(created),
117            verification_method: verification_method
118                .map(|v| v.map(VerificationMethodOf::unwrap).into())
119                .ok_or_else(|| serde::de::Error::custom("missing `verificationMethod` property"))?,
120            proof_purpose: proof_purpose
121                .ok_or_else(|| serde::de::Error::custom("missing `proofPurpose` property"))?,
122            expires: datetime_to_utc_datetimestamp(expires),
123            domains: domains.map(|d| d.into_vec()).unwrap_or_default(),
124            challenge,
125            nonce,
126            options,
127            signature,
128            extra_properties: BTreeMap::deserialize(serde::__private::de::FlatMapDeserializer(
129                &mut other,
130                PhantomData,
131            ))?,
132        })
133    }
134}
135
136impl<'de, T: DeserializeCryptographicSuite<'de>> serde::Deserialize<'de> for Proof<T> {
137    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138    where
139        D: serde::Deserializer<'de>,
140    {
141        deserializer.deserialize_map(ProofVisitor(PhantomData))
142    }
143}
144
145struct ProofVisitor<T>(PhantomData<T>);
146
147impl<'de, T: DeserializeCryptographicSuite<'de>> serde::de::Visitor<'de> for ProofVisitor<T> {
148    type Value = Proof<T>;
149
150    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
151        write!(formatter, "Data-Integrity proof")
152    }
153
154    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
155    where
156        A: MapAccess<'de>,
157    {
158        let mut keep = Vec::new();
159        let mut cryptosuite = None;
160        let mut data_integrity_proof = false;
161
162        while let Some(key) = map.next_key::<TypeField>()? {
163            match key {
164                TypeField::Type => {
165                    let name = map.next_value::<String>()?;
166
167                    if name == "DataIntegrityProof" {
168                        match cryptosuite.take() {
169                            Some(c) => {
170                                return Proof::<T>::deserialize_with_type(
171                                    Type::DataIntegrityProof(c),
172                                    ReplayMap::new(keep, map),
173                                );
174                            }
175                            None => {
176                                data_integrity_proof = true;
177                            }
178                        }
179                    } else {
180                        return Proof::<T>::deserialize_with_type(
181                            Type::Other(name),
182                            ReplayMap::new(keep, map),
183                        );
184                    }
185                }
186                TypeField::Cryptosuite => {
187                    let name = map.next_value::<CryptosuiteString>()?;
188                    if data_integrity_proof {
189                        return Proof::<T>::deserialize_with_type(
190                            Type::DataIntegrityProof(name),
191                            ReplayMap::new(keep, map),
192                        );
193                    } else {
194                        cryptosuite = Some(name)
195                    }
196                }
197                TypeField::Other(key) => {
198                    keep.push((key, map.next_value()?));
199                }
200            }
201        }
202
203        Err(serde::de::Error::custom("missing type"))
204    }
205}