Skip to main content

structured_xades/validation/
refs.rs

1//! Annex A original reference fields, distinct from authenticated material.
2
3use crate::{
4    certificate,
5    container::XADES,
6    properties::{self, Error, PropertyResult},
7    timestamp::XADES_141,
8    unsigned,
9    xml::{Access, Name, Snapshot, Xml},
10};
11use structured_asn1::memory::{OwnedVec, Work};
12
13/// Referenced artifact schema; no issuer, presence or digest agreement follows.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub enum ReferenceKind {
16    /// One certificate reference from an unsigned V2 reference property.
17    Certificate,
18    /// One CRL reference.
19    Crl,
20    /// One OCSP response reference.
21    Ocsp,
22    /// An original open-format `OtherRef`, requiring separate interpretation.
23    Other,
24}
25
26/// Original scalar obligation, retained without copying a normalized value.
27#[derive(Clone, Copy, Debug)]
28pub enum ReferenceField<N> {
29    /// Optional original unqualified URI attribute.
30    Uri(N),
31    /// Arbitrary-width XSD1.0 integer simple content.
32    Integer(N),
33    /// XSD1.0 dateTime simple content; no implicit timezone is introduced.
34    DateTime(N),
35    /// Original base64Binary simple content.
36    Binary(N),
37}
38
39/// Original artifact identifier, retained for actual semantic comparison.
40/// These selectors neither authenticate a responder nor override digest bytes.
41#[derive(Clone, Copy, Debug)]
42pub enum ReferenceIdentifier<N> {
43    /// No identifier was written (or the entry is an opaque `OtherRef`).
44    Absent,
45    /// Optional DER RFC5035 issuer/serial hint on an original certificate entry.
46    Certificate {
47        /// Original base64 `IssuerSerialV2` element.
48        issuer_serial: N,
49    },
50    /// Original CRL identifier fields; Number and URI are acquisition hints.
51    Crl {
52        /// Original `CRLIdentifier` carrying the optional URI attribute.
53        node: N,
54        /// Original RFC4514 distinguished-name string.
55        issuer: N,
56        /// Original XSD1.0 issue time.
57        time: N,
58        /// Original arbitrary-width CRL number, when written.
59        number: Option<N>,
60    },
61    /// Original OCSP responder identifier and production time.
62    Ocsp {
63        /// Original `OCSPIdentifier` carrying the optional URI attribute.
64        node: N,
65        /// Selected original responder schema branch.
66        responder: ResponderIdentifier<N>,
67        /// Original XSD1.0 production time.
68        time: N,
69    },
70}
71
72/// Original OCSP responder choice; name and key hash cannot be interchanged.
73#[derive(Clone, Copy, Debug)]
74pub enum ResponderIdentifier<N> {
75    /// Original RFC4514 distinguished-name string.
76    Name(N),
77    /// Original base64 DER byKey field, as specified by Annex A.1.2.
78    Key(N),
79}
80
81/// Parsed field selectors of one original reference. This type cannot replace
82/// the protected signing-certificate reference catalogue in key/path checking.
83pub struct ReferenceEntry<N> {
84    node: N,
85    kind: ReferenceKind,
86    digest: Option<(N, N)>,
87    identifier: ReferenceIdentifier<N>,
88}
89
90impl<N: Copy> ReferenceEntry<N> {
91    /// Original `Cert`, `CRLRef`, `OCSPRef` or opaque `OtherRef`.
92    #[must_use]
93    pub fn node(&self) -> N {
94        self.node
95    }
96    /// Actual artifact schema, not validation or authentication.
97    #[must_use]
98    pub fn kind(&self) -> ReferenceKind {
99        self.kind
100    }
101    /// Original method/value nodes when this schema carries a digest.
102    #[must_use]
103    pub fn digest(&self) -> Option<(N, N)> {
104        self.digest
105    }
106    /// Original identifier selectors, without reparsing the reference grammar.
107    #[must_use]
108    pub fn identifier(&self) -> ReferenceIdentifier<N> {
109        self.identifier
110    }
111    /// Fixed-size scalar obligations derived from the same identifier model.
112    /// No copied catalogue or normalized string storage is retained.
113    #[must_use]
114    pub fn fields(&self) -> [Option<ReferenceField<N>>; 4] {
115        let digest = self.digest.map(|(_, node)| ReferenceField::Binary(node));
116        match self.identifier {
117            ReferenceIdentifier::Certificate { issuer_serial } => [
118                digest,
119                Some(ReferenceField::Uri(self.node)),
120                Some(ReferenceField::Binary(issuer_serial)),
121                None,
122            ],
123            ReferenceIdentifier::Crl {
124                node, time, number, ..
125            } => [
126                digest,
127                Some(ReferenceField::DateTime(time)),
128                Some(ReferenceField::Uri(node)),
129                number.map(ReferenceField::Integer),
130            ],
131            ReferenceIdentifier::Ocsp {
132                node,
133                responder,
134                time,
135            } => [
136                match responder {
137                    ResponderIdentifier::Key(node) => Some(ReferenceField::Binary(node)),
138                    ResponderIdentifier::Name(_) => None,
139                },
140                Some(ReferenceField::DateTime(time)),
141                Some(ReferenceField::Uri(node)),
142                digest,
143            ],
144            ReferenceIdentifier::Absent => [
145                digest,
146                if self.kind == ReferenceKind::Certificate {
147                    Some(ReferenceField::Uri(self.node))
148                } else {
149                    None
150                },
151                None,
152                None,
153            ],
154        }
155    }
156}
157
158/// One complete original unsigned reference property. Original certificate
159/// paths, authority restrictions and actual artifact comparisons remain separate.
160pub struct ReferenceProperty<N, D, G> {
161    snapshot: Snapshot<D, G>,
162    signature: N,
163    node: N,
164    role: unsigned::Role,
165    entries: OwnedVec<ReferenceEntry<N>>,
166}
167
168impl<N: Copy, D: Copy, G: Copy> ReferenceProperty<N, D, G> {
169    /// Original document/generation.
170    #[must_use]
171    pub fn snapshot(&self) -> Snapshot<D, G> {
172        self.snapshot
173    }
174    /// Root owning this exact unsigned property.
175    #[must_use]
176    pub fn signature(&self) -> N {
177        self.signature
178    }
179    /// Original reference property.
180    #[must_use]
181    pub fn node(&self) -> N {
182        self.node
183    }
184    /// Ordinary versus attribute certificate/status reference role.
185    #[must_use]
186    pub fn role(&self) -> unsigned::Role {
187        self.role
188    }
189    /// Complete original ordered references, including opaque ones.
190    #[must_use]
191    pub fn entries(&self) -> &[ReferenceEntry<N>] {
192        &self.entries
193    }
194}
195
196type Reading<X> = ReferenceProperty<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
197
198// EN319132-1 V1.3.1 Annex A.1.1–A.1.4/C: each written group is nonempty.
199// A.1.2 additionally forbids empty CompleteRevocationRefs despite the XSD's
200// individually optional groups; schema optionality alone is insufficient.
201// V2's local CertRefs is in 1.4.1; the imported CertIDListV2's Cert is in 1.3.2.
202// https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf
203pub(crate) fn read<X: Xml, W: Work>(
204    access: &Access<'_, X, W>,
205    signature: X::Id,
206    original: unsigned::Entry<X::Id>,
207) -> PropertyResult<Reading<X>, X> {
208    let node = original.node;
209    let mut entries = access.execution().vector();
210    match original.role {
211        unsigned::Role::CertificateReferences | unsigned::Role::AttributeCertificateReferences => {
212            let mut children = access.children(node);
213            let list = required(&mut children, node, "CertRefs")?;
214            expect(access, list, XADES_141, "CertRefs")?;
215            finish(&mut children)?;
216            read_group(access, list, ReferenceKind::Certificate, &mut entries)?;
217        }
218        unsigned::Role::RevocationReferences | unsigned::Role::AttributeRevocationReferences => {
219            let mut prior = None;
220            for child in access.children(node) {
221                let child = child.map_err(Error::Child)?;
222                let (rank, kind) = if named(access, child, XADES, "CRLRefs")? {
223                    (0, ReferenceKind::Crl)
224                } else if named(access, child, XADES, "OCSPRefs")? {
225                    (1, ReferenceKind::Ocsp)
226                } else if named(access, child, XADES, "OtherRefs")? {
227                    (2, ReferenceKind::Other)
228                } else {
229                    return Err(Error::Unexpected { node: child });
230                };
231                if let Some(previous) = prior {
232                    if rank == previous {
233                        return Err(Error::Duplicate { node: child });
234                    }
235                    if rank < previous {
236                        return Err(Error::Order { node: child });
237                    }
238                }
239                prior = Some(rank);
240                read_group(access, child, kind, &mut entries)?;
241            }
242            if original.role == unsigned::Role::RevocationReferences && entries.is_empty() {
243                return Err(Error::Missing {
244                    parent: node,
245                    local: "CRLRefs, OCSPRefs or OtherRefs",
246                });
247            }
248        }
249        _ => return Err(Error::Unexpected { node }),
250    }
251    Ok(ReferenceProperty {
252        snapshot: access.snapshot(),
253        signature,
254        node,
255        role: original.role,
256        entries: entries.into_owned(),
257    })
258}
259
260fn read_group<X: Xml, W: Work>(
261    access: &Access<'_, X, W>,
262    parent: X::Id,
263    kind: ReferenceKind,
264    entries: &mut OwnedVec<ReferenceEntry<X::Id>, W>,
265) -> PropertyResult<(), X> {
266    let local = match kind {
267        ReferenceKind::Certificate => "Cert",
268        ReferenceKind::Crl => "CRLRef",
269        ReferenceKind::Ocsp => "OCSPRef",
270        ReferenceKind::Other => "OtherRef",
271    };
272    let mut found = false;
273    for child in access.children(parent) {
274        let node = child.map_err(Error::Child)?;
275        expect(access, node, XADES, local)?;
276        let mut entry = ReferenceEntry {
277            node,
278            kind,
279            digest: None,
280            identifier: ReferenceIdentifier::Absent,
281        };
282        match kind {
283            ReferenceKind::Certificate => {
284                let reference = certificate::read_entry(access, node)?;
285                entry.digest = Some((reference.method(), reference.digest()));
286                if let Some(issuer) = reference.issuer_serial() {
287                    properties::simple_content(access, issuer)?;
288                    entry.identifier = ReferenceIdentifier::Certificate {
289                        issuer_serial: issuer,
290                    };
291                }
292            }
293            ReferenceKind::Crl => crl(access, &mut entry)?,
294            ReferenceKind::Ocsp => ocsp(access, &mut entry)?,
295            ReferenceKind::Other => {}
296        }
297        entries
298            .try_push(entry)
299            .map_err(|cause| Error::Resource { node, cause })?;
300        found = true;
301    }
302    if !found {
303        return Err(Error::Missing { parent, local });
304    }
305    Ok(())
306}
307
308fn crl<X: Xml, W: Work>(
309    access: &Access<'_, X, W>,
310    entry: &mut ReferenceEntry<X::Id>,
311) -> PropertyResult<(), X> {
312    let mut children = access.children(entry.node);
313    let digest = required(&mut children, entry.node, "DigestAlgAndValue")?;
314    expect(access, digest, XADES, "DigestAlgAndValue")?;
315    let (method, value) = properties::digest_value(access, digest)?;
316    entry.digest = Some((method, value));
317    if let Some(identifier) = children.next() {
318        let identifier = identifier.map_err(Error::Child)?;
319        expect(access, identifier, XADES, "CRLIdentifier")?;
320        let mut fields = access.children(identifier);
321        let issuer = required(&mut fields, identifier, "Issuer")?;
322        simple(access, issuer, "Issuer")?;
323        let time = required(&mut fields, identifier, "IssueTime")?;
324        simple(access, time, "IssueTime")?;
325        let mut crl_number = None;
326        if let Some(number) = fields.next() {
327            let number = number.map_err(Error::Child)?;
328            simple(access, number, "Number")?;
329            crl_number = Some(number);
330        }
331        finish(&mut fields)?;
332        entry.identifier = ReferenceIdentifier::Crl {
333            node: identifier,
334            issuer,
335            time,
336            number: crl_number,
337        };
338    }
339    finish(&mut children)
340}
341
342fn ocsp<X: Xml, W: Work>(
343    access: &Access<'_, X, W>,
344    entry: &mut ReferenceEntry<X::Id>,
345) -> PropertyResult<(), X> {
346    let mut children = access.children(entry.node);
347    let identifier = required(&mut children, entry.node, "OCSPIdentifier")?;
348    expect(access, identifier, XADES, "OCSPIdentifier")?;
349    let mut fields = access.children(identifier);
350    let responder = required(&mut fields, identifier, "ResponderID")?;
351    expect(access, responder, XADES, "ResponderID")?;
352    let mut choice = access.children(responder);
353    let value = required(&mut choice, responder, "ByName or ByKey")?;
354    let responder = if named(access, value, XADES, "ByKey")? {
355        ResponderIdentifier::Key(value)
356    } else {
357        expect(access, value, XADES, "ByName")?;
358        ResponderIdentifier::Name(value)
359    };
360    properties::simple_content(access, value)?;
361    finish(&mut choice)?;
362    let time = required(&mut fields, identifier, "ProducedAt")?;
363    simple(access, time, "ProducedAt")?;
364    finish(&mut fields)?;
365    entry.identifier = ReferenceIdentifier::Ocsp {
366        node: identifier,
367        responder,
368        time,
369    };
370    if let Some(digest) = children.next() {
371        let digest = digest.map_err(Error::Child)?;
372        expect(access, digest, XADES, "DigestAlgAndValue")?;
373        let (method, value) = properties::digest_value(access, digest)?;
374        entry.digest = Some((method, value));
375    }
376    finish(&mut children)
377}
378
379fn required<X: Xml, W: Work>(
380    children: &mut crate::xml::Children<'_, '_, X, W>,
381    parent: X::Id,
382    local: &'static str,
383) -> PropertyResult<X::Id, X> {
384    children
385        .next()
386        .transpose()
387        .map_err(Error::Child)?
388        .ok_or(Error::Missing { parent, local })
389}
390fn finish<X: Xml, W: Work>(
391    children: &mut crate::xml::Children<'_, '_, X, W>,
392) -> PropertyResult<(), X> {
393    if let Some(extra) = children.next() {
394        return Err(Error::Unexpected {
395            node: extra.map_err(Error::Child)?,
396        });
397    }
398    Ok(())
399}
400fn simple<X: Xml, W: Work>(
401    access: &Access<'_, X, W>,
402    node: X::Id,
403    local: &str,
404) -> PropertyResult<(), X> {
405    expect(access, node, XADES, local)?;
406    properties::simple_content(access, node)
407}
408fn expect<X: Xml, W: Work>(
409    access: &Access<'_, X, W>,
410    node: X::Id,
411    namespace: &str,
412    local: &str,
413) -> PropertyResult<(), X> {
414    if !named(access, node, namespace, local)? {
415        return Err(Error::Unexpected { node });
416    }
417    Ok(())
418}
419fn named<X: Xml, W: Work>(
420    access: &Access<'_, X, W>,
421    node: X::Id,
422    namespace: &str,
423    local: &str,
424) -> PropertyResult<bool, X> {
425    access
426        .has_name(node, Name { namespace, local })
427        .map_err(Error::Access)
428}