use super::Basic;
use crate::certificate::{
SignatureTime, SignatureTimeBinding, SignatureTimeError, SignatureTimeFailure,
};
use crate::timestamp::{Role, TimestampPaths, TimestampScope};
use crate::xml::{Access, AccessError, Xml};
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
pub enum SignatureTimeFact<'a, 'path, 'document, N, D, G> {
Bound(SignatureTime<'a, 'path, 'document, N, D, G>),
Unproven {
property: N,
cause: SignatureTimeFailure,
},
}
pub struct Timed<'a, 'receipt, 'prepared, 'path, 'certificate, 'document, N, D, G> {
basic: &'a Basic<'receipt, 'prepared, 'path, 'document, N, D, G>,
paths: &'a TimestampPaths<'a, 'certificate, 'document, N, D, G>,
signatures: OwnedVec<SignatureTimeFact<'a, 'path, 'document, N, D, G>>,
static_complete: bool,
}
impl<'a, 'r, 'p, 'v, 'c, 'd, N, D, G> Timed<'a, 'r, 'p, 'v, 'c, 'd, N, D, G> {
#[must_use]
pub fn basic(&self) -> &'a Basic<'r, 'p, 'v, 'd, N, D, G> {
self.basic
}
#[must_use]
pub fn paths(&self) -> &'a TimestampPaths<'a, 'c, 'd, N, D, G> {
self.paths
}
#[must_use]
pub fn signatures(&self) -> &[SignatureTimeFact<'a, 'v, 'd, N, D, G>] {
&self.signatures
}
#[must_use]
pub fn all_static_time_requirements_met(&self) -> bool {
self.static_complete
}
}
#[derive(Debug)]
pub enum TimeError<E, N> {
Foreign {
signature: N,
},
Access(AccessError<E, N>),
Input(crate::timestamp::InputError<E, N>),
Signature(SignatureTimeError<E, N>),
Resource {
node: N,
cause: MemoryError,
},
}
pub type TimeResult<'a, 'r, 'p, 'v, 'c, 'd, X> = Result<
Timed<'a, 'r, 'p, 'v, 'c, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
TimeError<<X as Xml>::Error, <X as Xml>::Id>,
>;
type Root<'a, 'r, 'p, 'v, 'd, X> =
&'a Basic<'r, 'p, 'v, 'd, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
pub fn timestamped<'a, 'r, 'p, 'v, 'c, 'd, X: Xml, W: Work>(
basic: Root<'a, 'r, 'p, 'v, 'd, X>,
paths: &'a TimestampPaths<'a, 'c, 'd, X::Id, X::Document, X::Generation>,
access: &Access<'_, X, W>,
) -> TimeResult<'a, 'r, 'p, 'v, 'c, 'd, X> {
let prepared = basic.inputs().fields.prepared();
let signature = prepared.bound().containers().signature();
let required = paths.required();
let resource = |cause| TimeError::Resource {
node: signature,
cause,
};
if !matches!(required.scope(), TimestampScope::All(_))
|| required.scope().location().snapshot != access.snapshot()
|| prepared.bound().containers().snapshot() != access.snapshot()
|| !access
.equal_id(required.signature(), signature)
.map_err(resource)?
|| !required
.includes_counters(basic.inputs().counters, access)
.map_err(TimeError::Input)?
{
return Err(TimeError::Foreign { signature });
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(TimeError::Access)?;
let mut signatures = access.execution().vector();
let mut static_complete = basic.all_root_requirements_met();
for path in paths.paths() {
access.execution().charge_work(1).map_err(resource)?;
let verified = path.actor().verified();
if verified.role() != Role::Signature
|| !access
.equal_id(verified.signature(), signature)
.map_err(resource)?
{
continue;
}
signatures.try_reserve(1).map_err(resource)?;
let fact = match basic
.inputs()
.certificate
.bind_signature_time_path(path, access)
.map_err(TimeError::Signature)?
{
SignatureTimeBinding::Bound(value) => SignatureTimeFact::Bound(value),
SignatureTimeBinding::Unproven(cause) => {
static_complete = false;
SignatureTimeFact::Unproven {
property: verified.property(),
cause,
}
}
};
signatures.try_push(fact).map_err(resource)?;
}
if signatures.is_empty() {
static_complete = false;
}
access
.query(signature, |_, _, _| Ok(()))
.map_err(TimeError::Access)?;
Ok(Timed {
basic,
paths,
signatures: signatures.into_owned(),
static_complete,
})
}