1use crate::{
4 Asic, ISSUER,
5 content::Content,
6 error::{Error as ArchiveError, resource},
7 source::{DocumentId, Node},
8 zip::MemberId,
9};
10use structured_asn1::{
11 Reader, Span,
12 memory::{Execution, OwnedVec, Work},
13 range::RangeSource,
14};
15use structured_cades::properties::Properties;
16use structured_cms::{
17 SignedData,
18 signed::{CertificateChoice, revocation},
19};
20use structured_ltv::{
21 material::{ArtifactRef, ArtifactSource, Occurrence},
22 source::{Location, Query},
23};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum Placement {
28 Certificate(usize),
30 Revocation(usize),
32 CertificateValues {
34 signer: usize,
36 ordinal: usize,
38 unsigned: bool,
40 },
41 CrlValues {
43 signer: usize,
45 ordinal: usize,
47 unsigned: bool,
49 },
50 OcspValues {
52 signer: usize,
54 ordinal: usize,
56 unsigned: bool,
58 },
59}
60#[derive(Debug)]
62pub enum Finding {
63 Certificate {
65 choice: usize,
67 span: Span,
69 },
70 Revocation {
72 choice: usize,
74 span: Span,
76 },
77 RevocationSyntax {
79 choice: usize,
81 span: Span,
83 cause: revocation::Error,
85 },
86 Properties {
88 signer: usize,
90 cause: structured_cades::Error,
92 },
93}
94#[derive(Debug)]
96pub enum Error<E> {
97 Archive(ArchiveError<E>),
99 Revocation(revocation::Error),
101 Properties(structured_cades::Error),
103 Cms(structured_cms::Error),
105}
106struct Status<'a> {
107 choice: usize,
108 value: revocation::Reading<'a>,
109}
110struct Owner<'a> {
111 signer: usize,
112 unsigned: Option<Span>,
113 properties: Properties<'a>,
114}
115pub struct Pool<'a, 'v, 's, S: RangeSource> {
118 content: &'a Content<'v, 's, S>,
119 message: &'a SignedData<'a>,
120 statuses: OwnedVec<Status<'a>>,
121 owners: OwnedVec<Owner<'a>>,
122 findings: OwnedVec<Finding>,
123}
124impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
125 pub fn material<'a>(
133 &'a self,
134 message: &'a SignedData<'a>,
135 parent: &Reader<'a>,
136 ) -> Result<Pool<'a, 'v, 's, S>, Error<S::Error>> {
137 let work = parent.execution();
138 self.require(message, parent, &work)
139 .map_err(Error::Archive)?;
140 let refusal = |cause| Error::Archive(resource(self.enclosure(), cause));
141 let mut findings = work.vector();
142 let mut statuses = work.vector();
143 let mut owners = work.vector();
144 for (choice, value) in message.certificate_choices().iter().enumerate() {
145 work.charge_work(1).map_err(refusal)?;
146 let span = match value {
147 CertificateChoice::X509(_) => continue,
148 CertificateChoice::Extended(value)
149 | CertificateChoice::AttributeV1(value)
150 | CertificateChoice::AttributeV2(value)
151 | CertificateChoice::Other(value) => value.span(),
152 };
153 findings
154 .try_push(Finding::Certificate { choice, span })
155 .map_err(refusal)?;
156 }
157 for (choice, value) in message.revocations().iter().enumerate() {
158 work.charge_work(1).map_err(refusal)?;
159 let reader = message
160 .revocation_reader_in(parent, choice)
161 .map_err(Error::Cms)?
162 .expect("existing original revocation ordinal");
163 match revocation::read(&reader, value) {
164 Ok(revocation::Reading::Open { .. }) => findings
165 .try_push(Finding::Revocation {
166 choice,
167 span: value.span(),
168 })
169 .map_err(refusal)?,
170 Ok(value) => statuses
171 .try_push(Status { choice, value })
172 .map_err(refusal)?,
173 Err(cause) if cause.is_terminal() => return Err(Error::Revocation(cause)),
174 Err(cause) => findings
175 .try_push(Finding::RevocationSyntax {
176 choice,
177 span: value.span(),
178 cause,
179 })
180 .map_err(refusal)?,
181 }
182 }
183 for (signer, value) in message.signers().iter().enumerate() {
184 work.charge_work(1).map_err(refusal)?;
185 match Properties::read(value, parent) {
186 Ok(properties) => owners
187 .try_push(Owner {
188 signer,
189 unsigned: value
190 .unsigned_attributes()
191 .map(|attributes| attributes.element().span()),
192 properties,
193 })
194 .map_err(refusal)?,
195 Err(cause) if cause.is_terminal() => return Err(Error::Properties(cause)),
196 Err(cause) => findings
197 .try_push(Finding::Properties { signer, cause })
198 .map_err(refusal)?,
199 }
200 }
201 self.member()
202 .archive()
203 .admit(&work, self.enclosure())
204 .map_err(Error::Archive)?;
205 Ok(Pool {
206 content: self,
207 message,
208 statuses: statuses.into_owned(),
209 owners: owners.into_owned(),
210 findings: findings.into_owned(),
211 })
212 }
213}
214pub type Index<'a, 'q, 'v, 's, S> = structured_ltv::material::Index<
216 'a,
217 Node,
218 DocumentId<'v, 's, S>,
219 (),
220 Placement,
221 Finding,
222 Execution<'q>,
223 Asic,
224>;
225type IndexOf<'a, 'q, 'v, 's, S, O> = structured_ltv::material::Index<
226 'a,
227 Node,
228 DocumentId<'v, 's, S>,
229 (),
230 Placement,
231 O,
232 Execution<'q>,
233 Asic,
234>;
235impl<'a, 'v, 's, S: RangeSource> Pool<'a, 'v, 's, S> {
236 #[must_use]
238 pub fn member(&self) -> MemberId<S::Document, S::Generation> {
239 self.content.member().id()
240 }
241 #[must_use]
243 pub fn findings(&self) -> &[Finding] {
244 &self.findings
245 }
246 pub fn properties(&self) -> impl Iterator<Item = (usize, &Properties<'a>)> {
248 self.owners
249 .iter()
250 .map(|owner| (owner.signer, &owner.properties))
251 }
252 pub fn index<'p, 'q, W: Work>(
256 &'p self,
257 work: &'q W,
258 ) -> Result<Index<'p, 'q, 'v, 's, S>, structured_ltv::material::IndexError<Node>>
259 where
260 'v: 'q,
261 'a: 'p,
262 {
263 let archive = self.content.member().archive();
264 let query = archive.ltv_query(work);
265 let node = Node::Root;
266 let mut index = structured_ltv::material::Index::new(ISSUER, node, &self.findings, &query)?;
267 let mut ordinal = 0usize;
268 self.append(&mut index, &query, &mut ordinal)?;
269 Ok(index)
270 }
271 fn append<'p, 'q, O>(
272 &'p self,
273 index: &mut IndexOf<'p, 'q, 'v, 's, S, O>,
274 query: &crate::source::Query<'q, 'v, 's, S>,
275 ordinal: &mut usize,
276 ) -> Result<(), structured_ltv::material::IndexError<Node>>
277 where
278 'a: 'p,
279 {
280 let node = Node::Root;
281 let mut add = |artifact: ArtifactRef<'_, 'p>, placement| {
282 index.admit(query)?;
283 let location = Location {
284 snapshot: query.snapshot(),
285 node: Node::Member(self.member().ordinal()),
286 };
287 let source = ArtifactSource::Material(*ordinal);
288 *ordinal =
289 ordinal
290 .checked_add(1)
291 .ok_or(structured_ltv::material::IndexError::Resource {
292 signature: node,
293 cause: structured_asn1::memory::MemoryError::WorkExceeded,
294 })?;
295 index.insert(
296 ISSUER,
297 artifact,
298 Occurrence::new(source, location, placement),
299 query,
300 )
301 };
302 for (choice, value) in self.message.certificate_choices().iter().enumerate() {
303 query
304 .admit(node, 1)
305 .map_err(structured_ltv::material::IndexError::Scope)?;
306 if let CertificateChoice::X509(certificate) = value {
307 add(
308 ArtifactRef::Certificate(&self.message.certificates()[*certificate]),
309 Placement::Certificate(choice),
310 )?;
311 }
312 }
313 for status in &self.statuses {
314 query
315 .admit(node, 1)
316 .map_err(structured_ltv::material::IndexError::Scope)?;
317 match &status.value {
318 revocation::Reading::Crl(value) => add(
319 ArtifactRef::Crl(value),
320 Placement::Revocation(status.choice),
321 )?,
322 revocation::Reading::Ocsp(value) => add(
323 ArtifactRef::Ocsp(value),
324 Placement::Revocation(status.choice),
325 )?,
326 revocation::Reading::Open { .. } => {
327 unreachable!("open choices are retained as findings")
328 }
329 }
330 }
331 for owner in &self.owners {
332 query
333 .admit(node, 1)
334 .map_err(structured_ltv::material::IndexError::Scope)?;
335 let Some(values) = owner.properties.legacy_values() else {
336 continue;
337 };
338 let unsigned = |property: Span| {
339 owner.unsigned.is_some_and(|span| {
340 property.start >= span.start && property.end() <= span.end()
341 })
342 };
343 for entry in values.certificates() {
344 add(
345 ArtifactRef::Certificate(&entry.value),
346 Placement::CertificateValues {
347 signer: owner.signer,
348 ordinal: entry.ordinal,
349 unsigned: unsigned(entry.property.span()),
350 },
351 )?;
352 }
353 for entry in values.crls() {
354 add(
355 ArtifactRef::Crl(&entry.value),
356 Placement::CrlValues {
357 signer: owner.signer,
358 ordinal: entry.ordinal,
359 unsigned: unsigned(entry.property.span()),
360 },
361 )?;
362 }
363 for entry in values.ocsp_responses() {
364 add(
365 ArtifactRef::Ocsp(&entry.value),
366 Placement::OcspValues {
367 signer: owner.signer,
368 ordinal: entry.ordinal,
369 unsigned: unsigned(entry.property.span()),
370 },
371 )?;
372 }
373 }
374 Ok(())
375 }
376}
377
378pub struct SourceSlot<'p, 'a, 'v, 's, S: RangeSource> {
380 member: MemberId<S::Document, S::Generation>,
381 pool: Option<&'p Pool<'a, 'v, 's, S>>,
382}
383impl<'p, 'a, 'v, 's, S: RangeSource> SourceSlot<'p, 'a, 'v, 's, S> {
384 #[must_use]
386 pub fn member(&self) -> MemberId<S::Document, S::Generation> {
387 self.member
388 }
389 #[must_use]
391 pub fn pool(&self) -> Option<&'p Pool<'a, 'v, 's, S>> {
392 self.pool
393 }
394}
395pub struct Open<'p, D, G> {
397 pub member: MemberId<D, G>,
399 pub finding: &'p Finding,
401}
402pub struct Sources<'p, 'a, 'v, 's, S: RangeSource> {
406 archive: &'v crate::zip::Archive<'s, S>,
407 slots: OwnedVec<SourceSlot<'p, 'a, 'v, 's, S>>,
408 open: OwnedVec<Open<'p, S::Document, S::Generation>>,
409 complete: bool,
410}
411#[derive(Debug)]
413pub enum SourcesError<E> {
414 Archive(ArchiveError<E>),
416 Foreign,
418 Duplicate,
420}
421impl<'p, 'a, 'v, 's, S: RangeSource> Sources<'p, 'a, 'v, 's, S> {
422 pub fn read<W: Work>(
428 census: &crate::layout::Census<'v, 's, S>,
429 pools: &[&'p Pool<'a, 'v, 's, S>],
430 work: &W,
431 ) -> Result<Self, SourcesError<S::Error>> {
432 use crate::layout::Role;
433 let archive = census.archive();
434 let execution = archive.session().query(work);
435 archive
436 .admit(&execution, archive.directory_span())
437 .map_err(SourcesError::Archive)?;
438 let refusal = |cause| SourcesError::Archive(resource(archive.directory_span(), cause));
439 let mut selected = execution.map();
440 for (index, pool) in pools.iter().enumerate() {
441 execution.charge_work(1).map_err(refusal)?;
442 if !core::ptr::eq(pool.content.member().archive(), archive) {
443 return Err(SourcesError::Foreign);
444 }
445 let role = census
446 .occurrence(pool.member().ordinal(), &execution)
447 .map_err(SourcesError::Archive)?
448 .map(|(_, role)| role);
449 if !matches!(role, Some(Role::Cades | Role::Timestamp)) {
450 return Err(SourcesError::Foreign);
451 }
452 if selected
453 .try_insert(pool.member().ordinal(), index)
454 .map_err(refusal)?
455 .is_some()
456 {
457 return Err(SourcesError::Duplicate);
458 }
459 }
460 let mut slots = execution.vector();
461 let mut open = execution.vector();
462 let mut complete = true;
463 for ordinal in 0..census.len() {
464 let (member, role) = census
465 .occurrence(ordinal, &execution)
466 .map_err(SourcesError::Archive)?
467 .expect("original census bounds its ordinal");
468 if !matches!(role, Role::Cades | Role::Timestamp) {
469 continue;
470 }
471 let pool = selected
472 .get(&ordinal, &execution)
473 .map_err(refusal)?
474 .map(|index| pools[*index]);
475 if let Some(pool) = pool {
476 for finding in pool.findings() {
477 open.try_push(Open {
478 member: member.id(),
479 finding,
480 })
481 .map_err(refusal)?;
482 }
483 } else {
484 complete = false;
485 }
486 slots
487 .try_push(SourceSlot {
488 member: member.id(),
489 pool,
490 })
491 .map_err(refusal)?;
492 }
493 archive
494 .admit(&execution, archive.directory_span())
495 .map_err(SourcesError::Archive)?;
496 Ok(Self {
497 archive,
498 slots: slots.into_owned(),
499 open: open.into_owned(),
500 complete,
501 })
502 }
503 #[must_use]
505 pub fn slots(&self) -> &[SourceSlot<'p, 'a, 'v, 's, S>] {
506 &self.slots
507 }
508 #[must_use]
511 pub fn all_sources_inspected(&self) -> bool {
512 self.complete
513 }
514 #[must_use]
516 pub fn open(&self) -> &[Open<'p, S::Document, S::Generation>] {
517 &self.open
518 }
519 pub fn index<'g, 'q, W: Work>(
522 &'g self,
523 work: &'q W,
524 ) -> SourcesIndexResult<'g, 'q, 'p, 'v, 's, S>
525 where
526 'v: 'q,
527 'a: 'g,
528 'p: 'g,
529 {
530 let query = self.archive.ltv_query(work);
531 let mut index =
532 structured_ltv::material::Index::new(ISSUER, Node::Root, &self.open, &query)?;
533 let mut ordinal = 0usize;
534 for slot in &self.slots {
535 index.admit(&query)?;
536 if let Some(pool) = slot.pool {
537 pool.append(&mut index, &query, &mut ordinal)?;
538 }
539 }
540 Ok(index)
541 }
542}
543
544pub type SourcesIndexResult<'g, 'q, 'p, 'v, 's, S> = Result<
546 IndexOf<
547 'g,
548 'q,
549 'v,
550 's,
551 S,
552 Open<'p, <S as RangeSource>::Document, <S as RangeSource>::Generation>,
553 >,
554 structured_ltv::material::IndexError<Node>,
555>;