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>>;