1use crate::arena::{Kind, NodeId};
4use crate::document::{Document, Reading};
5use crate::error::{Error, ErrorKind, ReadFailure, Syntax};
6use crate::model::{admit, get};
7use crate::range::{RangeSource, Stamp};
8use crate::source::Location;
9use structured_asn1::memory::{Memory, OwnedMap, OwnedVec, Work};
10mod history;
11mod membership;
12mod parsed;
13pub use history::{History, HistoryError, ParsedHistory, Unified};
14pub mod checks;
15pub mod credentials;
16pub mod vri;
17pub use parsed::{MaterialIndex, Parsed, Placement, TimestampValue, TokenChoice, Value};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum KindOf {
22 Certificate,
24 Crl,
26 Ocsp,
28 Timestamp,
30}
31
32pub struct Item<'s> {
35 kind: KindOf,
36 bytes: OwnedVec<u8, Memory>,
37 location: Location,
38 number: u64,
39 generation: u16,
40 session: &'s crate::Session,
41}
42
43impl Item<'_> {
44 #[must_use]
46 pub fn kind(&self) -> KindOf {
47 self.kind
48 }
49 #[must_use]
51 pub fn as_bytes(&self) -> &[u8] {
52 &self.bytes
53 }
54 #[must_use]
56 pub fn location(&self) -> Location {
57 self.location
58 }
59 #[must_use]
61 pub fn identity(&self) -> (u64, u16) {
62 (self.number, self.generation)
63 }
64
65 pub fn read<'a, W: Work + 'a>(
67 &'a self,
68 operation: &W,
69 ) -> Result<(Decoded<'a>, structured_asn1::Reader<'a>), MaterialError> {
70 let mut reader = self
71 .session
72 .carried(&self.bytes, operation)
73 .map_err(MaterialError::Encoding)?;
74 if self.kind == KindOf::Timestamp {
75 let token = structured_cms::timestamp::Token::read_from(&mut reader)
76 .map_err(MaterialError::Cms)?;
77 if !reader.is_empty() {
78 return Err(MaterialError::Pdf(Error {
79 kind: ErrorKind::Syntax(Syntax::Stream),
80 location: self.location,
81 }));
82 }
83 return Ok((Decoded::Timestamp(token), reader));
84 }
85 let element = reader
86 .next_element()
87 .map_err(MaterialError::Encoding)?
88 .ok_or(MaterialError::Pdf(Error {
89 kind: ErrorKind::Syntax(Syntax::Stream),
90 location: self.location,
91 }))?;
92 if element.as_bytes().len() != self.bytes.len() {
93 return Err(MaterialError::Pdf(Error {
94 kind: ErrorKind::Syntax(Syntax::Stream),
95 location: self.location,
96 }));
97 }
98 let decoded = match self.kind {
99 KindOf::Certificate => Decoded::Certificate(
100 structured_pkix::Certificate::read_element(&reader, &element)
101 .map_err(MaterialError::Pkix)?,
102 ),
103 KindOf::Crl => Decoded::Crl(
104 structured_pkix::crl::CertificateList::read_element(&reader, &element)
105 .map_err(MaterialError::Pkix)?,
106 ),
107 KindOf::Ocsp => Decoded::Ocsp(
108 structured_pkix::ocsp::Response::read_element(&reader, &element)
109 .map_err(MaterialError::Pkix)?,
110 ),
111 KindOf::Timestamp => unreachable!("timestamp decoded before element consumption"),
112 };
113 Ok((decoded, reader))
114 }
115}
116
117pub enum Decoded<'a> {
119 Certificate(structured_pkix::Certificate<'a>),
121 Crl(structured_pkix::crl::CertificateList<'a>),
123 Ocsp(structured_pkix::ocsp::Response<'a>),
125 Timestamp(structured_cms::timestamp::Token<'a>),
127}
128
129#[derive(Debug)]
131pub enum MaterialError {
132 Pdf(Error),
134 Encoding(structured_asn1::Error),
136 Pkix(structured_pkix::Error),
138 Cms(structured_cms::Error),
140}
141
142#[derive(Debug)]
144pub struct MaterialFailure {
145 pub location: Location,
147 pub cause: MaterialError,
149}
150impl MaterialError {
151 #[must_use]
153 pub fn is_terminal(&self) -> bool {
154 match self {
155 Self::Pdf(cause) => matches!(
156 cause.kind,
157 ErrorKind::Resource(_) | ErrorKind::Limit(_) | ErrorKind::Foreign
158 ),
159 Self::Encoding(cause) => cause.is_resource(),
160 Self::Pkix(cause) => {
161 cause.is_resource() || cause.code() == structured_pkix::ErrorCode::ForeignReader
162 }
163 Self::Cms(cause) => cause.is_terminal(),
164 }
165 }
166}
167
168pub struct Vri {
171 name: [u8; 40],
172 location: Location,
173 certs: OwnedVec<usize, Memory>,
174 crls: OwnedVec<usize, Memory>,
175 ocsp: OwnedVec<usize, Memory>,
176 update: Option<crate::date::Date>,
177 timestamp: Option<usize>,
178 incorporated: bool,
179}
180
181#[derive(Debug, Clone, Copy)]
183pub enum GenerationTime {
184 Claimed(crate::date::Date),
186 Token(usize),
188 DocumentTimestampRequired,
191}
192
193impl Vri {
194 #[must_use]
198 pub fn all_material_in_dss(&self) -> bool {
199 self.incorporated
200 }
201 #[must_use]
203 pub fn name(&self) -> &[u8; 40] {
204 &self.name
205 }
206 #[must_use]
208 pub fn location(&self) -> Location {
209 self.location
210 }
211 #[must_use]
213 pub fn certificates(&self) -> &[usize] {
214 &self.certs
215 }
216 #[must_use]
218 pub fn crls(&self) -> &[usize] {
219 &self.crls
220 }
221 #[must_use]
223 pub fn ocsp(&self) -> &[usize] {
224 &self.ocsp
225 }
226 #[must_use]
228 pub fn update_time(&self) -> Option<crate::date::Date> {
229 self.update
230 }
231 #[must_use]
233 pub fn timestamp(&self) -> Option<usize> {
234 self.timestamp
235 }
236 #[must_use]
239 pub fn generation_time(&self) -> GenerationTime {
240 if let Some(value) = self.update {
241 return GenerationTime::Claimed(value);
242 }
243 if let Some(value) = self.timestamp {
244 return GenerationTime::Token(value);
245 }
246 GenerationTime::DocumentTimestampRequired
247 }
248}
249
250pub struct Validation<'s, D, G> {
253 session: &'s crate::Session,
254 stamp: Stamp<D, G>,
255 revision: usize,
256 present: bool,
257 items: OwnedVec<Item<'s>, Memory>,
258 vri: OwnedVec<Vri, Memory>,
259 vri_names: OwnedMap<vri::Name, usize, Memory>,
260 incorporated: bool,
261}
262
263impl<D: Copy + Eq, G: Copy + Eq> Validation<'_, D, G> {
264 #[must_use]
266 pub fn stamp(&self) -> Stamp<D, G> {
267 self.stamp
268 }
269 #[must_use]
271 pub fn revision(&self) -> usize {
272 self.revision
273 }
274 #[must_use]
276 pub fn is_present(&self) -> bool {
277 self.present
278 }
279 #[must_use]
281 pub fn items(&self) -> &[Item<'_>] {
282 &self.items
283 }
284 #[must_use]
286 pub fn vri(&self) -> &[Vri] {
287 &self.vri
288 }
289 #[must_use]
292 pub fn all_vri_material_in_dss(&self) -> bool {
293 self.incorporated
294 }
295}
296
297impl<'s, S: RangeSource> Document<'s, S> {
298 async fn dss_items<W: Work, Q: Work>(
299 &self,
300 revision: usize,
301 dss: &Reading<'s, S::Document, S::Generation>,
302 items: &mut OwnedVec<Item<'s>, W>,
303 work: &Q,
304 ) -> Result<(), ReadFailure<S::Error>> {
305 for (key, kind) in [
306 (b"Certs".as_slice(), KindOf::Certificate),
307 (b"CRLs", KindOf::Crl),
308 (b"OCSPs", KindOf::Ocsp),
309 ] {
310 if let Some(array) = get(&dss.value.arena, dss.value.root, key, work)? {
311 let Some(references) = array_references(self, dss, array, work).await? else {
312 continue;
313 };
314 for &(number, generation) in &references {
315 let item = material(self, revision, number, generation, kind, work).await?;
316 items.try_push(item).map_err(|cause| Error {
317 kind: ErrorKind::Resource(cause),
318 location: dss.value.root().location(),
319 })?;
320 }
321 }
322 }
323 Ok(())
324 }
325
326 pub async fn validation<W: Work>(
333 &self,
334 revision: usize,
335 operation: &W,
336 ) -> Result<Validation<'s, S::Document, S::Generation>, ReadFailure<S::Error>> {
337 let work = self.session.query(operation);
338 let root = self.root(revision, &work).await?;
339 let dss = self.select(root, b"DSS", &work).await?;
340 let present = dss.is_some();
341 let mut items = work.vector();
342 let mut vri = work.vector();
343 let mut vri_names = work.map();
344 let mut incorporated = true;
345 if let Some(dss) = dss {
346 type_name(self, &dss, dss.value.root, b"DSS", &work).await?;
347 self.dss_items(revision, &dss, &mut items, &work).await?;
348 let membership =
349 membership::Membership::new(&items, dss.value.root().location(), &work)?;
350 if let Some(index_owner) = self.select(dss, b"VRI", &work).await? {
351 let Kind::Dictionary(_, fields) =
352 index_owner.value.arena.nodes[index_owner.value.root.0].kind
353 else {
354 return Err(Error {
355 kind: ErrorKind::Syntax(Syntax::Dictionary),
356 location: index_owner.value.root().location(),
357 }
358 .into());
359 };
360 for ordinal in fields.start..fields.start + fields.length {
361 admit(&work, index_owner.value.root().location(), 1)?;
362 let entry = index_owner.value.arena.fields[ordinal];
363 let resolved;
364 let (owner, node) = if let Kind::Reference { number, generation } =
365 index_owner.value.arena.nodes[entry.value.0].kind
366 {
367 resolved = self.object(revision, number, generation, &work).await?;
368 let Some(owner) = &resolved else { continue };
369 if matches!(owner.value.arena.nodes[owner.value.root.0].kind, Kind::Null) {
370 continue;
371 }
372 (owner, owner.value.root)
373 } else {
374 (&index_owner, entry.value)
375 };
376 let (name, location) = vri_name(&index_owner.value.arena, entry.key, &work)?;
377 let (certs, crls, ocsp, update, timestamp) =
378 vri_members(self, owner, node, &mut items, &work).await?;
379 if vri_names
380 .try_insert(vri::Name(name), vri.len())
381 .map_err(|cause| Error {
382 kind: ErrorKind::Resource(cause),
383 location,
384 })?
385 .is_some()
386 {
387 return Err(Error {
388 kind: ErrorKind::Syntax(Syntax::Dictionary),
389 location,
390 }
391 .into());
392 }
393 let member_incorporated =
394 incorporation(&membership, &items, [&certs, &crls, &ocsp], &work)?;
395 if !member_incorporated {
396 incorporated = false;
397 }
398 vri.try_push(Vri {
399 name,
400 location,
401 certs,
402 crls,
403 ocsp,
404 update,
405 timestamp,
406 incorporated: member_incorporated,
407 })
408 .map_err(|cause| Error {
409 kind: ErrorKind::Resource(cause),
410 location,
411 })?;
412 }
413 }
414 }
415 self.check(
416 &work,
417 Location::new(crate::source::Source::File, 0, self.stamp.length).expect("file range"),
418 )?;
419 Ok(Validation {
420 session: self.session,
421 stamp: self.stamp,
422 revision,
423 present,
424 items: items.into_owned(),
425 vri: vri.into_owned(),
426 vri_names: vri_names.into_owned(),
427 incorporated,
428 })
429 }
430}
431
432fn incorporation<W: Work>(
433 membership: &membership::Membership<W>,
434 items: &[Item<'_>],
435 pools: [&[usize]; 3],
436 work: &impl Work,
437) -> crate::Result<bool> {
438 let mut complete = true;
439 for pool in pools {
440 for &ordinal in pool {
441 if !membership.contains(items, &items[ordinal], work)? {
442 complete = false;
443 }
444 }
445 }
446 Ok(complete)
447}
448
449fn vri_name(
450 arena: &crate::arena::Arena<impl Work>,
451 key_node: NodeId,
452 work: &impl Work,
453) -> crate::Result<([u8; 40], Location)> {
454 let Kind::Name(key) = arena.nodes[key_node.0].kind else {
455 unreachable!("dictionary name")
456 };
457 let location = arena.nodes[key_node.0].location;
458 if key.length != 40 {
459 return Err(Error {
460 kind: ErrorKind::Syntax(Syntax::Name),
461 location,
462 });
463 }
464 admit(work, location, 40)?;
465 let mut name = [0_u8; 40];
466 for (output, &byte) in name.iter_mut().zip(&arena.names[key.start..key.start + 40]) {
467 if !matches!(byte, b'0'..=b'9' | b'A'..=b'F') {
468 return Err(Error {
469 kind: ErrorKind::Syntax(Syntax::Name),
470 location,
471 });
472 }
473 *output = byte;
474 }
475 Ok((name, location))
476}
477
478async fn array_references<S: RangeSource>(
479 document: &Document<'_, S>,
480 owner: &Reading<'_, S::Document, S::Generation>,
481 node: NodeId,
482 work: &impl Work,
483) -> Result<Option<OwnedVec<(u64, u16), Memory>>, ReadFailure<S::Error>> {
484 let Some(selected) = document.resolve_value(owner, node, work).await? else {
485 return Ok(None);
486 };
487 let (owner, node) = selected.parts();
488 let arena = &owner.value.arena;
489 let Kind::Array(children) = arena.nodes[node.0].kind else {
490 return Err(Error {
491 kind: ErrorKind::Syntax(Syntax::Dictionary),
492 location: arena.nodes[node.0].location,
493 }
494 .into());
495 };
496 let mut output = work.vector();
497 let mut next = children.first;
498 while let Some(index) = next {
499 admit(work, arena.nodes[node.0].location, 1)?;
500 let link = arena.links[index];
501 next = link.next;
502 let Kind::Reference { number, generation } = arena.nodes[link.value.0].kind else {
503 return Err(Error {
504 kind: ErrorKind::Syntax(Syntax::Object),
505 location: arena.nodes[link.value.0].location,
506 }
507 .into());
508 };
509 output
510 .try_push((number, generation))
511 .map_err(|cause| Error {
512 kind: ErrorKind::Resource(cause),
513 location: arena.nodes[link.value.0].location,
514 })?;
515 }
516 Ok(Some(output.into_owned()))
517}
518
519async fn material<'s, S: RangeSource>(
520 document: &Document<'s, S>,
521 revision: usize,
522 number: u64,
523 generation: u16,
524 kind: KindOf,
525 work: &impl Work,
526) -> Result<Item<'s>, ReadFailure<S::Error>> {
527 let reading = document
528 .object(revision, number, generation, work)
529 .await?
530 .ok_or(Error {
531 kind: ErrorKind::Syntax(Syntax::Object),
532 location: Location::new(crate::source::Source::File, 0, 0).expect("file point"),
533 })?;
534 material_reading(document, &reading, number, generation, kind, work).await
535}
536
537async fn material_reading<'s, S: RangeSource>(
538 document: &Document<'s, S>,
539 reading: &Reading<'s, S::Document, S::Generation>,
540 number: u64,
541 generation: u16,
542 kind: KindOf,
543 work: &impl Work,
544) -> Result<Item<'s>, ReadFailure<S::Error>> {
545 let decoded = document.stream_data(reading, false, work).await?;
546 let item = Item {
547 kind,
548 bytes: decoded.bytes,
549 location: Location::new(
550 crate::source::Source::File,
551 decoded.body.start,
552 decoded.body.length,
553 )
554 .expect("checked stream extent"),
555 number,
556 generation,
557 session: document.session,
558 };
559 Ok(item)
560}
561
562type VriMembers = (
563 OwnedVec<usize>,
564 OwnedVec<usize>,
565 OwnedVec<usize>,
566 Option<crate::date::Date>,
567 Option<usize>,
568);
569
570async fn vri_members<'s, S: RangeSource, W: Work>(
571 document: &Document<'s, S>,
572 owner: &Reading<'s, S::Document, S::Generation>,
573 node: NodeId,
574 items: &mut OwnedVec<Item<'s>, W>,
575 work: &impl Work,
576) -> Result<VriMembers, ReadFailure<S::Error>> {
577 let location = owner.value.arena.nodes[node.0].location;
578 type_name(document, owner, node, b"VRI", work).await?;
579 let mut certs = work.vector();
580 let mut crls = work.vector();
581 let mut ocsp = work.vector();
582 for (key, kind, output) in [
583 (b"Cert".as_slice(), KindOf::Certificate, &mut certs),
584 (b"CRL", KindOf::Crl, &mut crls),
585 (b"OCSP", KindOf::Ocsp, &mut ocsp),
586 ] {
587 if let Some(array) = get(&owner.value.arena, node, key, work)? {
588 let Some(references) = array_references(document, owner, array, work).await? else {
589 continue;
590 };
591 if references.is_empty() {
592 return Err(Error {
593 kind: ErrorKind::Syntax(Syntax::Dictionary),
594 location,
595 }
596 .into());
597 }
598 for &(number, generation) in &references {
599 let item =
600 material(document, owner.revision, number, generation, kind, work).await?;
601 let index = items.len();
602 output.try_push(index).map_err(|cause| Error {
603 kind: ErrorKind::Resource(cause),
604 location,
605 })?;
606 items.try_push(item).map_err(|cause| Error {
607 kind: ErrorKind::Resource(cause),
608 location,
609 })?;
610 }
611 }
612 }
613 let (update, timestamp) = vri_generation(document, owner, node, items, work).await?;
614 Ok((
615 certs.into_owned(),
616 crls.into_owned(),
617 ocsp.into_owned(),
618 update,
619 timestamp,
620 ))
621}
622
623async fn vri_generation<'s, S: RangeSource, W: Work>(
624 document: &Document<'s, S>,
625 owner: &Reading<'s, S::Document, S::Generation>,
626 node: NodeId,
627 items: &mut OwnedVec<Item<'s>, W>,
628 work: &impl Work,
629) -> Result<(Option<crate::date::Date>, Option<usize>), ReadFailure<S::Error>> {
630 let location = owner.value.arena.nodes[node.0].location;
631 let update = if let Some(value) = get(&owner.value.arena, node, b"TU", work)? {
632 document.resolve_value(owner, value, work).await?
633 } else {
634 None
635 };
636 let timestamp_node = get(&owner.value.arena, node, b"TS", work)?;
637 let timestamp = if let Some(value) = timestamp_node {
638 document.resolve_value(owner, value, work).await?
639 } else {
640 None
641 };
642 if update.is_some() && timestamp.is_some() {
646 return Err(Error {
647 kind: ErrorKind::Syntax(Syntax::Dictionary),
648 location,
649 }
650 .into());
651 }
652 let update = if let Some(value) = update {
653 let (owner, node) = value.parts();
654 let bytes = document.string(owner, node, 22, work).await?;
655 Some(crate::date::Date::read(
656 &bytes,
657 owner.value.arena.nodes[node.0].location,
658 work,
659 )?)
660 } else {
661 None
662 };
663 let timestamp = if let Some(value) = timestamp {
664 let node = timestamp_node.expect("a resolved timestamp has an original field");
665 let Kind::Reference { number, generation } = owner.value.arena.nodes[node.0].kind else {
666 return Err(Error {
667 kind: ErrorKind::Syntax(Syntax::Stream),
668 location: owner.value.arena.nodes[node.0].location,
669 }
670 .into());
671 };
672 let (reading, _) = value.parts();
673 let item = material_reading(
674 document,
675 reading,
676 number,
677 generation,
678 KindOf::Timestamp,
679 work,
680 )
681 .await?;
682 let index = items.len();
683 items.try_push(item).map_err(|cause| Error {
684 kind: ErrorKind::Resource(cause),
685 location,
686 })?;
687 Some(index)
688 } else {
689 None
690 };
691 Ok((update, timestamp))
692}
693
694async fn type_name<'s, S: RangeSource>(
695 document: &Document<'s, S>,
696 owner: &Reading<'s, S::Document, S::Generation>,
697 dictionary: NodeId,
698 expected: &[u8],
699 work: &impl Work,
700) -> Result<(), ReadFailure<S::Error>> {
701 let Some(node) = get(&owner.value.arena, dictionary, b"Type", work)? else {
705 return Ok(());
706 };
707 let Some(selected) = document.resolve_value(owner, node, work).await? else {
708 return Ok(());
709 };
710 let (owner, node) = selected.parts();
711 let arena = &owner.value.arena;
712 let value = arena.nodes[node.0];
713 admit(work, value.location, expected.len() as u64 + 1)?;
714 if matches!(value.kind, Kind::Null) {
715 return Ok(());
716 }
717 if let Kind::Name(name) = value.kind
718 && name.length == expected.len()
719 && &arena.names[name.start..name.start + name.length] == expected
720 {
721 return Ok(());
722 }
723 Err(Error {
724 kind: ErrorKind::Syntax(Syntax::Dictionary),
725 location: value.location,
726 }
727 .into())
728}