use super::{ArchiveError, Archived, ArtifactRef, Index, IndexError};
use crate::binary::Location;
use crate::timestamp::{ElementCoverage, Verified};
use crate::xml::{Access, Xml};
use structured_asn1::memory::{HashWork, OwnedVec, Work};
use structured_pkix::{crl, ocsp};
pub struct AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G> {
inputs: &'closure crate::pki::AuthorityClosure<'view, 'certificate, 'document>,
paths: OwnedVec<PathMaterial<'view, 'certificate, 'document, N, D, G>>,
all_covered: bool,
}
impl<'closure, 'view, 'certificate, 'document, N, D, G>
AuthorityMaterial<'closure, 'view, 'certificate, 'document, N, D, G>
{
#[must_use]
pub fn inputs(&self) -> &'closure crate::pki::AuthorityClosure<'view, 'certificate, 'document> {
self.inputs
}
#[must_use]
pub fn paths(&self) -> &[PathMaterial<'view, 'certificate, 'document, N, D, G>] {
&self.paths
}
#[must_use]
pub fn all_artifacts_covered(&self) -> bool {
self.all_covered
}
}
pub type AuthorityArchiveResult<'closure, 'view, 'certificate, 'document, X> = Result<
AuthorityMaterial<
'closure,
'view,
'certificate,
'document,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
ArchiveError<<X as Xml>::Error, <X as Xml>::Id>,
>;
pub fn archived_authorities<
'closure,
'view,
'certificate,
'document,
X: Xml,
W: Work,
Q: Work,
C: Work,
>(
inputs: &'closure crate::pki::AuthorityClosure<'view, 'certificate, 'document>,
index: &Index<'_, X::Id, X::Document, X::Generation, W>,
coverage: &ElementCoverage<X::Id, X::Document, X::Generation, C>,
evidence: &Verified<X::Id, X::Document, X::Generation>,
access: &Access<'_, X, Q>,
) -> AuthorityArchiveResult<'closure, 'view, 'certificate, 'document, X>
where
X::Id: HashWork,
{
let signature = evidence.signature();
let resource = |cause| ArchiveError::Index(IndexError::Resource { signature, cause });
let mut paths = access
.query(signature, |_, _, work| Ok(work.vector()))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?;
paths
.try_reserve_exact(inputs.nodes().len())
.map_err(resource)?;
let mut all_covered = true;
for node in inputs.nodes() {
access
.query(signature, |_, _, work| Ok(work.charge_work(1)))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?
.map_err(resource)?;
let material = archived_path(node.statuses, index, coverage, evidence, access)?;
if !material.all_artifacts_covered() {
all_covered = false;
}
paths.try_push(material).map_err(resource)?;
}
Ok(AuthorityMaterial {
inputs,
paths: paths.into_owned(),
all_covered,
})
}
pub struct PathMaterial<'view, 'certificate, 'document, N, D, G> {
inputs: &'view crate::pki::PathStatuses<'view, 'certificate, 'document>,
certificates: super::PathCertificates<'view, 'document, N, D, G>,
statuses: OwnedVec<StatusMaterial<'view, 'certificate, 'document, N, D, G>>,
all_covered: bool,
}
impl<'view, 'certificate, 'document, N, D, G>
PathMaterial<'view, 'certificate, 'document, N, D, G>
{
#[must_use]
pub fn inputs(&self) -> &'view crate::pki::PathStatuses<'view, 'certificate, 'document> {
self.inputs
}
#[must_use]
pub fn certificates(&self) -> &super::PathCertificates<'view, 'document, N, D, G> {
&self.certificates
}
#[must_use]
pub fn statuses(&self) -> &[StatusMaterial<'view, 'certificate, 'document, N, D, G>] {
&self.statuses
}
#[must_use]
pub fn all_artifacts_covered(&self) -> bool {
self.all_covered
}
}
pub type PathArchiveResult<'view, 'certificate, 'document, X> = Result<
PathMaterial<
'view,
'certificate,
'document,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
ArchiveError<<X as Xml>::Error, <X as Xml>::Id>,
>;
pub fn archived_path<'view, 'certificate, 'document, X: Xml, W: Work, Q: Work, C: Work>(
inputs: &'view crate::pki::PathStatuses<'view, 'certificate, 'document>,
index: &Index<'_, X::Id, X::Document, X::Generation, W>,
coverage: &ElementCoverage<X::Id, X::Document, X::Generation, C>,
evidence: &Verified<X::Id, X::Document, X::Generation>,
access: &Access<'_, X, Q>,
) -> PathArchiveResult<'view, 'certificate, 'document, X>
where
X::Id: HashWork,
{
let certificates = index.archived_path(inputs.path(), coverage, evidence, access)?;
let mut all_covered = certificates.all_certificates_covered();
let signature = evidence.signature();
let resource = |cause| ArchiveError::Index(IndexError::Resource { signature, cause });
let mut statuses = access
.query(signature, |_, _, work| Ok(work.vector()))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?;
let mut count = 0;
for input in inputs.entries() {
access
.query(signature, |_, _, work| Ok(work.charge_work(1)))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?
.map_err(resource)?;
if !matches!(input, crate::pki::MemberStatus::ResponderNoCheck(_)) {
count += 1;
}
}
statuses.try_reserve_exact(count).map_err(resource)?;
for input in inputs.entries() {
access
.query(signature, |_, _, work| Ok(work.charge_work(1)))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?
.map_err(resource)?;
let Some(source) = member_source(input) else {
continue;
};
let status = archived(source, index, coverage, evidence, access)?;
if !status.all_artifacts_covered() {
all_covered = false;
}
statuses.try_push(status).map_err(resource)?;
}
Ok(PathMaterial {
inputs,
certificates,
statuses: statuses.into_owned(),
all_covered,
})
}
#[derive(Clone, Copy)]
pub enum StatusSource<'view, 'certificate, 'document> {
Crl(&'view crl::Coverage<'certificate, 'document>),
CrlReading(crate::pki::CrlReading<'view, 'certificate, 'document>),
Ocsp(&'view ocsp::BoundEvidence<'certificate, 'document, 'certificate, 'document>),
}
pub(crate) fn member_source<'view, 'certificate, 'document>(
input: &'view crate::pki::MemberStatus<'view, 'certificate, 'document>,
) -> Option<StatusSource<'view, 'certificate, 'document>> {
match input {
crate::pki::MemberStatus::Crl(bound) => Some(StatusSource::Crl(bound.coverage())),
crate::pki::MemberStatus::CrlReading(bound) => {
Some(StatusSource::CrlReading(bound.reading()))
}
crate::pki::MemberStatus::Ocsp(bound) => Some(StatusSource::Ocsp(bound.evidence())),
crate::pki::MemberStatus::ResponderNoCheck(_) => None,
}
}
impl<'certificate, 'document> StatusSource<'_, 'certificate, 'document> {
#[must_use]
pub fn target(self) -> &'certificate structured_pkix::Certificate<'document> {
match self {
Self::Crl(qualified) => qualified.target(),
Self::CrlReading(reading) => reading.target(),
Self::Ocsp(qualified) => qualified.target(),
}
}
#[must_use]
pub fn evaluated_at(self) -> structured_pkix::temporal::Reading {
match self {
Self::Crl(qualified) => qualified.evaluated_at(),
Self::CrlReading(reading) => reading.evaluated_at(),
Self::Ocsp(qualified) => qualified.evaluated_at(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StatusPart {
CompleteCrl,
DeltaCrl,
OcspResponse,
SigningCertificate,
}
#[derive(Debug)]
pub struct ArchivedStatus<N, D, G> {
pub source: usize,
pub part: StatusPart,
pub availability: Archived<N, D, G>,
}
pub struct StatusMaterial<'view, 'certificate, 'document, N, D, G> {
source: StatusSource<'view, 'certificate, 'document>,
archive: Location<N, D, G>,
entries: OwnedVec<ArchivedStatus<N, D, G>>,
all_covered: bool,
}
pub type StatusArchiveResult<'view, 'certificate, 'document, X> = Result<
StatusMaterial<
'view,
'certificate,
'document,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
ArchiveError<<X as Xml>::Error, <X as Xml>::Id>,
>;
impl<'view, 'certificate, 'document, N: Copy, D: Copy, G: Copy>
StatusMaterial<'view, 'certificate, 'document, N, D, G>
{
#[must_use]
pub fn source(&self) -> StatusSource<'view, 'certificate, 'document> {
self.source
}
#[must_use]
pub fn archive(&self) -> Location<N, D, G> {
self.archive
}
#[must_use]
pub fn entries(&self) -> &[ArchivedStatus<N, D, G>] {
&self.entries
}
#[must_use]
pub fn all_artifacts_covered(&self) -> bool {
self.all_covered
}
}
pub fn archived<'view, 'certificate, 'document, X: Xml, W: Work, Q: Work, C: Work>(
source: StatusSource<'view, 'certificate, 'document>,
index: &Index<'_, X::Id, X::Document, X::Generation, W>,
coverage: &ElementCoverage<X::Id, X::Document, X::Generation, C>,
evidence: &Verified<X::Id, X::Document, X::Generation>,
access: &Access<'_, X, Q>,
) -> StatusArchiveResult<'view, 'certificate, 'document, X>
where
X::Id: HashWork,
{
let signature = evidence.signature();
let mut entries = access
.query(signature, |_, _, work| Ok(work.vector()))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?;
let mut all_covered = true;
let mut record = |artifact, ordinal, part| {
let availability = index.archived(artifact, coverage, evidence, access)?;
if !matches!(availability, Archived::Present(_)) {
all_covered = false;
}
entries
.try_push(ArchivedStatus {
source: ordinal,
part,
availability,
})
.map_err(|cause| ArchiveError::Index(IndexError::Resource { signature, cause }))
};
visit_artifacts(source, signature, access, &mut record).map_err(|cause| match cause {
ArtifactVisitError::Access(cause) => ArchiveError::Index(IndexError::Access(cause)),
ArtifactVisitError::Resource(cause) => {
ArchiveError::Index(IndexError::Resource { signature, cause })
}
ArtifactVisitError::Visitor(cause) => cause,
})?;
Ok(StatusMaterial {
source,
archive: evidence.token(),
entries: entries.into_owned(),
all_covered,
})
}
fn record_crl<'certificate, 'document, E>(
source: &crl::EvidenceSource<'certificate, 'document>,
ordinal: usize,
mut record: impl FnMut(ArtifactRef<'certificate, 'document>, usize, StatusPart) -> Result<(), E>,
) -> Result<(), E> {
match source {
crl::EvidenceSource::Complete(complete) => {
record(
ArtifactRef::Crl(complete.crl()),
ordinal,
StatusPart::CompleteCrl,
)?;
record(
ArtifactRef::Certificate(complete.issuer()),
ordinal,
StatusPart::SigningCertificate,
)?;
}
crl::EvidenceSource::Delta { base, delta } => {
record(
ArtifactRef::Crl(base.crl()),
ordinal,
StatusPart::CompleteCrl,
)?;
record(ArtifactRef::Crl(delta.crl()), ordinal, StatusPart::DeltaCrl)?;
record(
ArtifactRef::Certificate(base.issuer()),
ordinal,
StatusPart::SigningCertificate,
)?;
record(
ArtifactRef::Certificate(delta.issuer()),
ordinal,
StatusPart::SigningCertificate,
)?;
}
}
Ok(())
}
pub(crate) enum ArtifactVisitError<E, V, N> {
Access(crate::xml::AccessError<E, N>),
Resource(structured_asn1::memory::MemoryError),
Visitor(V),
}
pub(crate) fn visit_artifacts<'certificate, 'document, X: Xml, W: Work, V>(
source: StatusSource<'_, 'certificate, 'document>,
signature: X::Id,
access: &Access<'_, X, W>,
mut record: impl FnMut(ArtifactRef<'certificate, 'document>, usize, StatusPart) -> Result<(), V>,
) -> Result<(), ArtifactVisitError<X::Error, V, X::Id>> {
let pay = || {
access
.query(signature, |_, _, work| Ok(work.charge_work(1)))
.map_err(ArtifactVisitError::Access)?
.map_err(ArtifactVisitError::Resource)
};
let record = |artifact, ordinal, part| {
record(artifact, ordinal, part).map_err(ArtifactVisitError::Visitor)
};
walk_artifacts(source, pay, record)
}
pub(crate) fn visit_artifacts_in<'c, 'd, W: Work>(
source: StatusSource<'_, 'c, 'd>,
work: &W,
record: impl FnMut(
ArtifactRef<'c, 'd>,
usize,
StatusPart,
) -> Result<(), structured_asn1::memory::MemoryError>,
) -> Result<(), structured_asn1::memory::MemoryError> {
walk_artifacts(source, || work.charge_work(1), record)
}
fn walk_artifacts<'c, 'd, E>(
source: StatusSource<'_, 'c, 'd>,
mut pay: impl FnMut() -> Result<(), E>,
mut record: impl FnMut(ArtifactRef<'c, 'd>, usize, StatusPart) -> Result<(), E>,
) -> Result<(), E> {
match source {
StatusSource::Crl(qualified) => {
for (ordinal, entry) in qualified.sources().iter().enumerate() {
pay()?;
record_crl(&entry.source(), ordinal, &mut record)?;
}
}
StatusSource::CrlReading(reading) => {
pay()?;
record_crl(&reading.source(), 0, &mut record)?;
}
StatusSource::Ocsp(qualified) => {
pay()?;
let signed = qualified.evidence().source().signed();
record(
ArtifactRef::Ocsp(signed.response()),
0,
StatusPart::OcspResponse,
)?;
record(
ArtifactRef::Certificate(signed.signer()),
0,
StatusPart::SigningCertificate,
)?;
}
}
Ok(())
}