Skip to main content

structured_xades/validation/
ref_crl_value.rs

1//! Bind optional CRL identifier fields and mandatory digest to one original list.
2
3use super::{
4    CrlReferenceError, CrlReferenceIdentity, ReferenceDigest, ReferenceDigestError, ReferenceKind,
5    ReferenceProperty, ReferenceTarget, ReferenceTime, check_crl_reference,
6    compare_reference_target,
7};
8use crate::{
9    binary::{self, BinaryXml, Located},
10    certificate::{Agreement, DigestProvider},
11    properties::CalendarXml,
12    scalar::TextXml,
13    xml::{Access, AccessError, Xml},
14};
15use structured_asn1::ScopedReader;
16use structured_pkix::crl::CertificateList;
17
18/// One original row's identifier and digest against the SAME actual CRL.
19/// Private construction retains the source pair for subsequent composition;
20/// a caller cannot substitute another list after the checks have completed.
21pub struct CrlValueReference<'references, 'actual, 'document, N, D, G> {
22    references: &'references ReferenceProperty<N, D, G>,
23    entry: usize,
24    actual: &'actual CertificateList<'document>,
25    identity: Option<Located<CrlReferenceIdentity<N>, N, D, G>>,
26    digest: Option<ReferenceDigest>,
27}
28
29impl<'r, 'a, 'd, N: Copy, D: Copy, G: Copy> CrlValueReference<'r, 'a, 'd, N, D, G> {
30    /// Original reference property, including its actual role and generation.
31    #[must_use]
32    pub fn references(&self) -> &'r ReferenceProperty<N, D, G> {
33        self.references
34    }
35
36    /// Original row ordinal within that property.
37    #[must_use]
38    pub fn entry(&self) -> usize {
39        self.entry
40    }
41
42    /// Same complete/delta CRL whose original DER supplied the digest input.
43    #[must_use]
44    pub fn actual(&self) -> &'a CertificateList<'d> {
45        self.actual
46    }
47
48    /// Written original identifier and all field facts, or legitimate absence.
49    #[must_use]
50    pub fn identity(&self) -> Option<&Located<CrlReferenceIdentity<N>, N, D, G>> {
51        self.identity.as_ref()
52    }
53
54    /// Whole-list comparison. None means malformed/unsupported mandatory time
55    /// syntax prevented provider dispatch, not a successful absent digest.
56    #[must_use]
57    pub fn digest(&self) -> Option<ReferenceDigest> {
58        self.digest
59    }
60
61    /// Only agreement with this one CRL: mandatory digest plus every written
62    /// issuer/time field. An absent optional identifier may be inferred from
63    /// the digest; an unstated timezone does not prove time equality.
64    /// This is not signature authentication, status applicability, actual path
65    /// use, complete/delta coverage, incorporation or a baseline grade.
66    #[must_use]
67    pub fn agrees(&self) -> bool {
68        if self.digest != Some(ReferenceDigest::Compared(Agreement::Matches)) {
69            return false;
70        }
71        if let Some(identity) = &self.identity {
72            return identity.value.issuer_matches()
73                && identity.value.issued_at() == ReferenceTime::Matches;
74        }
75        true
76    }
77}
78
79/// Exact original processing stage; resource/source refusals never disagree.
80#[derive(Debug)]
81pub enum CrlValueError<E, PE, N, D, G> {
82    /// Original source/generation/current-query refusal.
83    Access(AccessError<E, N>),
84    /// Caller selected an ordinal outside the original property.
85    Index {
86        /// Requested row ordinal, not a document byte offset.
87        index: usize,
88    },
89    /// Existing original issuer/time interpreter refused.
90    Identity(CrlReferenceError<E, N>),
91    /// Existing whole-list digest/source/provider interpreter refused.
92    Digest(binary::Error<E, ReferenceDigestError<PE>, N, D, G>),
93}
94
95/// Complete source-paired CRL facts or the unchanged terminal cause.
96pub type CrlValueResult<'r, 'a, 'd, X, PE> = Result<
97    Option<
98        CrlValueReference<'r, 'a, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
99    >,
100    CrlValueError<
101        <X as Xml>::Error,
102        PE,
103        <X as Xml>::Id,
104        <X as Xml>::Document,
105        <X as Xml>::Generation,
106    >,
107>;
108
109/// Compose Annex A.1.2's mandatory DER-list digest and optional `CRLIdentifier`
110/// through the SAME existing digest/name/calendar kernels and current query.
111/// No parse, serialization, signature verification or acquisition repeats.
112/// A different reference kind returns None, never successful agreement.
113///
114/// EN319132-1 A.1.2 permits omission of `CRLIdentifier` if other information
115/// identifies the list. A written identifier still requires issuer/issue time;
116/// its Number and URI remain hints. Readable mismatch retains the independent
117/// digest fact; malformed/unsupported time prevents provider side effects.
118/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
119pub fn check_crl_value_reference<'r, 'a, 'd, 'q, X, P>(
120    xml: &X,
121    references: &'r ReferenceProperty<X::Id, X::Document, X::Generation>,
122    index: usize,
123    actual: &'a CertificateList<'d>,
124    provider: &'q P,
125    operation: &ScopedReader<'_, '_>,
126) -> CrlValueResult<'r, 'a, 'd, X, P::Error>
127where
128    X: BinaryXml + CalendarXml + TextXml,
129    P: DigestProvider<'q, X>,
130{
131    let work = operation.execution();
132    let access = Access::new(xml, references.snapshot(), &work);
133    access
134        .query(references.node(), |_, _, _| Ok(()))
135        .map_err(CrlValueError::Access)?;
136    let Some(entry) = references.entries().get(index) else {
137        return Err(CrlValueError::Index { index });
138    };
139    if entry.kind() != ReferenceKind::Crl {
140        return Ok(None);
141    }
142    let identity = check_crl_reference(xml, references, index, actual, operation)
143        .map_err(CrlValueError::Identity)?;
144    let digest = match identity.as_ref() {
145        Some(identity)
146            if matches!(
147                identity.value.issued_at(),
148                ReferenceTime::Malformed | ReferenceTime::Unsupported
149            ) =>
150        {
151            None
152        }
153        _ => Some(
154            compare_reference_target(
155                xml,
156                references,
157                index,
158                ReferenceTarget::Crl(actual),
159                provider,
160                &work,
161            )
162            .map_err(CrlValueError::Digest)?,
163        ),
164    };
165    access
166        .query(entry.node(), |_, _, _| Ok(()))
167        .map_err(CrlValueError::Access)?;
168    Ok(Some(CrlValueReference {
169        references,
170        entry: index,
171        actual,
172        identity,
173        digest,
174    }))
175}