1use super::{Decoded, MaterialError, Validation};
4use crate::{
5 error::{Error, ErrorKind},
6 model::admit,
7 signature::Signature,
8 timestamp::{ISSUER, Node, Pades},
9};
10use structured_asn1::memory::{Execution, Memory, OwnedVec, Work};
11use structured_ltv::{
12 material::{ArtifactRef, ArtifactSource, Index, IndexError, Occurrence},
13 source::{Location, Query},
14};
15
16pub enum Value<'a> {
18 Certificate(structured_pkix::Certificate<'a>),
20 Crl(structured_pkix::crl::CertificateList<'a>),
22 Ocsp {
24 envelope: structured_pkix::ocsp::Response<'a>,
26 basic: Result<Option<structured_pkix::ocsp::BasicResponse<'a>>, structured_pkix::Error>,
28 },
29 Timestamp(TimestampValue<'a>),
31 Unreadable(MaterialError),
34}
35pub struct TimestampValue<'a> {
37 pub(crate) token: structured_cms::timestamp::Token<'a>,
38 pub(crate) revocations: OwnedVec<crate::timestamp::sources::Revocation<'a>>,
39}
40impl<'a> TimestampValue<'a> {
41 #[must_use]
43 pub fn token(&self) -> &structured_cms::timestamp::Token<'a> {
44 &self.token
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum Placement {
51 Dss {
53 revision: usize,
55 item: usize,
57 },
58 Cms(structured_cades::material::Placement),
60 DocumentTimestamp {
62 timestamp: usize,
64 choice: TokenChoice,
66 },
67 VriTimestamp {
69 revision: usize,
71 vri: usize,
73 choice: TokenChoice,
75 },
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub enum TokenChoice {
81 Certificate(usize),
83 Revocation(usize),
85}
86
87pub struct Parsed<'v, 's, D, G> {
90 pub(super) owner: &'v Validation<'s, D, G>,
91 pub(super) values: OwnedVec<Value<'v>, Memory>,
92 readable: bool,
93}
94
95pub type MaterialIndex<'v, D, G, W> = Index<'v, Node, D, G, Placement, (), W, Pades>;
97
98impl<'s, D: Copy + Eq, G: Copy + Eq> Validation<'s, D, G> {
99 #[expect(
103 clippy::result_large_err,
104 reason = "a terminal inner diagnostic retains its actual encoded material enclosure without allocating on the refusal path"
105 )]
106 pub fn parse<'v, W: Work + 'v>(
107 &'v self,
108 operation: &'v W,
109 ) -> Result<Parsed<'v, 's, D, G>, super::MaterialFailure> {
110 let execution = self.session.query(operation);
111 let mut values = execution.vector();
112 let mut readable = true;
113 for item in &self.items {
114 let located = |cause| super::MaterialFailure {
115 location: item.location,
116 cause,
117 };
118 let work = item.session.query(operation);
119 admit(&work, item.location, 1).map_err(|cause| located(MaterialError::Pdf(cause)))?;
120 let (value, reader) = match item.read(&work) {
121 Ok(value) => value,
122 Err(cause) if cause.is_terminal() => return Err(located(cause)),
123 Err(cause) => {
124 readable = false;
125 values.try_push(Value::Unreadable(cause)).map_err(|cause| {
126 located(MaterialError::Pdf(Error {
127 kind: ErrorKind::Resource(cause),
128 location: item.location,
129 }))
130 })?;
131 continue;
132 }
133 };
134 let value = match value {
135 Decoded::Certificate(value) => Value::Certificate(value),
136 Decoded::Crl(value) => Value::Crl(value),
137 Decoded::Ocsp(envelope) => {
138 let basic =
139 if let structured_pkix::ocsp::Response::Successful(value) = &envelope {
140 match value.basic_in(&reader) {
141 Ok(value) => Ok(value),
142 Err(cause)
143 if cause.is_resource()
144 || cause.code()
145 == structured_pkix::ErrorCode::ForeignReader =>
146 {
147 return Err(located(MaterialError::Pkix(cause)));
148 }
149 Err(cause) => {
150 readable = false;
151 Err(cause)
152 }
153 }
154 } else {
155 Ok(None)
156 };
157 Value::Ocsp { envelope, basic }
158 }
159 Decoded::Timestamp(token) => {
160 let revocations = crate::timestamp::sources::read_revocations(
161 &token,
162 &reader,
163 &work,
164 item.location,
165 )
166 .map_err(|cause| match cause {
167 crate::timestamp::sources::RevocationError::Pdf(cause) => {
168 MaterialError::Pdf(cause)
169 }
170 crate::timestamp::sources::RevocationError::Cms(cause) => {
171 MaterialError::Cms(cause)
172 }
173 crate::timestamp::sources::RevocationError::Pkix(cause) => {
174 MaterialError::Pkix(cause)
175 }
176 })
177 .map_err(located)?;
178 Value::Timestamp(TimestampValue { token, revocations })
179 }
180 };
181 values.try_push(value).map_err(|cause| {
182 located(MaterialError::Pdf(Error {
183 kind: ErrorKind::Resource(cause),
184 location: item.location,
185 }))
186 })?;
187 }
188 Ok(Parsed {
189 owner: self,
190 values: values.into_owned(),
191 readable,
192 })
193 }
194}
195
196impl<'v, D: Copy + Eq, G: Copy + Eq> Parsed<'v, '_, D, G> {
197 #[must_use]
199 pub fn all_vri_material_in_dss(&self) -> bool {
200 self.owner.all_vri_material_in_dss()
201 }
202 #[must_use]
204 pub fn values(&self) -> &[Value<'v>] {
205 &self.values
206 }
207 #[must_use]
210 pub fn all_values_readable(&self) -> bool {
211 self.readable
212 }
213
214 pub fn index<'q, 'operation, S: crate::range::RangeSource<Document = D, Generation = G>>(
217 &'q self,
218 signature: Signature<'_, '_, D, G>,
219 query: &crate::query::Query<'operation, '_, S>,
220 ) -> Result<MaterialIndex<'q, D, G, Execution<'operation>>, IndexError<Node>>
221 where
222 'v: 'q,
223 {
224 let source = signature.owner();
225 let root = Node::of(signature.location());
226 if !core::ptr::eq(source.value.session, self.owner.session)
227 || !query.belongs(self.owner.session)
228 || source.value.stamp != self.owner.stamp
229 || query.snapshot() != self.owner.stamp.snapshot
230 {
231 return Err(IndexError::Foreign { signature: root });
232 }
233 let mut index = Index::new(ISSUER, root, &[], query)?;
234 let mut ordinal = 0;
235 self.append(&mut index, &mut ordinal, query)?;
236 Ok(index)
237 }
238
239 pub(super) fn append<'q, Q, O>(
240 &'q self,
241 index: &mut Index<'q, Node, D, G, Placement, O, Q::Work, Pades>,
242 ordinal: &mut usize,
243 query: &Q,
244 ) -> Result<(), IndexError<Node>>
245 where
246 Q: Query<Id = Node, Document = D, Generation = G>,
247 'v: 'q,
248 {
249 if query.snapshot() != self.owner.stamp.snapshot {
250 return Err(IndexError::Foreign {
251 signature: index.signature(),
252 });
253 }
254 for (item_ordinal, value) in self.values.iter().enumerate() {
255 index.admit(query)?;
256 let item = &self.owner.items[item_ordinal];
257 let artifact = match value {
258 Value::Certificate(value) => Some(ArtifactRef::Certificate(value)),
259 Value::Crl(value) => Some(ArtifactRef::Crl(value)),
260 Value::Ocsp {
261 basic: Ok(Some(value)),
262 ..
263 } => Some(ArtifactRef::Ocsp(value)),
264 Value::Ocsp {
265 basic: Ok(None) | Err(_),
266 ..
267 }
268 | Value::Timestamp(_)
269 | Value::Unreadable(_) => None,
270 };
271 if let Some(artifact) = artifact {
272 index.insert(
273 ISSUER,
274 artifact,
275 Occurrence::new(
276 ArtifactSource::Material(*ordinal),
277 Location {
278 snapshot: self.owner.stamp.snapshot,
279 node: Node::dss(item.location, self.owner.revision),
280 },
281 Placement::Dss {
282 revision: self.owner.revision,
283 item: item_ordinal,
284 },
285 ),
286 query,
287 )?;
288 *ordinal = ordinal.checked_add(1).ok_or(IndexError::Resource {
289 signature: index.signature(),
290 cause: structured_asn1::memory::MemoryError::CapacityOverflow,
291 })?;
292 }
293 }
294 Ok(())
295 }
296}