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 utils;
27use utils::{FlatMapDeserializer, ReplayMap};
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(FlatMapDeserializer::new(&mut other))?
101            .0;
102
103        let signature = WithType::<T, SignatureOf<T>>::new(&suite)
104            .deserialize(FlatMapDeserializer::new(&mut other))?
105            .0;
106
107        Ok(Self {
108            context,
109            type_: suite,
110            created: datetime_to_utc_datetimestamp(created),
111            verification_method: verification_method
112                .map(|v| v.map(VerificationMethodOf::unwrap).into())
113                .ok_or_else(|| serde::de::Error::custom("missing `verificationMethod` property"))?,
114            proof_purpose: proof_purpose
115                .ok_or_else(|| serde::de::Error::custom("missing `proofPurpose` property"))?,
116            expires: datetime_to_utc_datetimestamp(expires),
117            domains: domains.map(|d| d.into_vec()).unwrap_or_default(),
118            challenge,
119            nonce,
120            options,
121            signature,
122            extra_properties: BTreeMap::deserialize(FlatMapDeserializer::new(&mut other))?,
123        })
124    }
125}
126
127impl<'de, T: DeserializeCryptographicSuite<'de>> serde::Deserialize<'de> for Proof<T> {
128    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
129    where
130        D: serde::Deserializer<'de>,
131    {
132        deserializer.deserialize_map(ProofVisitor(PhantomData))
133    }
134}
135
136struct ProofVisitor<T>(PhantomData<T>);
137
138impl<'de, T: DeserializeCryptographicSuite<'de>> serde::de::Visitor<'de> for ProofVisitor<T> {
139    type Value = Proof<T>;
140
141    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
142        write!(formatter, "Data-Integrity proof")
143    }
144
145    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
146    where
147        A: MapAccess<'de>,
148    {
149        let mut keep = Vec::new();
150        let mut cryptosuite = None;
151        let mut data_integrity_proof = false;
152
153        while let Some(key) = map.next_key::<TypeField>()? {
154            match key {
155                TypeField::Type => {
156                    let name = map.next_value::<String>()?;
157
158                    if name == "DataIntegrityProof" {
159                        match cryptosuite.take() {
160                            Some(c) => {
161                                return Proof::<T>::deserialize_with_type(
162                                    Type::DataIntegrityProof(c),
163                                    ReplayMap::new(keep, map),
164                                );
165                            }
166                            None => {
167                                data_integrity_proof = true;
168                            }
169                        }
170                    } else {
171                        return Proof::<T>::deserialize_with_type(
172                            Type::Other(name),
173                            ReplayMap::new(keep, map),
174                        );
175                    }
176                }
177                TypeField::Cryptosuite => {
178                    let name = map.next_value::<CryptosuiteString>()?;
179                    if data_integrity_proof {
180                        return Proof::<T>::deserialize_with_type(
181                            Type::DataIntegrityProof(name),
182                            ReplayMap::new(keep, map),
183                        );
184                    } else {
185                        cryptosuite = Some(name)
186                    }
187                }
188                TypeField::Other(key) => {
189                    keep.push((key, map.next_value()?));
190                }
191            }
192        }
193
194        Err(serde::de::Error::custom("missing type"))
195    }
196}