1use super::{ArchiveError, Archived, ArtifactRef, Coverage, Index, IndexError, PathCertificates};
4use crate::pki::{AuthorityClosure, CrlReading, MemberStatus, PathStatuses};
5use crate::source::{Location, Query, ScopeError};
6use crate::timestamp::Verified;
7use structured_asn1::memory::{HashWork, MemoryError, OwnedVec, Work};
8use structured_pkix::{crl, ocsp};
9
10pub struct AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G, P> {
14 inputs: &'closure AuthorityClosure<'view, 'certificate, 'document>,
15 paths: OwnedVec<PathMaterial<'view, 'certificate, 'document, N, D, G, P>>,
16 all_covered: bool,
17}
18
19impl<'closure, 'view, 'certificate, 'document, N, D, G, P>
20 AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G, P>
21{
22 #[must_use]
24 pub fn inputs(&self) -> &'closure AuthorityClosure<'view, 'certificate, 'document> {
25 self.inputs
26 }
27 #[must_use]
29 pub fn paths(&self) -> &[PathMaterial<'view, 'certificate, 'document, N, D, G, P>] {
30 &self.paths
31 }
32 #[must_use]
35 pub fn all_artifacts_covered(&self) -> bool {
36 self.all_covered
37 }
38}
39
40pub fn archived_authorities<'closure, 'view, 'certificate, 'document, N, D, G, P, O, W, Q, C, I>(
45 inputs: &'closure AuthorityClosure<'view, 'certificate, 'document>,
46 index: &Index<'_, N, D, G, P, O, W, I>,
47 coverage: &Coverage<N, D, G, C, I>,
48 evidence: &Verified<N, D, G, I>,
49 query: &Q,
50) -> Result<AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G, P>, ArchiveError<N>>
51where
52 N: Copy + Eq + HashWork,
53 D: Copy + Eq,
54 G: Copy + Eq,
55 P: Copy,
56 W: Work,
57 C: Work,
58 Q: Query<Id = N, Document = D, Generation = G>,
59{
60 let signature = evidence.signature();
61 let resource = |cause| ArchiveError::Index(IndexError::Resource { signature, cause });
62 let scope = |cause| ArchiveError::Index(IndexError::Scope(cause));
63 query.admit(signature, 1).map_err(scope)?;
64 let mut paths = query.execution().vector();
65 paths
66 .try_reserve_exact(inputs.nodes().len())
67 .map_err(resource)?;
68 let mut all_covered = true;
69 for node in inputs.nodes() {
70 query.admit(signature, 2).map_err(scope)?;
71 let material = archived_path(node.statuses, index, coverage, evidence, query)?;
72 if !material.all_artifacts_covered() {
73 all_covered = false;
74 }
75 paths.try_push(material).map_err(resource)?;
76 }
77 Ok(AuthorityMaterial {
78 inputs,
79 paths: paths.into_owned(),
80 all_covered,
81 })
82}
83
84pub struct PathMaterial<'view, 'certificate, 'document, N, D, G, P> {
88 inputs: &'view PathStatuses<'view, 'certificate, 'document>,
89 certificates: PathCertificates<'view, 'document, N, D, G, P>,
90 statuses: OwnedVec<StatusMaterial<'view, 'certificate, 'document, N, D, G, P>>,
91 all_covered: bool,
92}
93
94impl<'view, 'certificate, 'document, N, D, G, P>
95 PathMaterial<'view, 'certificate, 'document, N, D, G, P>
96{
97 #[must_use]
99 pub fn inputs(&self) -> &'view PathStatuses<'view, 'certificate, 'document> {
100 self.inputs
101 }
102 #[must_use]
104 pub fn certificates(&self) -> &PathCertificates<'view, 'document, N, D, G, P> {
105 &self.certificates
106 }
107 #[must_use]
111 pub fn statuses(&self) -> &[StatusMaterial<'view, 'certificate, 'document, N, D, G, P>] {
112 &self.statuses
113 }
114 #[must_use]
117 pub fn all_artifacts_covered(&self) -> bool {
118 self.all_covered
119 }
120}
121
122pub fn archived_path<'view, 'certificate, 'document, N, D, G, P, O, W, Q, C, I>(
127 inputs: &'view PathStatuses<'view, 'certificate, 'document>,
128 index: &Index<'_, N, D, G, P, O, W, I>,
129 coverage: &Coverage<N, D, G, C, I>,
130 evidence: &Verified<N, D, G, I>,
131 query: &Q,
132) -> Result<PathMaterial<'view, 'certificate, 'document, N, D, G, P>, ArchiveError<N>>
133where
134 N: Copy + Eq + HashWork,
135 D: Copy + Eq,
136 G: Copy + Eq,
137 P: Copy,
138 W: Work,
139 C: Work,
140 Q: Query<Id = N, Document = D, Generation = G>,
141{
142 let certificates = index.archived_path(inputs.path(), coverage, evidence, query)?;
143 let mut all_covered = certificates.all_certificates_covered();
144 let signature = evidence.signature();
145 let resource = |cause| ArchiveError::Index(IndexError::Resource { signature, cause });
146 let scope = |cause| ArchiveError::Index(IndexError::Scope(cause));
147 query.admit(signature, 1).map_err(scope)?;
148 let mut statuses = query.execution().vector();
149 let mut count = 0;
150 for input in inputs.entries() {
151 query.admit(signature, 2).map_err(scope)?;
152 if !matches!(input, MemberStatus::ResponderNoCheck(_)) {
153 count += 1;
154 }
155 }
156 statuses.try_reserve_exact(count).map_err(resource)?;
157 for input in inputs.entries() {
158 query.admit(signature, 2).map_err(scope)?;
159 let Some(source) = member_source(input) else {
160 continue;
161 };
162 let status = archived(source, index, coverage, evidence, query)?;
163 if !status.all_artifacts_covered() {
164 all_covered = false;
165 }
166 statuses.try_push(status).map_err(resource)?;
167 }
168 Ok(PathMaterial {
169 inputs,
170 certificates,
171 statuses: statuses.into_owned(),
172 all_covered,
173 })
174}
175
176#[derive(Clone, Copy)]
178pub enum StatusSource<'view, 'certificate, 'document> {
179 Crl(&'view crl::Coverage<'certificate, 'document>),
182 CrlReading(CrlReading<'view, 'certificate, 'document>),
185 Ocsp(&'view ocsp::BoundEvidence<'certificate, 'document, 'certificate, 'document>),
188}
189
190#[must_use]
194pub fn member_source<'view, 'certificate, 'document>(
195 input: &'view MemberStatus<'view, 'certificate, 'document>,
196) -> Option<StatusSource<'view, 'certificate, 'document>> {
197 match input {
198 MemberStatus::Crl(bound) => Some(StatusSource::Crl(bound.coverage())),
199 MemberStatus::CrlReading(bound) => Some(StatusSource::CrlReading(bound.reading())),
200 MemberStatus::Ocsp(bound) => Some(StatusSource::Ocsp(bound.evidence())),
201 MemberStatus::ResponderNoCheck(_) => None,
202 }
203}
204
205impl<'certificate, 'document> StatusSource<'_, 'certificate, 'document> {
206 #[must_use]
208 pub fn target(self) -> &'certificate structured_pkix::Certificate<'document> {
209 match self {
210 Self::Crl(qualified) => qualified.target(),
211 Self::CrlReading(reading) => reading.target(),
212 Self::Ocsp(qualified) => qualified.target(),
213 }
214 }
215 #[must_use]
217 pub fn evaluated_at(self) -> structured_pkix::temporal::Reading {
218 match self {
219 Self::Crl(qualified) => qualified.evaluated_at(),
220 Self::CrlReading(reading) => reading.evaluated_at(),
221 Self::Ocsp(qualified) => qualified.evaluated_at(),
222 }
223 }
224}
225
226#[derive(Debug, Clone, Copy, PartialEq, Eq)]
228pub enum StatusPart {
229 CompleteCrl,
231 DeltaCrl,
233 OcspResponse,
237 SigningCertificate,
239}
240
241#[derive(Debug)]
243pub struct ArchivedStatus<N, D, G, P> {
244 pub source: usize,
246 pub part: StatusPart,
248 pub availability: Archived<N, D, G, P>,
250}
251
252pub struct StatusMaterial<'view, 'certificate, 'document, N, D, G, P> {
256 source: StatusSource<'view, 'certificate, 'document>,
257 archive: Location<N, D, G>,
258 entries: OwnedVec<ArchivedStatus<N, D, G, P>>,
259 all_covered: bool,
260}
261
262impl<'view, 'certificate, 'document, N: Copy, D: Copy, G: Copy, P>
263 StatusMaterial<'view, 'certificate, 'document, N, D, G, P>
264{
265 #[must_use]
267 pub fn source(&self) -> StatusSource<'view, 'certificate, 'document> {
268 self.source
269 }
270 #[must_use]
272 pub fn archive(&self) -> Location<N, D, G> {
273 self.archive
274 }
275 #[must_use]
277 pub fn entries(&self) -> &[ArchivedStatus<N, D, G, P>] {
278 &self.entries
279 }
280 #[must_use]
283 pub fn all_artifacts_covered(&self) -> bool {
284 self.all_covered
285 }
286}
287
288pub fn archived<'view, 'certificate, 'document, N, D, G, P, O, W, Q, C, I>(
300 source: StatusSource<'view, 'certificate, 'document>,
301 index: &Index<'_, N, D, G, P, O, W, I>,
302 coverage: &Coverage<N, D, G, C, I>,
303 evidence: &Verified<N, D, G, I>,
304 query: &Q,
305) -> Result<StatusMaterial<'view, 'certificate, 'document, N, D, G, P>, ArchiveError<N>>
306where
307 N: Copy + Eq + HashWork,
308 D: Copy + Eq,
309 G: Copy + Eq,
310 P: Copy,
311 W: Work,
312 C: Work,
313 Q: Query<Id = N, Document = D, Generation = G>,
314{
315 let signature = evidence.signature();
316 query
317 .admit(signature, 1)
318 .map_err(|cause| ArchiveError::Index(IndexError::Scope(cause)))?;
319 let mut entries = query.execution().vector();
320 let mut all_covered = true;
321 let mut record = |artifact, ordinal, part| {
322 let availability = index.archived(artifact, coverage, evidence, query)?;
323 if !matches!(availability, Archived::Present(_)) {
324 all_covered = false;
325 }
326 entries
327 .try_push(ArchivedStatus {
328 source: ordinal,
329 part,
330 availability,
331 })
332 .map_err(|cause| ArchiveError::Index(IndexError::Resource { signature, cause }))
333 };
334 visit_artifacts(source, signature, query, &mut record).map_err(|cause| match cause {
335 ArtifactVisitError::Scope(cause) => ArchiveError::Index(IndexError::Scope(cause)),
336 ArtifactVisitError::Visitor(cause) => cause,
337 })?;
338 Ok(StatusMaterial {
339 source,
340 archive: evidence.token(),
341 entries: entries.into_owned(),
342 all_covered,
343 })
344}
345
346fn record_crl<'certificate, 'document, E>(
347 source: &crl::EvidenceSource<'certificate, 'document>,
348 ordinal: usize,
349 mut record: impl FnMut(ArtifactRef<'certificate, 'document>, usize, StatusPart) -> Result<(), E>,
350) -> Result<(), E> {
351 match source {
352 crl::EvidenceSource::Complete(complete) => {
353 record(
354 ArtifactRef::Crl(complete.crl()),
355 ordinal,
356 StatusPart::CompleteCrl,
357 )?;
358 record(
359 ArtifactRef::Certificate(complete.issuer()),
360 ordinal,
361 StatusPart::SigningCertificate,
362 )?;
363 }
364 crl::EvidenceSource::Delta { base, delta } => {
365 record(
366 ArtifactRef::Crl(base.crl()),
367 ordinal,
368 StatusPart::CompleteCrl,
369 )?;
370 record(ArtifactRef::Crl(delta.crl()), ordinal, StatusPart::DeltaCrl)?;
371 record(
372 ArtifactRef::Certificate(base.issuer()),
373 ordinal,
374 StatusPart::SigningCertificate,
375 )?;
376 record(
377 ArtifactRef::Certificate(delta.issuer()),
378 ordinal,
379 StatusPart::SigningCertificate,
380 )?;
381 }
382 }
383 Ok(())
384}
385
386#[derive(Debug)]
388pub enum ArtifactVisitError<V, N> {
389 Scope(ScopeError<N>),
391 Visitor(V),
393}
394
395pub fn visit_artifacts<'certificate, 'document, Q: Query, V>(
400 source: StatusSource<'_, 'certificate, 'document>,
401 signature: Q::Id,
402 query: &Q,
403 mut record: impl FnMut(ArtifactRef<'certificate, 'document>, usize, StatusPart) -> Result<(), V>,
404) -> Result<(), ArtifactVisitError<V, Q::Id>> {
405 let pay = || query.admit(signature, 2).map_err(ArtifactVisitError::Scope);
406 let record = |artifact, ordinal, part| {
407 record(artifact, ordinal, part).map_err(ArtifactVisitError::Visitor)
408 };
409 walk_artifacts(source, pay, record)
410}
411
412pub fn visit_artifacts_in<'c, 'd, W: Work>(
415 source: StatusSource<'_, 'c, 'd>,
416 work: &W,
417 record: impl FnMut(ArtifactRef<'c, 'd>, usize, StatusPart) -> Result<(), MemoryError>,
418) -> Result<(), MemoryError> {
419 walk_artifacts(source, || work.charge_work(1), record)
420}
421
422fn walk_artifacts<'c, 'd, E>(
423 source: StatusSource<'_, 'c, 'd>,
424 mut pay: impl FnMut() -> Result<(), E>,
425 mut record: impl FnMut(ArtifactRef<'c, 'd>, usize, StatusPart) -> Result<(), E>,
426) -> Result<(), E> {
427 match source {
428 StatusSource::Crl(qualified) => {
429 for (ordinal, entry) in qualified.sources().iter().enumerate() {
430 pay()?;
431 record_crl(&entry.source(), ordinal, &mut record)?;
432 }
433 }
434 StatusSource::CrlReading(reading) => {
435 pay()?;
436 record_crl(&reading.source(), 0, &mut record)?;
437 }
438 StatusSource::Ocsp(qualified) => {
439 pay()?;
440 let signed = qualified.evidence().source().signed();
441 record(
442 ArtifactRef::Ocsp(signed.response()),
443 0,
444 StatusPart::OcspResponse,
445 )?;
446 record(
447 ArtifactRef::Certificate(signed.signer()),
448 0,
449 StatusPart::SigningCertificate,
450 )?;
451 }
452 }
453 Ok(())
454}