1use crate::binary::{self, Located, LocatedValue, Location, OwnedBinaryXml, OwnedDecoded};
4use crate::container::{DS, XADES};
5use crate::header;
6use crate::xml::{Access, AccessError, Xml};
7use structured_asn1::{Encoding, Reader};
8use structured_pkix::{Certificate, crl::CertificateList, ocsp::Response};
9
10mod catalogue;
11mod checks;
12pub use checks::{CheckError, CheckResult, Checks, check_participants};
13mod conditional;
14pub use conditional::{
15 ConditionalError, ConditionalFailure, ConditionalFinding, ConditionalPlacement,
16 ConditionalResult, conditional_placement,
17};
18mod counters;
19pub mod credentials;
20pub use catalogue::{Catalogue, Entry, read};
21mod pipeline;
22pub use pipeline::{
23 Basic, Error as PipelineError, Opaque, PipelineResult, Placement, Retained, View,
24 with_counter_values, with_values,
25};
26pub(crate) mod exclusivity;
27#[cfg(test)]
28mod fixture;
29mod index;
30pub use index::{Archived, Index, IndexResult, Occurrence, index, index_with_tokens};
31mod timestamp_placement;
32pub use exclusivity::{
33 ExclusiveFinding, ExclusiveFindingKind, ExclusivePlacement, ExclusivityError,
34 ExclusivityResult, exclusivity,
35};
36pub use timestamp_placement::{
37 TimestampPlacement, TimestampPlacementError, TimestampPlacementFailure,
38 TimestampPlacementFinding, TimestampPlacementResult, timestamp_placement,
39};
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43pub enum Kind {
44 Certificate,
46 Crl,
48 Ocsp,
50}
51
52impl Kind {
53 fn local(self) -> &'static str {
54 match self {
55 Self::Certificate => "EncapsulatedX509Certificate",
56 Self::Crl => "EncapsulatedCRLValue",
57 Self::Ocsp => "EncapsulatedOCSPValue",
58 }
59 }
60}
61
62#[derive(Debug)]
64pub struct Source<N, D, G> {
65 kind: Kind,
66 original: Located<OwnedDecoded, N, D, G>,
67}
68
69pub type Material<X> = Source<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
71
72#[derive(Debug)]
74pub enum RetainError<E, N> {
75 HeaderIndex {
77 signature: N,
79 index: usize,
81 },
82 Access(AccessError<E, N>),
84 Name {
86 node: N,
88 kind: Kind,
90 },
91}
92
93pub type RetainResult<T, X> = Result<T, RetainError<<X as Xml>::Error, <X as Xml>::Id>>;
95
96#[derive(Debug)]
100#[expect(
101 clippy::large_enum_variant,
102 reason = "fixed-size parsed value avoids an extra allocation per material; any retained collection admits its full enum capacity"
103)]
104pub enum Value<'source> {
105 Certificate(Certificate<'source>),
107 Crl(CertificateList<'source>),
109 Ocsp(Response<'source>),
111}
112
113#[derive(Debug)]
115pub enum Cause {
116 Encoding(structured_asn1::Error),
118 Pki(structured_pkix::Error),
120 Empty {
122 offset: u64,
124 },
125 Trailing {
127 offset: u64,
129 },
130}
131
132pub type ReadResult<'source, N, D, G> =
134 Result<Located<Value<'source>, N, D, G>, Located<Cause, N, D, G>>;
135
136impl<N: Copy, D: Copy, G: Copy> Source<N, D, G> {
137 #[must_use]
139 pub fn kind(&self) -> Kind {
140 self.kind
141 }
142
143 #[must_use]
145 pub fn location(&self) -> Location<N, D, G> {
146 self.original.location
147 }
148
149 #[must_use]
151 pub fn bytes(&self) -> &[u8] {
152 &self.original.value.bytes
153 }
154
155 pub fn read_in<'source>(
161 &'source self,
162 operation: &Reader<'source>,
163 ) -> ReadResult<'source, N, D, G> {
164 let read = || {
165 let mut reader = self
166 .original
167 .value
168 .reader_in(operation, Encoding::Der)
169 .map_err(Cause::Encoding)?;
170 let element = reader
171 .next_element()
172 .map_err(Cause::Encoding)?
173 .ok_or(Cause::Empty {
174 offset: reader.offset(),
175 })?;
176 let value = match self.kind {
177 Kind::Certificate => Value::Certificate(
178 Certificate::read_element(&reader, &element).map_err(Cause::Pki)?,
179 ),
180 Kind::Crl => Value::Crl(
181 CertificateList::read_element(&reader, &element).map_err(Cause::Pki)?,
182 ),
183 Kind::Ocsp => {
184 Value::Ocsp(Response::read_element(&reader, &element).map_err(Cause::Pki)?)
185 }
186 };
187 if !reader.is_empty() {
188 return Err(Cause::Trailing {
189 offset: reader.offset(),
190 });
191 }
192 Ok(value)
193 };
194 match read() {
195 Ok(value) => Ok(Located {
196 location: self.location(),
197 value,
198 }),
199 Err(value) => Err(Located {
200 location: self.location(),
201 value,
202 }),
203 }
204 }
205}
206
207pub fn retain<X: OwnedBinaryXml>(
212 xml: &X,
213 location: Location<X::Id, X::Document, X::Generation>,
214 kind: Kind,
215 operation: &Reader<'_>,
216) -> RetainResult<Material<X>, X> {
217 retain_expected(xml, location, kind, XADES, kind.local(), operation)
218}
219
220pub fn retain_header<X: OwnedBinaryXml>(
226 xml: &X,
227 header: &header::Reading<X>,
228 index: usize,
229 operation: &Reader<'_>,
230) -> RetainResult<Material<X>, X> {
231 let execution = operation.execution();
232 let access = Access::new(xml, header.snapshot(), &execution);
233 access
234 .query(header.signature(), |_, _, _| Ok(()))
235 .map_err(RetainError::Access)?;
236 let Some(&node) = header.certificates().get(index) else {
237 return Err(RetainError::HeaderIndex {
238 signature: header.signature(),
239 index,
240 });
241 };
242 retain_expected(
243 xml,
244 Location {
245 snapshot: header.snapshot(),
246 node,
247 },
248 Kind::Certificate,
249 DS,
250 "X509Certificate",
251 operation,
252 )
253}
254
255fn retain_expected<X: OwnedBinaryXml>(
256 xml: &X,
257 location: Location<X::Id, X::Document, X::Generation>,
258 kind: Kind,
259 namespace: &str,
260 local: &str,
261 operation: &Reader<'_>,
262) -> RetainResult<Material<X>, X> {
263 let execution = operation.execution();
264 let access = Access::new(xml, location.snapshot, &execution);
265 if !access
266 .has_name(location.node, crate::xml::Name { namespace, local })
267 .map_err(RetainError::Access)?
268 {
269 return Err(RetainError::Name {
270 node: location.node,
271 kind,
272 });
273 }
274 let original: LocatedValue<OwnedDecoded, X> =
275 binary::owned_source(xml, location, operation).map_err(RetainError::Access)?;
276 Ok(Source { kind, original })
277}