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