Skip to main content

structured_xades/material/credentials/
mod.rs

1//! Format-owned credential material under original whole/archive coverage.
2//!
3//! X.509 issuer paths do not describe every dependency of an attribute
4//! certificate or signed assertion. The existing format authenticator's receipt
5//! supplies those inputs; this module checks their original incorporation.
6
7mod check;
8mod scope;
9mod selection;
10
11pub use check::check;
12pub use scope::Scope;
13
14use crate::binary::Location;
15use crate::credential::{Checked, Credential, ParticipantCredentials};
16use crate::reference::VisitError;
17use crate::xml::{Access, AccessError, Snapshot, Xml};
18use structured_asn1::memory::{MemoryError, OwnedVec, Work};
19use structured_ltv::baseline::CredentialMaterial;
20use structured_ltv::material::{ArchiveError, ArtifactRef, IndexError};
21use structured_ltv::timestamp::{ObligationError, TimestampScope};
22
23/// One actual format dependency. Borrowed binary artifacts are queried during
24/// the callback and cannot escape it; no DER copy or fingerprint substitutes.
25#[derive(Clone, Copy)]
26pub enum Need<'value, 'document, N, D, G> {
27    /// Original known certificate, whole CRL or authenticated basic response.
28    Known(ArtifactRef<'value, 'document>),
29    /// Exact original OtherCertificate/OtherValue catalogue member. The format
30    /// owner establishes its meaning and agreement before visiting this need.
31    Open(Location<N, D, G>),
32    /// A required format-specific value has no matching original open member.
33    /// Discovery can be complete while incorporated material is missing.
34    Missing,
35}
36
37/// Complete format dependency discovery and interpretation, independent of
38/// finding an original incorporated copy. Resource failures are always Err.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum Completeness {
41    /// All actual dependencies were visited and their format semantics checked.
42    Complete,
43    /// This format handler cannot establish the required material semantics.
44    Unsupported,
45    /// Discovery/interpretation is still incomplete; an empty prefix is no proof.
46    Unprocessed,
47    /// Original required material is readable but not usable for this credential.
48    Unusable,
49    /// Original required material violates its selected format syntax.
50    Malformed,
51}
52
53/// Same format owner as credential authentication, operating on its actual
54/// retained receipt A, WITHOUT authenticating the credential again. It visits
55/// every required input, including the attribute certificate's own status;
56/// returning no inputs is Complete only if that format actually needs none.
57/// `CertificateFree` is never an implicit implementation of this capability.
58///
59/// EN319132-1 V1.3.1 ยงยง5.2.6/5.4.4/5.4.5/6.3(aa) leave concrete assertion and
60/// other credential formats with their owners while requiring their validation
61/// material. A X.509 issuer's status does not substitute for credential status.
62/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
63pub trait Provider<X: Xml, A> {
64    /// Original format/processing/resource cause, not a negative completeness fact.
65    type Error;
66
67    /// Visit dependencies of THIS authenticated original credential once. Source
68    /// decoding, semantic/status checks and every traversal/copy/allocation use
69    /// the supplied current Access/Work before execution. Do not reparse XML,
70    /// reverify the credential, acquire sources implicitly or drop a callback
71    /// refusal. The common caller independently preserves that first refusal.
72    fn visit<VE, W: Work>(
73        &self,
74        credential: &Checked<'_, '_, A, X::Id, X::Document, X::Generation>,
75        open: &[super::Opaque<X::Id, X::Document, X::Generation>],
76        access: &Access<'_, X, W>,
77        visitor: impl for<'value, 'document> FnMut(
78            Need<'value, 'document, X::Id, X::Document, X::Generation>,
79        ) -> Result<(), VE>,
80    ) -> Result<Completeness, VisitError<Self::Error, VE>>;
81}
82
83/// No incorporated copy, or only copies outside the selected earlier archive.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum Missing {
86    /// No exact original incorporated artifact/open catalogue member exists.
87    Absent,
88    /// Exact copies exist, but this archive's original input covers none of them.
89    Uncovered,
90}
91
92/// Original format dependency ordinal, without retaining callback-local DER.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct Finding {
95    /// Zero-based ordinal in this handler's complete dependency visitation.
96    pub dependency: usize,
97    /// Missing versus outside this original archive's coverage.
98    pub cause: Missing,
99}
100
101/// One actual authenticated credential and all its original material findings.
102pub struct Entry<N, D, G> {
103    source: Credential<N, D, G>,
104    completeness: Completeness,
105    findings: OwnedVec<Finding>,
106}
107
108impl<N: Copy, D: Copy, G: Copy> Entry<N, D, G> {
109    /// Original signed credential, not a selected issuer certificate.
110    #[must_use]
111    pub fn source(&self) -> Credential<N, D, G> {
112        self.source
113    }
114    /// Original format interpretation, distinct from copy availability.
115    #[must_use]
116    pub fn completeness(&self) -> Completeness {
117        self.completeness
118    }
119    /// Missing dependencies in original visitation order.
120    #[must_use]
121    pub fn findings(&self) -> &[Finding] {
122        &self.findings
123    }
124}
125
126/// Every actual credential in the source-derived whole or earlier-archive scope.
127/// Borrowing the authenticated catalogue preserves its original receipt identity;
128/// retained findings own only admitted metadata, never temporary parsed values.
129pub struct Report<'auth, 'path, 'document, A, N, D, G> {
130    credentials: &'auth ParticipantCredentials<'path, 'document, A, N, D, G>,
131    scope: TimestampScope<N, D, G>,
132    entries: OwnedVec<Entry<N, D, G>>,
133    complete: bool,
134}
135
136impl<'auth, 'path, 'document, A, N: Copy, D: Copy, G: Copy>
137    Report<'auth, 'path, 'document, A, N, D, G>
138{
139    /// Same original independently authenticated credential census.
140    #[must_use]
141    pub fn credentials(&self) -> &'auth ParticipantCredentials<'path, 'document, A, N, D, G> {
142        self.credentials
143    }
144    /// Exact original whole-signature or earlier-archive requirement scope.
145    #[must_use]
146    pub fn scope(&self) -> TimestampScope<N, D, G> {
147        self.scope
148    }
149    /// Every original required credential once, including negative outcomes.
150    #[must_use]
151    pub fn entries(&self) -> &[Entry<N, D, G>] {
152        &self.entries
153    }
154    /// Only format-specific material interpretation and incorporation. This
155    /// does not replace X.509 path/status, timestamp or application trust checks.
156    #[must_use]
157    pub fn all_requirements_met(&self) -> bool {
158        self.complete
159    }
160    /// Original source generation of the authenticated participant set.
161    #[must_use]
162    pub fn snapshot(&self) -> Snapshot<D, G> {
163        self.credentials.snapshot()
164    }
165}
166
167impl<'path, 'document, A, N: Copy, D: Copy, G: Copy> CredentialMaterial
168    for Report<'_, 'path, 'document, A, N, D, G>
169{
170    type Id = N;
171    type Document = D;
172    type Generation = G;
173    type Credentials = ParticipantCredentials<'path, 'document, A, N, D, G>;
174
175    fn scope(&self) -> TimestampScope<N, D, G> {
176        self.scope
177    }
178    fn snapshot(&self) -> Snapshot<D, G> {
179        self.credentials.snapshot()
180    }
181    fn credentials(&self) -> &Self::Credentials {
182        self.credentials
183    }
184    fn all_requirements_met(&self) -> bool {
185        self.complete
186    }
187}
188
189/// Terminal source, scope, callback or resource failure. No successful partial
190/// report is returned, and external handler side effects cannot be rolled back.
191#[derive(Debug)]
192pub enum Error<E, PE, N> {
193    /// Foreign source, signature, incomplete counter census or archive binding.
194    Foreign {
195        /// Original enclosing node.
196        node: N,
197    },
198    /// Original XML/current-operation/source refusal.
199    Access(AccessError<E, N>),
200    /// Original scope/membership query refusal.
201    Obligation(ObligationError<N>),
202    /// Original known artifact lookup refusal.
203    Index(IndexError<N>),
204    /// Original archive evidence/coverage/lookup refusal.
205    Archive(ArchiveError<N>),
206    /// Original credential format handler refused.
207    Provider {
208        /// Original credential payload.
209        node: N,
210        /// Unchanged processing/resource cause.
211        cause: PE,
212    },
213    /// Admission failed before dependent traversal or report growth.
214    Resource {
215        /// Original selecting credential or enclosing root.
216        node: N,
217        /// Original memory/work refusal.
218        cause: MemoryError,
219    },
220    /// The handler returned a visitor error without invoking/refusing its visitor.
221    CallbackProtocol {
222        /// Original credential payload.
223        node: N,
224    },
225}
226
227/// Original typed credential-material report or terminal refusal.
228pub type CheckResult<'auth, 'path, 'document, X, A, PE> = Result<
229    Report<
230        'auth,
231        'path,
232        'document,
233        A,
234        <X as Xml>::Id,
235        <X as Xml>::Document,
236        <X as Xml>::Generation,
237    >,
238    Error<<X as Xml>::Error, PE, <X as Xml>::Id>,
239>;
240
241pub(super) type ProcessingResult<T, X, PE> =
242    Result<T, Error<<X as Xml>::Error, PE, <X as Xml>::Id>>;