use crate::{
Cades, Level,
basic::{Basic, SignerBinding},
material::{self, Placement},
prepare::Preparation,
source::{DocumentId, Node},
};
use structured_asn1::memory::Work;
use structured_ltv::baseline::{ArchiveMaterial, Archives, CredentialMaterial, LongTerm, Timed};
pub use structured_ltv::baseline::{archive_material, archives, long_term, timestamped};
pub type BasicOf<'a, 'm, 'd> = Basic<'a, 'a, 'm, 'd, 'a>;
pub type TimedOf<'a, 'm, 'd> = Timed<
'a,
'a,
'd,
BasicOf<'a, 'm, 'd>,
SignerBinding<'a, 'm, 'd, 'a>,
Node,
DocumentId<'m, 'd>,
(),
Cades,
>;
pub type CredentialsOf<'a, 'm, 'd, E> = material::credentials::Report<'a, 'a, 'm, 'd, 'a, E>;
pub type LongTermOf<'a, 'm, 'd, E> = LongTerm<
'a,
TimedOf<'a, 'm, 'd>,
material::Checks<'a, 'a, 'a, 'a, 'd, 'm>,
CredentialsOf<'a, 'm, 'd, E>,
>;
pub type ArchivesOf<'a, 'm, 'd, E> = Archives<
'a,
Preparation<'m, 'd>,
ArchiveMaterial<
'a,
structured_ltv::material::ParticipantMaterial<
'a,
'a,
'a,
'a,
'd,
Node,
DocumentId<'m, 'd>,
(),
Placement,
Cades,
>,
CredentialsOf<'a, 'm, 'd, E>,
>,
>;
pub enum Evidence<'a, 'm, 'd, E> {
Basic(&'a BasicOf<'a, 'm, 'd>),
Timestamped(&'a TimedOf<'a, 'm, 'd>),
LongTerm(&'a LongTermOf<'a, 'm, 'd, E>),
Archival {
long_term: &'a LongTermOf<'a, 'm, 'd, E>,
archives: &'a ArchivesOf<'a, 'm, 'd, E>,
},
}
impl<'a, 'm, 'd> Evidence<'a, 'm, 'd, ()> {
#[must_use]
pub fn basic(report: &'a BasicOf<'a, 'm, 'd>) -> Self {
Self::Basic(report)
}
#[must_use]
pub fn timestamped(report: &'a TimedOf<'a, 'm, 'd>) -> Self {
Self::Timestamped(report)
}
}
impl<'m, 'd, E> Evidence<'_, 'm, 'd, E> {
#[must_use]
pub fn level(&self) -> Level {
match self {
Self::Basic(_) => Level::Basic,
Self::Timestamped(_) => Level::Timestamped,
Self::LongTerm(_) => Level::LongTerm,
Self::Archival { .. } => Level::Archival,
}
}
fn prepared(&self) -> &Preparation<'m, 'd> {
match self {
Self::Basic(basic) => basic.binding().prepared(),
Self::Timestamped(timed) => timed.basic().binding().prepared(),
Self::LongTerm(long)
| Self::Archival {
long_term: long, ..
} => long.timed().basic().binding().prepared(),
}
}
}
pub struct Assessment<'a, 'm, 'd, E> {
evidence: Evidence<'a, 'm, 'd, E>,
met: bool,
}
impl<'a, 'm, 'd, E> Assessment<'a, 'm, 'd, E> {
#[must_use]
pub fn requested(&self) -> Level {
self.evidence.level()
}
#[must_use]
pub fn evidence(&self) -> &Evidence<'a, 'm, 'd, E> {
&self.evidence
}
#[must_use]
pub fn all_requirements_met(&self) -> bool {
self.met
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LevelError {
UnavailablePreservation {
property: Node,
kind: crate::preservation::Kind,
},
UnavailablePreservationSource {
source: Node,
},
Mismatch {
requested: Level,
supplied: Level,
},
Foreign,
Resource(structured_asn1::memory::MemoryError),
}
pub fn assess<'a, 'm, 'd, E>(
evidence: Evidence<'a, 'm, 'd, E>,
operation: &structured_asn1::Reader<'_>,
) -> Result<Assessment<'a, 'm, 'd, E>, LevelError> {
let prepared = evidence.prepared();
let requested = prepared.requested();
let supplied = evidence.level();
if requested != supplied {
return Err(LevelError::Mismatch {
requested,
supplied,
});
}
prepared
.document()
.message()
.require_session(operation, "cades.baseline")
.map_err(|_| LevelError::Foreign)?;
let work = operation.execution();
work.charge_work(1).map_err(LevelError::Resource)?;
if requested >= Level::Timestamped {
for required in prepared.preservation() {
work.charge_work(1).map_err(LevelError::Resource)?;
if !required.kind.is_reference_timestamp() && !required.kind.is_archive() {
return Err(LevelError::UnavailablePreservation {
property: required.property,
kind: required.kind,
});
}
}
if !prepared.preservation().is_empty() {
for finding in prepared.preservation_findings() {
work.charge_work(1).map_err(LevelError::Resource)?;
if finding.severity == crate::prepare::Severity::Required {
return Err(LevelError::UnavailablePreservationSource {
source: finding.location.node,
});
}
}
}
}
let met = match &evidence {
Evidence::Basic(basic) => basic.all_basic_requirements_met(),
Evidence::Timestamped(timed) => timed_met(timed),
Evidence::LongTerm(long) => long_met(long),
Evidence::Archival {
long_term,
archives,
} => {
work.charge_work(1).map_err(LevelError::Resource)?;
if !core::ptr::eq(archives.census(), prepared) {
return Err(LevelError::Foreign);
}
let credentials = long_term.credentials().credentials();
for archive in archives.reports() {
work.charge_work(1).map_err(LevelError::Resource)?;
if !core::ptr::eq(archive.credentials().credentials(), credentials) {
return Err(LevelError::Foreign);
}
}
long_met(long_term) && archives.all_archive_material_covered()
}
};
let met = met && prepared.all_format_requirements_met();
Ok(Assessment { evidence, met })
}
fn timed_met(timed: &TimedOf<'_, '_, '_>) -> bool {
timed.basic().all_basic_requirements_met() && timed.all_static_time_requirements_met()
}
fn long_met<E>(long: &LongTermOf<'_, '_, '_, E>) -> bool {
timed_met(long.timed()) && long.all_material_requirements_met()
}