Skip to main content

structured_xades/validation/
ref_fields.rs

1//! XML-owned scalar execution over the already selected reference field model.
2
3use super::{ReferenceField, ReferenceProperty};
4use crate::{
5    binary::{BinaryXml, Location},
6    certificate::{self, Agreement},
7    properties::{DateTimeFact, DateTimeXml},
8    reference::LexicalSource,
9    scalar::{self, Datatype, ScalarXml},
10    xml::{Access, AccessError, Name, Xml},
11};
12use core::convert::Infallible;
13use structured_asn1::memory::{MemoryError, Work};
14
15/// Original field processing or completed unproven syntax, never artifact
16/// mismatch, missing material or provider authentication.
17#[derive(Debug)]
18pub enum ReferenceFieldError<E, N, D, G> {
19    /// Reference metadata belongs to another original generation.
20    Foreign {
21        /// Original property.
22        node: N,
23    },
24    /// Current operation, source or method inspection refused.
25    Access(AccessError<E, N>),
26    /// Original requested URI/integer lexical outcome.
27    Scalar(scalar::Error<E, N>),
28    /// The complete XML-owned dateTime reading was malformed or unsupported.
29    Time {
30        /// Original `IssueTime` or `ProducedAt`.
31        node: N,
32        /// Actual schema interpretation; no timezone default.
33        fact: DateTimeFact,
34    },
35    /// Original base64 decoding failure in its real XML enclosure.
36    Binary {
37        /// Original binary node/generation.
38        location: Location<N, D, G>,
39        /// Original source/resource failure.
40        cause: AccessError<E, N>,
41    },
42    /// Known prohibited or malformed method before provider dispatch.
43    Method {
44        /// Original `DigestMethod`.
45        node: N,
46        /// Completed original syntax/format finding.
47        cause: Agreement,
48    },
49    /// Metadata admission refused before accessing the next retained entry.
50    Resource {
51        /// Original property.
52        node: N,
53        /// Exact resource cause.
54        cause: MemoryError,
55    },
56}
57
58/// Scalar interpretation result in the original XML owner's identity domain.
59pub type ReferenceFieldResult<X> = Result<
60    (),
61    ReferenceFieldError<
62        <X as Xml>::Error,
63        <X as Xml>::Id,
64        <X as Xml>::Document,
65        <X as Xml>::Generation,
66    >,
67>;
68
69/// Execute each retained reference field through its XML owner's EXISTING
70/// datatype/base64 kernel, without another child parse, normalization or ASN.1
71/// fabrication. Hint dateTimes may genuinely omit a timezone; unlike `SigningTime`
72/// they are not converted to a claimed UTC instant. Open `OtherRef` payloads retain
73/// their explicit opaque kind for a format handler, not an invented validity flag.
74pub fn check_reference_fields<X: ScalarXml + DateTimeXml + BinaryXml, W: Work>(
75    references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
76    access: &Access<'_, X, W>,
77) -> ReferenceFieldResult<X> {
78    let node = references.node();
79    if references.snapshot() != access.snapshot() {
80        return Err(ReferenceFieldError::Foreign { node });
81    }
82    access
83        .query(node, |_, _, _| Ok(()))
84        .map_err(ReferenceFieldError::Access)?;
85    let mut ordinal = 0;
86    while ordinal < references.entries().len() {
87        access
88            .execution()
89            .charge_work(1)
90            .map_err(|cause| ReferenceFieldError::Resource { node, cause })?;
91        let entry = &references.entries()[ordinal];
92        if let Some((method, _)) = entry.digest()
93            && let Some(cause) = certificate::preflight_method(access, method)
94                .map_err(ReferenceFieldError::Access)?
95        {
96            return Err(ReferenceFieldError::Method {
97                node: method,
98                cause,
99            });
100        }
101        let fields = entry.fields();
102        for field in fields {
103            access
104                .execution()
105                .charge_work(1)
106                .map_err(|cause| ReferenceFieldError::Resource { node, cause })?;
107            if let Some(field) = field {
108                check_field(access, field)?;
109            }
110        }
111        ordinal += 1;
112    }
113    access
114        .query(node, |_, _, _| Ok(()))
115        .map_err(ReferenceFieldError::Access)
116}
117
118fn check_field<X: ScalarXml + DateTimeXml + BinaryXml, W: Work>(
119    access: &Access<'_, X, W>,
120    field: ReferenceField<X::Id>,
121) -> ReferenceFieldResult<X> {
122    match field {
123        ReferenceField::Uri(node) => {
124            let uri = Name {
125                namespace: "",
126                local: "URI",
127            };
128            if access
129                .attribute(node, uri, |value| value.is_some())
130                .map_err(ReferenceFieldError::Access)?
131            {
132                scalar::check(
133                    access,
134                    node,
135                    LexicalSource::Attribute(uri),
136                    Datatype::AnyUri,
137                )
138                .map_err(ReferenceFieldError::Scalar)?;
139            }
140        }
141        ReferenceField::Integer(node) => {
142            scalar::check(
143                access,
144                node,
145                LexicalSource::SimpleContent,
146                Datatype::Integer,
147            )
148            .map_err(ReferenceFieldError::Scalar)?;
149        }
150        ReferenceField::DateTime(node) => {
151            let fact = access
152                .query(node, |xml, snapshot, work| {
153                    xml.date_time(snapshot, node, work)
154                })
155                .map_err(ReferenceFieldError::Access)?;
156            // Annex A's identifier fields are xsd:dateTime, whose timezone is
157            // optional (XSD1.0 Datatypes §3.2.7). The distinct §6.3(h)
158            // SigningTime UTC requirement must not leak into these hints.
159            // https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#dateTime
160            match fact {
161                DateTimeFact::Valid { .. } => (),
162                fact => return Err(ReferenceFieldError::Time { node, fact }),
163            }
164        }
165        ReferenceField::Binary(node) => {
166            let location = Location {
167                snapshot: access.snapshot(),
168                node,
169            };
170            let checked = access
171                .query(node, |xml, snapshot, work| {
172                    xml.with_octets(snapshot, node, work, |_| Ok::<_, Infallible>(()))
173                })
174                .map_err(|cause| ReferenceFieldError::Binary { location, cause })?;
175            checked.unwrap_or_else(|never| match never {});
176        }
177    }
178    Ok(())
179}