use crate::{
material, pki,
timestamp::TimestampScope,
xml::{Access, Xml},
};
use structured_asn1::memory::{MemoryError, Work};
pub struct ArchiveMaterial<'a, M, C> {
x509: &'a M,
credentials: &'a C,
covered: bool,
}
impl<M, C> ArchiveMaterial<'_, M, C> {
#[must_use]
pub fn x509(&self) -> &M {
self.x509
}
#[must_use]
pub fn credentials(&self) -> &C {
self.credentials
}
#[must_use]
pub fn all_material_covered(&self) -> bool {
self.covered
}
}
type X509<'a, 'r, 'v, 'c, 'd, X> = material::ParticipantMaterial<
'a,
'r,
'v,
'c,
'd,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>;
type Credentials<'a, 'p, 'd, A, X> = material::credentials::Report<
'a,
'p,
'd,
A,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>;
pub type ArchiveMaterialResult<'a, M, C, X> =
Result<ArchiveMaterial<'a, M, C>, pki::ParticipantError<<X as Xml>::Error, <X as Xml>::Id>>;
pub fn archive_material<'s, 'a, 'r, 'v, 'c, 'd, 'au, 'p, A, X: Xml, W: Work>(
x509: &'s X509<'a, 'r, 'v, 'c, 'd, X>,
credentials: &'s Credentials<'au, 'p, 'd, A, X>,
access: &Access<'_, X, W>,
) -> ArchiveMaterialResult<'s, X509<'a, 'r, 'v, 'c, 'd, X>, Credentials<'au, 'p, 'd, A, X>, X> {
let required = x509.inputs().requirements();
let scope = required.scope();
let original = scope.location();
let supplied = credentials.scope();
let foreign = || pki::ParticipantError::Foreign {
signature: required.signature(),
};
if !matches!(scope, TimestampScope::BeforeArchive(_))
|| !matches!(supplied, TimestampScope::BeforeArchive(_))
|| original.snapshot != access.snapshot()
|| supplied.location().snapshot != original.snapshot
|| credentials.snapshot() != original.snapshot
{
return Err(foreign());
}
access
.query(original.node, |_, _, work| Ok(work.charge_work(1)))
.map_err(pki::ParticipantError::Access)?
.map_err(|cause| pki::ParticipantError::Resource {
node: original.node,
cause,
})?;
if !required.same_credentials(credentials.credentials())
|| !access
.equal_id(original.node, supplied.location().node)
.map_err(|cause| pki::ParticipantError::Resource {
node: original.node,
cause,
})?
{
return Err(foreign());
}
Ok(ArchiveMaterial {
x509,
credentials,
covered: x509.all_x509_artifacts_covered() && credentials.all_requirements_met(),
})
}
pub struct Archives<'a, P, R> {
prepared: &'a P,
reports: &'a [&'a R],
covered: bool,
}
impl<P, R> Archives<'_, P, R> {
#[must_use]
pub fn prepared(&self) -> &P {
self.prepared
}
#[must_use]
pub fn reports(&self) -> &[&R] {
self.reports
}
#[must_use]
pub fn all_archive_material_covered(&self) -> bool {
self.covered
}
}
#[derive(Debug)]
pub enum ArchiveCensusError<E, N> {
Foreign {
node: N,
},
Missing {
property: N,
},
Extra,
Access(crate::xml::AccessError<E, N>),
Resource {
node: N,
cause: MemoryError,
},
}
type Proof<'a, 'cl, 'r, 'v, 'c, 'd, 'au, 'p, A, X> =
ArchiveMaterial<'a, X509<'cl, 'r, 'v, 'c, 'd, X>, Credentials<'au, 'p, 'd, A, X>>;
pub type ArchivesResult<'a, P, R, X> =
Result<Archives<'a, P, R>, ArchiveCensusError<<X as Xml>::Error, <X as Xml>::Id>>;
pub fn archives<'s, 'a, 'cl, 'r, 'v, 'c, 'd, 'au, 'p, A, X: Xml, W: Work>(
prepared: &'s crate::prepare::Prepared<X>,
reports: &'s [&'s Proof<'a, 'cl, 'r, 'v, 'c, 'd, 'au, 'p, A, X>],
access: &Access<'_, X, W>,
) -> ArchivesResult<'s, crate::prepare::Prepared<X>, Proof<'a, 'cl, 'r, 'v, 'c, 'd, 'au, 'p, A, X>, X>
{
let layout = prepared.bound().containers();
let signature = layout.signature();
if layout.snapshot() != access.snapshot() {
return Err(ArchiveCensusError::Foreign { node: signature });
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(ArchiveCensusError::Access)?;
let mut cursor = 0;
let mut covered = true;
for stamp in prepared.timestamps() {
let node = stamp.node();
let resource = |cause| ArchiveCensusError::Resource { node, cause };
access.execution().charge_work(1).map_err(resource)?;
if stamp.role() != crate::timestamp::Role::Archive {
continue;
}
let Some(report) = reports.get(cursor) else {
return Err(ArchiveCensusError::Missing { property: node });
};
let required = report.x509.inputs().requirements();
let location = required.scope().location();
if location.snapshot != access.snapshot()
|| !access
.equal_id(required.signature(), signature)
.map_err(resource)?
|| !access.equal_id(location.node, node).map_err(resource)?
{
return Err(ArchiveCensusError::Foreign { node });
}
if !report.covered {
covered = false;
}
cursor += 1;
}
if cursor != reports.len() {
return Err(ArchiveCensusError::Extra);
}
if cursor == 0 {
covered = false;
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(ArchiveCensusError::Access)?;
Ok(Archives {
prepared,
reports,
covered,
})
}