1use super::counters::collect_counters;
4use super::{Cause, Entry, RetainError, Source, Value};
5use crate::binary::{Located, Location, OwnedBinaryXml};
6use crate::prepare::Prepared;
7use crate::unsigned;
8use crate::xml::{Access, AccessError, Xml};
9use structured_asn1::{
10 Reader,
11 memory::{HashWork, MemoryError, OwnedVec, Work},
12};
13use structured_pkix::ocsp::{BasicResponse, Response};
14
15#[derive(Debug, Clone, Copy)]
17pub enum Placement<N> {
18 KeyInfo(N),
21 Signature(N),
23 Attribute(N),
25 General(N),
27 Timestamp(N),
29 TimestampToken {
32 property: N,
34 token: N,
36 role: crate::timestamp::Role,
38 },
39}
40
41pub struct Retained<N, D, G> {
43 placement: Placement<N>,
44 source: Source<N, D, G>,
45}
46
47impl<N: Copy, D: Copy, G: Copy> Retained<N, D, G> {
48 #[must_use]
50 pub fn placement(&self) -> Placement<N> {
51 self.placement
52 }
53 #[must_use]
55 pub fn source(&self) -> &Source<N, D, G> {
56 &self.source
57 }
58}
59
60#[derive(Debug, Clone, Copy)]
62pub struct Opaque<N, D, G> {
63 pub placement: Placement<N>,
65 pub location: Location<N, D, G>,
67 pub entry: Entry<N>,
69}
70
71pub struct Basic<'source, N, D, G> {
74 source_index: usize,
75 location: Location<N, D, G>,
76 response: BasicResponse<'source>,
77}
78
79impl<'source, N: Copy, D: Copy, G: Copy> Basic<'source, N, D, G> {
80 #[must_use]
82 pub fn source_index(&self) -> usize {
83 self.source_index
84 }
85 #[must_use]
87 pub fn location(&self) -> Location<N, D, G> {
88 self.location
89 }
90 #[must_use]
92 pub fn response(&self) -> &BasicResponse<'source> {
93 &self.response
94 }
95}
96
97pub struct View<'source, N, D, G> {
101 pub(super) snapshot: crate::xml::Snapshot<D, G>,
102 pub(super) signature: N,
103 pub(super) sources: &'source [Retained<N, D, G>],
104 pub(super) values: &'source [Located<Value<'source>, N, D, G>],
105 opaque: &'source [Opaque<N, D, G>],
106 pub(super) basic: &'source [Basic<'source, N, D, G>],
107}
108
109impl<'source, N, D, G> View<'source, N, D, G> {
110 #[must_use]
112 pub fn sources(&self) -> &'source [Retained<N, D, G>] {
113 self.sources
114 }
115 #[must_use]
117 pub fn values(&self) -> &'source [Located<Value<'source>, N, D, G>] {
118 self.values
119 }
120 #[must_use]
122 pub fn opaque(&self) -> &'source [Opaque<N, D, G>] {
123 self.opaque
124 }
125 #[must_use]
129 pub fn basic_responses(&self) -> &'source [Basic<'source, N, D, G>] {
130 self.basic
131 }
132}
133
134impl<N: Copy, D: Copy, G: Copy> View<'_, N, D, G> {
135 #[must_use]
137 pub fn snapshot(&self) -> crate::xml::Snapshot<D, G> {
138 self.snapshot
139 }
140
141 #[must_use]
143 pub fn signature(&self) -> N {
144 self.signature
145 }
146}
147
148#[derive(Debug)]
150pub enum Error<E, VE, N, D, G> {
151 Access(AccessError<E, N>),
153 Property(crate::properties::Error<E, N>),
155 Validation(crate::validation::Error<E, N>),
157 Retain {
159 location: Location<N, D, G>,
161 cause: RetainError<E, N>,
163 },
164 Parse(Located<Cause, N, D, G>),
166 Foreign {
168 node: N,
170 },
171 Resource {
173 node: N,
175 cause: MemoryError,
177 },
178 Visitor(VE),
180}
181
182pub type PipelineResult<T, X, VE> = Result<
184 T,
185 Error<<X as Xml>::Error, VE, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
186>;
187
188pub(super) type Record<X> = Retained<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
189pub(super) type Open<X> = Opaque<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
190type Basics<'source, X, W> =
191 OwnedVec<Basic<'source, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>, W>;
192
193pub fn with_values<X: OwnedBinaryXml, R, VE>(
212 xml: &X,
213 prepared: &Prepared<X>,
214 operation: &Reader<'_>,
215 visitor: impl for<'source> FnOnce(
216 &Reader<'source>,
217 View<'source, X::Id, X::Document, X::Generation>,
218 ) -> Result<R, VE>,
219) -> PipelineResult<R, X, VE> {
220 with_inputs(xml, prepared, None, operation, visitor)
221}
222
223pub fn with_counter_values<X: OwnedBinaryXml, R, VE>(
244 xml: &X,
245 prepared: &Prepared<X>,
246 counters: &CounterSources<'_, '_, X>,
247 operation: &Reader<'_>,
248 visitor: impl for<'source> FnOnce(
249 &Reader<'source>,
250 View<'source, X::Id, X::Document, X::Generation>,
251 ) -> Result<R, VE>,
252) -> PipelineResult<R, X, VE> {
253 with_inputs(xml, prepared, Some(counters), operation, visitor)
254}
255
256pub(super) type CounterSources<'view, 'document, X> = crate::pki::CounterPaths<
257 'view,
258 'document,
259 <X as Xml>::Id,
260 <X as Xml>::Document,
261 <X as Xml>::Generation,
262>;
263
264fn with_inputs<X: OwnedBinaryXml, R, VE>(
265 xml: &X,
266 prepared: &Prepared<X>,
267 counters: Option<&CounterSources<'_, '_, X>>,
268 operation: &Reader<'_>,
269 visitor: impl for<'source> FnOnce(
270 &Reader<'source>,
271 View<'source, X::Id, X::Document, X::Generation>,
272 ) -> Result<R, VE>,
273) -> PipelineResult<R, X, VE> {
274 let layout = prepared.bound().containers();
275 let execution = operation.execution();
276 let access = Access::new(xml, layout.snapshot(), &execution);
277 admit(&access, layout.signature())?;
278 if let Some(counters) = counters
279 && (counters.snapshot() != layout.snapshot()
280 || !access
281 .equal_id(counters.signature(), layout.signature())
282 .map_err(|cause| Error::Resource {
283 node: layout.signature(),
284 cause,
285 })?)
286 {
287 return Err(Error::Foreign {
288 node: counters.signature(),
289 });
290 }
291 let mut sources = execution.vector();
292 let mut opaque = execution.vector();
293 collect_header(xml, &access, prepared.header(), operation, &mut sources)?;
294 let parents = material_parents(&access, prepared)?;
295 for index in 0..prepared.material().len() {
296 admit(&access, layout.signature())?;
297 let catalogue = &prepared.material()[index];
298 let node = catalogue.parent();
299 let placement = source_placement(parents, node, &execution)
300 .map_err(|cause| Error::Resource { node, cause })?
301 .ok_or(Error::Foreign { node })?;
302 collect(
303 xml,
304 &access,
305 catalogue,
306 placement,
307 operation,
308 &mut sources,
309 &mut opaque,
310 )?;
311 }
312 for index in 0..prepared.validation().len() {
313 admit(&access, layout.signature())?;
314 let data = &prepared.validation()[index];
315 if data.snapshot() != layout.snapshot()
316 || !access
317 .equal_id(data.signature(), layout.signature())
318 .map_err(|cause| Error::Resource {
319 node: data.node(),
320 cause,
321 })?
322 {
323 return Err(Error::Foreign { node: data.node() });
324 }
325 collect_validation(xml, &access, data, operation, &mut sources, &mut opaque)?;
326 }
327 if let Some(counters) = counters {
328 collect_counters(xml, &access, counters, operation, &mut sources, &mut opaque)?;
329 }
330 let mut values = execution.vector();
331 for index in 0..sources.len() {
332 admit(&access, layout.signature())?;
333 let source = &sources[index].source;
334 let value = source.read_in(operation).map_err(Error::Parse)?;
335 values.try_push(value).map_err(|cause| Error::Resource {
336 node: source.location().node,
337 cause,
338 })?;
339 }
340 let basic = read_basics(&access, layout.signature(), &values, operation, &execution)?;
341 admit(&access, layout.signature())?;
342 let result = visitor(
343 operation,
344 View {
345 snapshot: layout.snapshot(),
346 signature: layout.signature(),
347 sources: &sources,
348 values: &values,
349 opaque: &opaque,
350 basic: &basic,
351 },
352 )
353 .map_err(Error::Visitor)?;
354 admit(&access, layout.signature())?;
357 Ok(result)
358}
359
360fn read_basics<'source, X: Xml, W: Work, VE>(
361 access: &Access<'_, X, W>,
362 signature: X::Id,
363 values: &[crate::binary::LocatedValue<Value<'source>, X>],
364 operation: &Reader<'source>,
365 execution: &W,
366) -> PipelineResult<Basics<'source, X, W>, X, VE> {
367 let mut basic = execution.vector();
368 let mut cursor = 0;
369 while cursor < values.len() {
370 admit(access, signature)?;
371 let index = cursor;
372 cursor += 1;
373 let value = &values[index];
374 let Value::Ocsp(Response::Successful(envelope)) = &value.value else {
375 continue;
376 };
377 if let Some(response) = envelope.basic_in(operation).map_err(|cause| {
378 Error::Parse(Located {
379 location: value.location,
380 value: Cause::Pki(cause),
381 })
382 })? {
383 basic
384 .try_push(Basic {
385 source_index: index,
386 location: value.location,
387 response,
388 })
389 .map_err(|cause| Error::Resource {
390 node: value.location.node,
391 cause,
392 })?;
393 }
394 }
395 Ok(basic)
396}
397
398fn collect_header<X: OwnedBinaryXml, W: Work, VE>(
399 xml: &X,
400 access: &Access<'_, X, W>,
401 header: &crate::header::Reading<X>,
402 operation: &Reader<'_>,
403 sources: &mut OwnedVec<Record<X>, W>,
404) -> PipelineResult<(), X, VE> {
405 collect_data(
410 xml,
411 access,
412 header.signature(),
413 header.x509_data(),
414 operation,
415 sources,
416 )
417}
418
419pub(super) fn collect_data<X: OwnedBinaryXml, W: Work, VE>(
420 xml: &X,
421 access: &Access<'_, X, W>,
422 signature: X::Id,
423 data: X::Id,
424 operation: &Reader<'_>,
425 sources: &mut OwnedVec<Record<X>, W>,
426) -> PipelineResult<(), X, VE> {
427 crate::header::visit_values(access, data, |node, kind| {
428 admit(access, signature)?;
429 let location = Location {
430 snapshot: access.snapshot(),
431 node,
432 };
433 let schema = match kind {
434 crate::header::ValueKind::Certificate => super::Kind::Certificate,
435 crate::header::ValueKind::Crl => super::Kind::Crl,
436 };
437 let source = super::retain_expected(
438 xml,
439 location,
440 schema,
441 crate::container::DS,
442 kind.local(),
443 operation,
444 )
445 .map_err(|cause| Error::Retain { location, cause })?;
446 sources
447 .try_push(Retained {
448 placement: Placement::KeyInfo(signature),
449 source,
450 })
451 .map_err(|cause| Error::Resource { node, cause })?;
452 Ok(())
453 })
454 .map_err(|cause| match cause {
455 crate::reference::VisitError::Processing(cause) => Error::Property(cause),
456 crate::reference::VisitError::Visitor(cause) => cause,
457 })
458}
459
460fn material_parents<X: Xml, W: Work, VE>(
461 access: &Access<'_, X, W>,
462 prepared: &Prepared<X>,
463) -> PipelineResult<[Option<X::Id>; 4], X, VE> {
464 let mut parents = [None; 4];
465 for index in 0..prepared.unsigned().entries().len() {
466 admit(access, prepared.unsigned().signature())?;
467 let entry = prepared.unsigned().entries()[index];
468 let slot = match entry.role {
469 unsigned::Role::Certificates => 0,
470 unsigned::Role::Revocation => 1,
471 unsigned::Role::AttributeCertificates => 2,
472 unsigned::Role::AttributeRevocation => 3,
473 _ => continue,
474 };
475 parents[slot] = Some(entry.node);
476 }
477 Ok(parents)
478}
479
480pub(super) fn collect<X: OwnedBinaryXml, W: Work, VE>(
481 xml: &X,
482 access: &Access<'_, X, W>,
483 catalogue: &super::Catalogue<X::Id, X::Document, X::Generation>,
484 placement: Placement<X::Id>,
485 operation: &Reader<'_>,
486 sources: &mut OwnedVec<Record<X>, W>,
487 opaque: &mut OwnedVec<Open<X>, W>,
488) -> PipelineResult<(), X, VE> {
489 if catalogue.snapshot() != access.snapshot() {
490 return Err(Error::Foreign {
491 node: catalogue.parent(),
492 });
493 }
494 for index in 0..catalogue.entries().len() {
495 admit(access, catalogue.parent())?;
496 let entry = catalogue.entries()[index];
497 match entry {
498 Entry::Encapsulated { node, kind } => {
499 let location = Location {
500 snapshot: catalogue.snapshot(),
501 node,
502 };
503 let source = super::retain(xml, location, kind, operation)
504 .map_err(|cause| Error::Retain { location, cause })?;
505 sources
506 .try_push(Retained { placement, source })
507 .map_err(|cause| Error::Resource { node, cause })?;
508 }
509 Entry::OtherCertificate { node } | Entry::OtherStatus { node } => {
510 opaque
511 .try_push(Opaque {
512 placement,
513 location: Location {
514 snapshot: catalogue.snapshot(),
515 node,
516 },
517 entry,
518 })
519 .map_err(|cause| Error::Resource { node, cause })?;
520 }
521 }
522 }
523 Ok(())
524}
525
526pub(super) fn collect_validation<X: OwnedBinaryXml, W: Work, VE>(
527 xml: &X,
528 access: &Access<'_, X, W>,
529 data: &crate::validation::Reading<X>,
530 operation: &Reader<'_>,
531 sources: &mut OwnedVec<Record<X>, W>,
532 opaque: &mut OwnedVec<Open<X>, W>,
533) -> PipelineResult<(), X, VE> {
534 if data.snapshot() != access.snapshot() {
535 return Err(Error::Foreign { node: data.node() });
536 }
537 let placement = match data.role() {
538 crate::validation::Role::Any => Placement::General(data.node()),
539 crate::validation::Role::Timestamp => Placement::Timestamp(data.node()),
540 };
541 for catalogue in [data.certificates(), data.revocations()]
542 .into_iter()
543 .flatten()
544 {
545 collect(
546 xml, access, catalogue, placement, operation, sources, opaque,
547 )?;
548 }
549 Ok(())
550}
551
552pub(super) fn admit<X: Xml, W: Work, VE>(
553 access: &Access<'_, X, W>,
554 node: X::Id,
555) -> PipelineResult<(), X, VE> {
556 access
557 .query(node, |_, _, work| Ok(work.charge_work(1)))
558 .map_err(Error::Access)?
559 .map_err(|cause| Error::Resource { node, cause })
560}
561
562fn source_placement<N: Copy + HashWork, W: Work>(
563 parents: [Option<N>; 4],
564 node: N,
565 work: &W,
566) -> Result<Option<Placement<N>>, MemoryError> {
567 for (index, parent) in parents.into_iter().enumerate() {
568 if let Some(parent) = parent
569 && crate::xml::equal_id(parent, node, work)?
570 {
571 return Ok(Some(if index < 2 {
572 Placement::Signature(node)
573 } else {
574 Placement::Attribute(node)
575 }));
576 }
577 }
578 Ok(None)
579}
580
581#[cfg(test)]
582mod placement_tests {
583 use super::{Placement, source_placement};
584 use alloc::sync::Arc;
585 use structured_asn1::memory::{HashWork, Memory, MemoryAccount, MemoryError};
586
587 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
588 struct Identity(u8, u64);
589 impl HashWork for Identity {
590 fn hash_work(&self) -> Result<u64, MemoryError> {
591 Ok(self.1)
592 }
593 }
594
595 #[test]
596 fn placement_pays_the_retained_parent_operand_before_comparison() {
597 let node = Identity(1, 1);
600 let parent = Identity(2, 100);
601 let short = Memory::new(Arc::new(MemoryAccount::new(0, 4)));
602 assert!(matches!(
603 source_placement([Some(parent), None, None, None], node, &short),
604 Err(MemoryError::WorkExceeded)
605 ));
606 assert_eq!(short.work_remaining(), 4);
607 let exact = Memory::new(Arc::new(MemoryAccount::new(0, 2)));
608 assert!(matches!(
609 source_placement([None, None, Some(node), None], node, &exact),
610 Ok(Some(Placement::Attribute(_)))
611 ));
612 assert_eq!(exact.work_remaining(), 0);
613 }
614}