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,
235 SigningCertificate,
237}
238
239#[derive(Debug)]
241pub struct ArchivedStatus<N, D, G, P> {
242 pub source: usize,
244 pub part: StatusPart,
246 pub availability: Archived<N, D, G, P>,
248}
249
250pub struct StatusMaterial<'view, 'certificate, 'document, N, D, G, P> {
254 source: StatusSource<'view, 'certificate, 'document>,
255 archive: Location<N, D, G>,
256 entries: OwnedVec<ArchivedStatus<N, D, G, P>>,
257 all_covered: bool,
258}
259
260impl<'view, 'certificate, 'document, N: Copy, D: Copy, G: Copy, P>
261 StatusMaterial<'view, 'certificate, 'document, N, D, G, P>
262{
263 #[must_use]
265 pub fn source(&self) -> StatusSource<'view, 'certificate, 'document> {
266 self.source
267 }
268 #[must_use]
270 pub fn archive(&self) -> Location<N, D, G> {
271 self.archive
272 }
273 #[must_use]
275 pub fn entries(&self) -> &[ArchivedStatus<N, D, G, P>] {
276 &self.entries
277 }
278 #[must_use]
281 pub fn all_artifacts_covered(&self) -> bool {
282 self.all_covered
283 }
284}
285
286pub fn archived<'view, 'certificate, 'document, N, D, G, P, O, W, Q, C, I>(
296 source: StatusSource<'view, 'certificate, 'document>,
297 index: &Index<'_, N, D, G, P, O, W, I>,
298 coverage: &Coverage<N, D, G, C, I>,
299 evidence: &Verified<N, D, G, I>,
300 query: &Q,
301) -> Result<StatusMaterial<'view, 'certificate, 'document, N, D, G, P>, ArchiveError<N>>
302where
303 N: Copy + Eq + HashWork,
304 D: Copy + Eq,
305 G: Copy + Eq,
306 P: Copy,
307 W: Work,
308 C: Work,
309 Q: Query<Id = N, Document = D, Generation = G>,
310{
311 let signature = evidence.signature();
312 query
313 .admit(signature, 1)
314 .map_err(|cause| ArchiveError::Index(IndexError::Scope(cause)))?;
315 let mut entries = query.execution().vector();
316 let mut all_covered = true;
317 let mut record = |artifact, ordinal, part| {
318 let availability = index.archived(artifact, coverage, evidence, query)?;
319 if !matches!(availability, Archived::Present(_)) {
320 all_covered = false;
321 }
322 entries
323 .try_push(ArchivedStatus {
324 source: ordinal,
325 part,
326 availability,
327 })
328 .map_err(|cause| ArchiveError::Index(IndexError::Resource { signature, cause }))
329 };
330 visit_artifacts(source, signature, query, &mut record).map_err(|cause| match cause {
331 ArtifactVisitError::Scope(cause) => ArchiveError::Index(IndexError::Scope(cause)),
332 ArtifactVisitError::Visitor(cause) => cause,
333 })?;
334 Ok(StatusMaterial {
335 source,
336 archive: evidence.token(),
337 entries: entries.into_owned(),
338 all_covered,
339 })
340}
341
342fn record_crl<'certificate, 'document, E>(
343 source: &crl::EvidenceSource<'certificate, 'document>,
344 ordinal: usize,
345 mut record: impl FnMut(ArtifactRef<'certificate, 'document>, usize, StatusPart) -> Result<(), E>,
346) -> Result<(), E> {
347 match source {
348 crl::EvidenceSource::Complete(complete) => {
349 record(
350 ArtifactRef::Crl(complete.crl()),
351 ordinal,
352 StatusPart::CompleteCrl,
353 )?;
354 record(
355 ArtifactRef::Certificate(complete.issuer()),
356 ordinal,
357 StatusPart::SigningCertificate,
358 )?;
359 }
360 crl::EvidenceSource::Delta { base, delta } => {
361 record(
362 ArtifactRef::Crl(base.crl()),
363 ordinal,
364 StatusPart::CompleteCrl,
365 )?;
366 record(ArtifactRef::Crl(delta.crl()), ordinal, StatusPart::DeltaCrl)?;
367 record(
368 ArtifactRef::Certificate(base.issuer()),
369 ordinal,
370 StatusPart::SigningCertificate,
371 )?;
372 record(
373 ArtifactRef::Certificate(delta.issuer()),
374 ordinal,
375 StatusPart::SigningCertificate,
376 )?;
377 }
378 }
379 Ok(())
380}
381
382#[derive(Debug)]
384pub enum ArtifactVisitError<V, N> {
385 Scope(ScopeError<N>),
387 Visitor(V),
389}
390
391pub fn visit_artifacts<'certificate, 'document, Q: Query, V>(
396 source: StatusSource<'_, 'certificate, 'document>,
397 signature: Q::Id,
398 query: &Q,
399 mut record: impl FnMut(ArtifactRef<'certificate, 'document>, usize, StatusPart) -> Result<(), V>,
400) -> Result<(), ArtifactVisitError<V, Q::Id>> {
401 let pay = || query.admit(signature, 2).map_err(ArtifactVisitError::Scope);
402 let record = |artifact, ordinal, part| {
403 record(artifact, ordinal, part).map_err(ArtifactVisitError::Visitor)
404 };
405 walk_artifacts(source, pay, record)
406}
407
408pub fn visit_artifacts_in<'c, 'd, W: Work>(
411 source: StatusSource<'_, 'c, 'd>,
412 work: &W,
413 record: impl FnMut(ArtifactRef<'c, 'd>, usize, StatusPart) -> Result<(), MemoryError>,
414) -> Result<(), MemoryError> {
415 walk_artifacts(source, || work.charge_work(1), record)
416}
417
418fn walk_artifacts<'c, 'd, E>(
419 source: StatusSource<'_, 'c, 'd>,
420 mut pay: impl FnMut() -> Result<(), E>,
421 mut record: impl FnMut(ArtifactRef<'c, 'd>, usize, StatusPart) -> Result<(), E>,
422) -> Result<(), E> {
423 match source {
424 StatusSource::Crl(qualified) => {
425 for (ordinal, entry) in qualified.sources().iter().enumerate() {
426 pay()?;
427 record_crl(&entry.source(), ordinal, &mut record)?;
428 }
429 }
430 StatusSource::CrlReading(reading) => {
431 pay()?;
432 record_crl(&reading.source(), 0, &mut record)?;
433 }
434 StatusSource::Ocsp(qualified) => {
435 pay()?;
436 let signed = qualified.evidence().source().signed();
437 record(
438 ArtifactRef::Ocsp(signed.response()),
439 0,
440 StatusPart::OcspResponse,
441 )?;
442 record(
443 ArtifactRef::Certificate(signed.signer()),
444 0,
445 StatusPart::SigningCertificate,
446 )?;
447 }
448 }
449 Ok(())
450}