use super::{ArchiveCensus, CredentialMaterial};
use crate::material::ParticipantMaterial;
use crate::pki::ParticipantError;
use crate::source::{Query, ScopeError};
use crate::timestamp::TimestampScope;
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, P, Q, I> = ParticipantMaterial<
'a,
'r,
'v,
'c,
'd,
<Q as Query>::Id,
<Q as Query>::Document,
<Q as Query>::Generation,
P,
I,
>;
pub type ArchiveMaterialResult<'a, M, C, N> =
Result<ArchiveMaterial<'a, M, C>, ParticipantError<N>>;
pub fn archive_material<'s, 'a, 'r, 'v, 'c, 'd, P, C, Q, I>(
x509: &'s X509<'a, 'r, 'v, 'c, 'd, P, Q, I>,
credentials: &'s C,
query: &Q,
) -> ArchiveMaterialResult<'s, X509<'a, 'r, 'v, 'c, 'd, P, Q, I>, C, Q::Id>
where
C: CredentialMaterial<Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
Q: Query,
{
let required = x509.inputs().requirements();
let scope = required.scope();
let original = scope.location();
let supplied = credentials.scope();
let foreign = || ParticipantError::Foreign {
signature: required.signature(),
};
if !matches!(scope, TimestampScope::BeforeArchive(_))
|| !matches!(supplied, TimestampScope::BeforeArchive(_))
|| original.snapshot != query.snapshot()
|| supplied.location().snapshot != original.snapshot
|| credentials.snapshot() != original.snapshot
{
return Err(foreign());
}
query.admit(original.node, 2)?;
if !required.same_credentials(credentials.credentials())
|| !query
.equal(original.node, supplied.location().node)
.map_err(|cause| 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> {
census: &'a P,
reports: &'a [&'a R],
covered: bool,
}
impl<P, R> Archives<'_, P, R> {
#[must_use]
pub fn census(&self) -> &P {
self.census
}
#[must_use]
pub fn reports(&self) -> &[&R] {
self.reports
}
#[must_use]
pub fn all_archive_material_covered(&self) -> bool {
self.covered
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArchiveCensusError<N> {
Foreign {
node: N,
},
Missing {
property: N,
},
Extra,
Scope(ScopeError<N>),
Resource {
node: N,
cause: MemoryError,
},
}
pub type ArchivesResult<'a, P, R, N> = Result<Archives<'a, P, R>, ArchiveCensusError<N>>;
type Proof<'a, 'cl, 'r, 'v, 'c, 'd, P, C, Q, I> =
ArchiveMaterial<'a, X509<'cl, 'r, 'v, 'c, 'd, P, Q, I>, C>;
pub fn archives<'s, 'a, 'cl, 'r, 'v, 'c, 'd, A, P, C, Q, I>(
census: &'s A,
reports: &'s [&'s Proof<'a, 'cl, 'r, 'v, 'c, 'd, P, C, Q, I>],
query: &Q,
) -> ArchivesResult<'s, A, Proof<'a, 'cl, 'r, 'v, 'c, 'd, P, C, Q, I>, Q::Id>
where
A: ArchiveCensus<Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
Q: Query,
{
let selected = census.signature();
let signature = selected.node;
if selected.snapshot != query.snapshot() {
return Err(ArchiveCensusError::Foreign { node: signature });
}
query
.admit(signature, 1)
.map_err(ArchiveCensusError::Scope)?;
let mut cursor = 0;
let mut covered = true;
for ordinal in 0..census.timestamps() {
query
.execution()
.charge_work(1)
.map_err(|cause| ArchiveCensusError::Resource {
node: signature,
cause,
})?;
let Some(node) = census.archive(ordinal) else {
continue;
};
let resource = |cause| ArchiveCensusError::Resource { node, cause };
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 != query.snapshot()
|| !query
.equal(required.signature(), signature)
.map_err(resource)?
|| !query.equal(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;
}
query
.admit(signature, 1)
.map_err(ArchiveCensusError::Scope)?;
Ok(Archives {
census,
reports,
covered,
})
}