Skip to main content

structured_xades/material/
checks.rs

1//! Cohesive whole-signature X.509 validation-material assessment.
2
3use super::{
4    ConditionalError, ConditionalPlacement, ExclusivePlacement, ExclusivityError, Index, Placement,
5    TimestampPlacement, TimestampPlacementError, conditional_placement, exclusivity,
6    timestamp_placement,
7};
8use crate::Xades;
9use crate::pki::CounterPaths;
10use crate::prepare::Prepared;
11use crate::xml::{Access, Xml};
12use structured_asn1::memory::Work;
13use structured_ltv::baseline::X509Material;
14use structured_ltv::material::{Incorporation, IncorporationError, artifact_uses, incorporation};
15use structured_ltv::pki::{ParticipantClosure, ParticipantRequirements, UsesError, authority_uses};
16
17/// All whole-signature X.509 presence and placement checks derived from the
18/// SAME complete participant/status closure and original artifact index.
19/// Reports preserve negative facts; no unsuccessful subset is promoted.
20///
21/// Format-specific credential data, status usability, chronology, archive
22/// coverage and application trust are separate. In particular a certificate-free
23/// credential does not make its other format-specific evidence unnecessary.
24pub struct Checks<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G> {
25    presence: Incorporation<'certificate, 'document, N, D, G, Placement<N>>,
26    conditional: ConditionalPlacement<'certificate, 'document, N, D, G>,
27    exclusive: ExclusivePlacement<'certificate, 'document, N, D, G>,
28    timestamps:
29        TimestampPlacement<'closure, 'requirements, 'view, 'certificate, 'document, N, D, G>,
30}
31
32impl<N: Copy, D: Copy, G: Copy> Checks<'_, '_, '_, '_, '_, N, D, G> {
33    /// Complete incorporated presence, including explicitly missing artifacts.
34    #[must_use]
35    pub fn presence(&self) -> &Incorporation<'_, '_, N, D, G, Placement<N>> {
36        &self.presence
37    }
38
39    /// Requirements activated by each present original validation container.
40    #[must_use]
41    pub fn conditional(&self) -> &ConditionalPlacement<'_, '_, N, D, G> {
42        &self.conditional
43    }
44
45    /// Forbidden or indeterminate exclusively credential/TSA CA copies.
46    #[must_use]
47    pub fn exclusive(&self) -> &ExclusivePlacement<'_, '_, N, D, G> {
48        &self.exclusive
49    }
50
51    /// Each actual timestamp's own validation-material placement.
52    #[must_use]
53    pub fn timestamps(&self) -> &TimestampPlacement<'_, '_, '_, '_, '_, N, D, G> {
54        &self.timestamps
55    }
56
57    /// Only complete X.509 incorporated presence and mandatory placement. This
58    /// is NOT a B-LT grade, Good status or complete credential-format evidence.
59    #[must_use]
60    pub fn all_x509_requirements_met(&self) -> bool {
61        self.presence.all_present()
62            && self.conditional.findings().is_empty()
63            && self.exclusive.findings().is_empty()
64            && self.timestamps.findings().is_empty()
65    }
66}
67
68impl<'v, 'd, N: Copy, D: Copy, G: Copy> X509Material<'v, 'd>
69    for Checks<'_, '_, 'v, '_, 'd, N, D, G>
70{
71    type Id = N;
72    type Document = D;
73    type Generation = G;
74    type Issuer = Xades;
75
76    fn requirements(&self) -> &ParticipantRequirements<'v, 'd, N, D, G, Xades> {
77        self.timestamps.inputs().requirements()
78    }
79    fn all_x509_requirements_met(&self) -> bool {
80        Checks::all_x509_requirements_met(self)
81    }
82}
83
84/// Terminal cause at its actual stage; no partial success report is returned.
85#[derive(Debug)]
86pub enum CheckError<'certificate, 'document, E, N> {
87    /// Whole-source/counter scope or container-rule refusal.
88    Conditional(ConditionalError<E, N>),
89    /// Original authority attribution or artifact-census refusal.
90    Uses(UsesError<N>),
91    /// Original incorporated-value lookup refusal.
92    Presence(IncorporationError<N>),
93    /// Original certificate classification or exclusive-container refusal.
94    Exclusive(ExclusivityError<'certificate, 'document, N>),
95    /// Original per-token authority/placement refusal.
96    Timestamp(TimestampPlacementError<N>),
97}
98
99/// Whole-signature material result in the XML owner's identity/error domain.
100pub type CheckResult<'closure, 'requirements, 'view, 'certificate, 'document, X> = Result<
101    Checks<
102        'closure,
103        'requirements,
104        'view,
105        'certificate,
106        'document,
107        <X as Xml>::Id,
108        <X as Xml>::Document,
109        <X as Xml>::Generation,
110    >,
111    CheckError<'certificate, 'document, <X as Xml>::Error, <X as Xml>::Id>,
112>;
113
114/// Assess all whole-signature X.509 material rules in one path. The original
115/// prepared/counter/source/scope guards run first, then one purpose/artifact
116/// census is reused for presence and exclusivity. Per-TSA placement uses the
117/// same closure's actual roots, never a caller-written successful-token list.
118///
119/// EN319132-1 V1.3.1 §§5.4.2–5.4.6 and6.3(x/cc) distinguish presence, particular
120/// mandatory containers, exclusively used CA data and each timestamp's data.
121/// No one of these predicates implies the others. Processing/resource refusal
122/// is terminal; completed missing/misplaced/indeterminate facts remain in the
123/// report. This does not repeat parsing, crypto, path or status verification.
124/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
125pub fn check_participants<
126    'closure,
127    'requirements,
128    'view,
129    'certificate,
130    'document,
131    X: Xml,
132    W: Work,
133    Q: Work,
134>(
135    prepared: &Prepared<X>,
136    inputs: &'closure ParticipantClosure<
137        'requirements,
138        'view,
139        'certificate,
140        'document,
141        X::Id,
142        X::Document,
143        X::Generation,
144        Xades,
145    >,
146    counters: &CounterPaths<'_, 'document, X::Id, X::Document, X::Generation>,
147    index: &Index<'_, X::Id, X::Document, X::Generation, W>,
148    access: &Access<'_, X, Q>,
149) -> CheckResult<'closure, 'requirements, 'view, 'certificate, 'document, X> {
150    let conditional = conditional_placement(prepared, inputs, counters, index, access)
151        .map_err(CheckError::Conditional)?;
152    let purposes = authority_uses(inputs, access).map_err(CheckError::Uses)?;
153    let artifacts = artifact_uses(&purposes, access).map_err(CheckError::Uses)?;
154    let presence = incorporation(&artifacts, index, access).map_err(CheckError::Presence)?;
155    let exclusive = exclusivity(&artifacts, index, access).map_err(CheckError::Exclusive)?;
156    let timestamps = timestamp_placement(inputs, index, access).map_err(CheckError::Timestamp)?;
157    Ok(Checks {
158        presence,
159        conditional,
160        exclusive,
161        timestamps,
162    })
163}