1use crate::{
5 Cades, Error, ISSUER,
6 prepare::Preparation,
7 source::{DocumentId, Node, Original},
8};
9use structured_asn1::{
10 Reader, Tag,
11 memory::{OwnedVec, Work},
12 universal,
13};
14use structured_cms::{
15 SignedData,
16 signed::{CertificateChoice, OtherRevocationInfo},
17 timestamp::Token,
18};
19use structured_ltv::{
20 material::{self, ArtifactRef, ArtifactSource, CarriedOrigin, Occurrence},
21 source::Query,
22};
23use structured_pkix::{crl::CertificateList, ocsp::BasicResponse};
24
25mod checks;
26pub mod credentials;
27mod legacy;
28pub use checks::{CheckError, Checks, Finding, check_participants};
29
30#[derive(Debug)]
32pub enum ArchiveIndexError {
33 Unproven,
35 Index(material::IndexError<Node>),
37 Coverage(material::CoverageError<Node>),
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct LegacyLocation {
45 pub signature: Node,
47 pub property: Node,
49 pub member: usize,
51 pub token: Option<usize>,
53 pub unsigned: bool,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum Placement {
61 RootCertificate(usize),
63 RootRevocation(usize),
65 TokenCertificate {
67 token: usize,
69 choice: usize,
71 },
72 TokenRevocation {
74 token: usize,
76 choice: usize,
78 },
79 CertificateValues(LegacyLocation),
81 CrlValues(LegacyLocation),
83 OcspValues(LegacyLocation),
85 OtherRevocationValues(LegacyLocation),
87}
88
89pub struct Open<'a> {
92 pub element: structured_asn1::Element<'a>,
94 pub node: Node,
96 pub placement: Placement,
98 pub cause: Option<Error>,
100}
101
102struct Crl<'d> {
103 value: CertificateList<'d>,
104 node: Node,
105 placement: Placement,
106}
107struct Ocsp<'d> {
108 value: BasicResponse<'d>,
109 node: Node,
110 placement: Placement,
111}
112
113pub struct Pool<'prepared, 'model, 'document> {
116 prepared: &'prepared Preparation<'model, 'document>,
117 tokens: &'prepared [crate::prepare::tokens::Model<'document>],
118 token_index: &'prepared [Option<usize>],
119 token_faults: &'prepared [(Node, crate::prepare::ReadError)],
120 crls: OwnedVec<Crl<'document>>,
121 ocsp: OwnedVec<Ocsp<'document>>,
122 open: OwnedVec<Open<'document>>,
123 related: OwnedVec<legacy::Related<'document>>,
124 legacy_slots: Option<legacy::Slots>,
125}
126
127pub type Index<'source, 'model, 'document, W> = material::Index<
129 'source,
130 Node,
131 DocumentId<'model, 'document>,
132 (),
133 Placement,
134 Open<'document>,
135 W,
136 Cades,
137>;
138
139impl<'p, 'm, 'd> Pool<'p, 'm, 'd> {
140 pub fn read(prepared: &'p Preparation<'m, 'd>, operation: &Reader<'d>) -> crate::Result<Self> {
144 prepared
145 .document()
146 .message()
147 .require_session(operation, "cades.material")?;
148 let work = operation.execution();
149 let tokens = &*prepared.parsed.models;
150 let mut crls = work.vector();
151 let mut ocsp = work.vector();
152 let mut open = work.vector();
153 collect(
154 prepared.document().message(),
155 None,
156 operation,
157 &mut crls,
158 &mut ocsp,
159 &mut open,
160 )?;
161 for model in tokens {
162 work.charge_work(1).map_err(|cause| {
163 Error::resource(cause, "cades.material", model.token.message().span().start)
164 })?;
165 collect(
166 model.token.message(),
167 Some(model.ordinal),
168 operation,
169 &mut crls,
170 &mut ocsp,
171 &mut open,
172 )?;
173 }
174 let related = legacy::read_related(prepared, operation)?;
175 legacy::append_open(prepared, tokens, &related, &mut open, &work)?;
176 let mut out = Self {
177 prepared,
178 tokens,
179 token_index: &prepared.parsed.ordinals,
180 token_faults: &prepared.parsed.faults,
181 crls: crls.into_owned(),
182 ocsp: ocsp.into_owned(),
183 open: open.into_owned(),
184 related,
185 legacy_slots: None,
186 };
187 out.legacy_slots = legacy::slots(&out, &work)?;
188 Ok(out)
189 }
190 #[must_use]
192 pub fn prepared(&self) -> &'p Preparation<'m, 'd> {
193 self.prepared
194 }
195 #[must_use]
197 pub fn token_faults(&self) -> &[(Node, crate::prepare::ReadError)] {
198 self.token_faults
199 }
200 #[must_use]
202 pub fn open(&self) -> &[Open<'d>] {
203 &self.open
204 }
205 #[must_use]
207 pub fn crls(&self) -> usize {
208 self.crls.len()
209 + self
210 .legacy_slots
211 .as_ref()
212 .map_or(0, |slots| slots.crls.len())
213 }
214 pub fn crl<W: Work>(
217 &self,
218 ordinal: usize,
219 work: &W,
220 ) -> crate::Result<Option<&CertificateList<'d>>> {
221 work.charge_work(1).map_err(|cause| {
222 Error::resource(
223 cause,
224 "cades.material.crl",
225 self.prepared.signer().span().start,
226 )
227 })?;
228 if ordinal < self.crls.len() {
229 return Ok(Some(&self.crls[ordinal].value));
230 }
231 legacy::crl(self, ordinal - self.crls.len(), work)
232 }
233 #[must_use]
235 pub fn ocsp_responses(&self) -> usize {
236 self.ocsp.len()
237 + self
238 .legacy_slots
239 .as_ref()
240 .map_or(0, |slots| slots.ocsp.len())
241 }
242 pub fn ocsp<W: Work>(
245 &self,
246 ordinal: usize,
247 work: &W,
248 ) -> crate::Result<Option<&BasicResponse<'d>>> {
249 work.charge_work(1).map_err(|cause| {
250 Error::resource(
251 cause,
252 "cades.material.ocsp",
253 self.prepared.signer().span().start,
254 )
255 })?;
256 if ordinal < self.ocsp.len() {
257 return Ok(Some(&self.ocsp[ordinal].value));
258 }
259 legacy::ocsp(self, ordinal - self.ocsp.len(), work)
260 }
261 pub fn token<W: Work>(&self, ordinal: usize, work: &W) -> crate::Result<Option<&Token<'d>>> {
264 work.charge_work(1).map_err(|cause| {
265 Error::resource(cause, "cades.material", self.prepared.signer().span().start)
266 })?;
267 let Some(Some(index)) = self.token_index.get(ordinal) else {
268 return Ok(None);
269 };
270 debug_assert_eq!(self.tokens[*index].ordinal, ordinal);
271 Ok(Some(&self.tokens[*index].token))
272 }
273 pub fn with_token<R, E>(
276 &self,
277 ordinal: usize,
278 operation: &Reader<'d>,
279 visitor: impl for<'scope> FnOnce(crate::timestamp::Context<'scope, 'm, 'd>) -> Result<R, E>,
280 ) -> Result<R, crate::timestamp::VisitError<E>> {
281 operation.charge(1).map_err(|cause| {
282 crate::timestamp::VisitError::Processing(Error::encoding(cause, "cades.timestamp"))
283 })?;
284 let entry = self.prepared.timestamps().get(ordinal).ok_or_else(|| {
285 crate::timestamp::VisitError::Processing(Error::Foreign {
286 at: self.prepared.signer().span().start,
287 })
288 })?;
289 let token = self
290 .token(ordinal, &operation.execution())
291 .map_err(crate::timestamp::VisitError::Processing)?
292 .ok_or_else(|| crate::timestamp::VisitError::Unreadable {
293 property: entry.property(),
294 })?;
295 crate::timestamp::with_existing(self.prepared, ordinal, token, operation, visitor)
296 }
297 pub fn index_for_archive<'source, Q>(
301 &'source self,
302 archive: crate::timestamp::ArchiveView<'_, 'm, 'd, '_>,
303 query: &Q,
304 ) -> Result<Index<'source, 'm, 'd, Q::Work>, ArchiveIndexError>
305 where
306 Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
307 'd: 'source,
308 {
309 let actor = archive
310 .timestamp()
311 .actor()
312 .ok_or(ArchiveIndexError::Unproven)?;
313 let evidence = actor.verified();
314 let snapshot = self
315 .prepared
316 .document()
317 .locate(self.prepared.signer().span())
318 .snapshot;
319 if evidence.token().snapshot != snapshot || query.snapshot() != snapshot {
320 return Err(ArchiveIndexError::Index(material::IndexError::Foreign {
321 signature: evidence.signature(),
322 }));
323 }
324 archive
325 .coverage()
326 .require_evidence(evidence, archive.coverage().subject(), query)
327 .map_err(ArchiveIndexError::Coverage)?;
328 self.index_for_signature(evidence.signature(), query)
329 .map_err(ArchiveIndexError::Index)
330 }
331
332 fn index_for_signature<'source, Q>(
333 &'source self,
334 signature: Node,
335 query: &Q,
336 ) -> Result<Index<'source, 'm, 'd, Q::Work>, material::IndexError<Node>>
337 where
338 Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
339 'd: 'source,
340 {
341 let mut index = Index::new(ISSUER, signature, &self.open, query)?;
342 certificates(
343 self.prepared.document().message(),
344 None,
345 self.prepared,
346 &mut index,
347 query,
348 )?;
349 for token in self.tokens {
350 query
351 .admit(signature, 1)
352 .map_err(material::IndexError::Scope)?;
353 certificates(
354 token.token.message(),
355 Some(token.ordinal),
356 self.prepared,
357 &mut index,
358 query,
359 )?;
360 }
361 for crl in &self.crls {
362 query
363 .admit(signature, 1)
364 .map_err(material::IndexError::Scope)?;
365 index.insert(
366 ISSUER,
367 ArtifactRef::Crl(&crl.value),
368 revocation_occurrence(self.prepared, crl.node, crl.placement)?,
369 query,
370 )?;
371 }
372 for response in &self.ocsp {
373 query
374 .admit(signature, 1)
375 .map_err(material::IndexError::Scope)?;
376 index.insert(
377 ISSUER,
378 ArtifactRef::Ocsp(&response.value),
379 revocation_occurrence(self.prepared, response.node, response.placement)?,
380 query,
381 )?;
382 }
383 legacy::index(self, &mut index, query)?;
384 Ok(index)
385 }
386
387 pub fn index<'source, Q>(
391 &'source self,
392 query: &Q,
393 ) -> Result<Index<'source, 'm, 'd, Q::Work>, material::IndexError<Node>>
394 where
395 Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
396 'd: 'source,
397 {
398 let signature = self
399 .prepared
400 .document()
401 .locate(self.prepared.signer().span());
402 if query.snapshot() != signature.snapshot {
403 return Err(material::IndexError::Foreign {
404 signature: signature.node,
405 });
406 }
407 self.index_for_signature(signature.node, query)
408 }
409}
410
411fn certificates<'s, 'm, 'd, Q>(
412 message: &'s SignedData<'d>,
413 token: Option<usize>,
414 prepared: &Preparation<'m, 'd>,
415 index: &mut Index<'s, 'm, 'd, Q::Work>,
416 query: &Q,
417) -> Result<(), material::IndexError<Node>>
418where
419 Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
420 'd: 's,
421{
422 for (choice, entry) in message.certificate_choices().iter().enumerate() {
423 index.admit(query)?;
424 if let CertificateChoice::X509(ordinal) = entry {
425 let value = &message.certificates()[*ordinal];
426 let placement = match token {
427 Some(token) => Placement::TokenCertificate { token, choice },
428 None => Placement::RootCertificate(choice),
429 };
430 let source = match token {
431 Some(token) => ArtifactSource::TokenCertificate {
432 token,
433 certificate: *ordinal,
434 },
435 None => ArtifactSource::Material(choice),
436 };
437 let enclosure = if let Some(token) = token {
438 token_enclosure(prepared, token)?
439 } else {
440 Node::of(value.span())
441 };
442 index.insert(
443 ISSUER,
444 ArtifactRef::Certificate(value),
445 Occurrence::new(
446 source,
447 prepared.document().locate(enclosure.span()),
448 placement,
449 ),
450 query,
451 )?;
452 }
453 }
454 Ok(())
455}
456
457fn token_enclosure(
458 prepared: &Preparation<'_, '_>,
459 token: usize,
460) -> Result<Node, material::IndexError<Node>> {
461 let Some(value) = prepared
462 .timestamps()
463 .get(token)
464 .and_then(crate::prepare::Timestamp::value)
465 else {
466 return Err(material::IndexError::Foreign {
467 signature: Node::of(prepared.signer().span()),
468 });
469 };
470 Ok(Node::of(value.span()))
471}
472
473fn revocation_occurrence<'m, 'd>(
474 prepared: &Preparation<'m, 'd>,
475 node: Node,
476 placement: Placement,
477) -> Result<Occurrence<Node, DocumentId<'m, 'd>, (), Placement>, material::IndexError<Node>> {
478 let signature = Node::of(prepared.signer().span());
479 let (source, enclosure) = match placement {
480 Placement::RootRevocation(ordinal) => {
481 let ordinal = prepared
482 .document()
483 .message()
484 .certificate_choices()
485 .len()
486 .checked_add(ordinal)
487 .ok_or(material::IndexError::Resource {
488 signature,
489 cause: structured_asn1::memory::MemoryError::CapacityOverflow,
490 })?;
491 (ArtifactSource::Material(ordinal), node)
492 }
493 Placement::TokenRevocation { token, choice } => (
494 ArtifactSource::TokenRevocation(CarriedOrigin { token, choice }),
495 token_enclosure(prepared, token)?,
496 ),
497 Placement::RootCertificate(_)
498 | Placement::TokenCertificate { .. }
499 | Placement::CertificateValues(_)
500 | Placement::CrlValues(_)
501 | Placement::OcspValues(_)
502 | Placement::OtherRevocationValues(_) => {
503 return Err(material::IndexError::Foreign { signature });
504 }
505 };
506 let location = Original {
510 snapshot: prepared.document().locate(enclosure.span()).snapshot,
511 node: enclosure,
512 };
513 Ok(Occurrence::new(source, location, placement))
514}
515
516fn collect<'d, W: Work>(
517 message: &SignedData<'d>,
518 token: Option<usize>,
519 operation: &Reader<'d>,
520 crls: &mut OwnedVec<Crl<'d>, W>,
521 ocsp: &mut OwnedVec<Ocsp<'d>, W>,
522 open: &mut OwnedVec<Open<'d>, W>,
523) -> crate::Result<()> {
524 for (choice, certificate) in message.certificate_choices().iter().enumerate() {
525 operation
526 .charge(1)
527 .map_err(|cause| Error::encoding(cause, "cades.material"))?;
528 let element = match certificate {
529 CertificateChoice::X509(_) => continue,
530 CertificateChoice::Extended(element)
531 | CertificateChoice::AttributeV1(element)
532 | CertificateChoice::AttributeV2(element)
533 | CertificateChoice::Other(element) => *element,
534 };
535 let placement = match token {
536 Some(token) => Placement::TokenCertificate { token, choice },
537 None => Placement::RootCertificate(choice),
538 };
539 push_open(open, element, placement, None)?;
540 }
541 for (choice, element) in message.revocations().iter().enumerate() {
542 operation
543 .charge(1)
544 .map_err(|cause| Error::encoding(cause, "cades.material"))?;
545 let node = Node::of(element.span());
546 let placement = match token {
547 Some(token) => Placement::TokenRevocation { token, choice },
548 None => Placement::RootRevocation(choice),
549 };
550 let reader = message
551 .revocation_reader_in(operation, choice)?
552 .ok_or(Error::Foreign {
553 at: element.span().start,
554 })?;
555 let result = if element.tag() == Tag::constructed(universal::SEQUENCE) {
556 CertificateList::read_element(&reader, element)
557 .map_err(Error::Pkix)
558 .and_then(|value| {
559 crls.try_push(Crl {
560 value,
561 node,
562 placement,
563 })
564 .map_err(|cause| Error::resource(cause, "cades.material", element.span().start))
565 })
566 } else {
567 match OtherRevocationInfo::read(&reader, element)
568 .map_err(Error::Cms)
569 .and_then(|other| other.ocsp().map_err(Error::Cms))
570 {
571 Ok(Some(response)) => match response.basic_in(operation).map_err(Error::Pkix) {
572 Ok(Some(value)) => ocsp
573 .try_push(Ocsp {
574 value,
575 node,
576 placement,
577 })
578 .map_err(|cause| {
579 Error::resource(cause, "cades.material", element.span().start)
580 }),
581 Ok(None) => push_open(open, *element, placement, None),
582 Err(cause) => Err(cause),
583 },
584 Ok(None) => push_open(open, *element, placement, None),
585 Err(cause) => Err(cause),
586 }
587 };
588 if let Err(cause) = result {
589 if cause.is_terminal() {
590 return Err(cause);
591 }
592 push_open(open, *element, placement, Some(cause))?;
593 }
594 }
595 Ok(())
596}
597
598fn push_open<'a, W: Work>(
599 out: &mut OwnedVec<Open<'a>, W>,
600 element: structured_asn1::Element<'a>,
601 placement: Placement,
602 cause: Option<Error>,
603) -> crate::Result<()> {
604 out.try_push(Open {
605 element,
606 node: Node::of(element.span()),
607 placement,
608 cause,
609 })
610 .map_err(|cause| Error::resource(cause, "cades.material", element.span().start))
611}