use super::BasicStage;
use crate::pki::SignerReceipt;
use crate::source::{Query, ScopeError};
use crate::timestamp::{
Purpose, SignatureTime, SignatureTimeBinding, SignatureTimeError, SignatureTimeFailure,
TimestampPaths, TimestampScope, existence_time_path, signature_time_path,
};
use structured_asn1::memory::{MemoryError, OwnedVec, Work};
pub enum SignatureTimeFact<'a, S, N, D, G, I> {
Bound(SignatureTime<'a, S, N, D, G, I>),
Unproven {
property: N,
cause: SignatureTimeFailure,
},
}
pub struct Timed<'a, 'certificate, 'document, B, S, N, D, G, I> {
basic: &'a B,
paths: &'a TimestampPaths<'a, 'certificate, 'document, N, D, G, I>,
signatures: OwnedVec<SignatureTimeFact<'a, S, N, D, G, I>>,
static_complete: bool,
}
impl<'a, 'c, 'd, B, S, N, D, G, I> Timed<'a, 'c, 'd, B, S, N, D, G, I> {
#[must_use]
pub fn basic(&self) -> &'a B {
self.basic
}
#[must_use]
pub fn paths(&self) -> &'a TimestampPaths<'a, 'c, 'd, N, D, G, I> {
self.paths
}
#[must_use]
pub fn signatures(&self) -> &[SignatureTimeFact<'a, S, N, D, G, I>] {
&self.signatures
}
#[must_use]
pub fn all_static_time_requirements_met(&self) -> bool {
self.static_complete
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimeError<N> {
Foreign {
signature: N,
},
Scope(ScopeError<N>),
Signature(SignatureTimeError<N>),
Resource {
node: N,
cause: MemoryError,
},
}
pub type TimeResult<'a, 'c, 'd, B, S, Q, I> = Result<
Timed<'a, 'c, 'd, B, S, <Q as Query>::Id, <Q as Query>::Document, <Q as Query>::Generation, I>,
TimeError<<Q as Query>::Id>,
>;
pub fn timestamped<'a, 'c, 'd, 'path, 'document, B, Q, I>(
basic: &'a B,
paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
query: &Q,
) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
where
'document: 'path,
B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
Q: Query,
{
timed(basic, paths, None, query)
}
pub fn timestamped_existence<'a, 'c, 'd, 'path, 'document, B, Q, I>(
_issuer: I,
basic: &'a B,
paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
archive: Option<Q::Id>,
query: &Q,
) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
where
'document: 'path,
B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
Q: Query,
{
timed(basic, paths, archive, query)
}
fn timed<'a, 'c, 'd, 'path, 'document, B, Q, I>(
basic: &'a B,
paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
archive: Option<Q::Id>,
query: &Q,
) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
where
'document: 'path,
B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
Q: Query,
{
let selected = basic.signer().signature();
let signature = selected.node;
let required = paths.required();
let resource = |cause| TimeError::Resource {
node: signature,
cause,
};
if !matches!(required.scope(), TimestampScope::All(_))
|| required.scope().location().snapshot != query.snapshot()
|| selected.snapshot != query.snapshot()
|| !query
.equal(required.signature(), signature)
.map_err(resource)?
|| !required
.includes_counters(basic.counters(), query)
.map_err(TimeError::Scope)?
{
return Err(TimeError::Foreign { signature });
}
query.admit(signature, 1).map_err(TimeError::Scope)?;
let mut signature_written = false;
if let Some(archive) = archive {
let mut archive_written = false;
for entry in required.entries() {
query.admit(entry.property, 1).map_err(TimeError::Scope)?;
if !query.equal(entry.signature, signature).map_err(resource)? {
continue;
}
if entry.purpose == Purpose::Signature {
signature_written = true;
}
if entry.purpose == Purpose::Archive
&& query.equal(entry.property, archive).map_err(resource)?
{
archive_written = true;
}
}
if !archive_written {
return Err(TimeError::Foreign { signature });
}
}
let mut signatures = query.execution().vector();
let mut static_complete = basic.all_requirements_met();
for path in paths.paths() {
query.execution().charge_work(1).map_err(resource)?;
let verified = path.actor().verified();
if verified.purpose() != Purpose::Signature && archive.is_none() {
continue;
}
if !query
.equal(verified.signature(), signature)
.map_err(resource)?
{
continue;
}
let fallback = !signature_written
&& verified.purpose() == Purpose::Archive
&& match archive {
Some(archive) => query
.equal(verified.property(), archive)
.map_err(resource)?,
None => false,
};
if verified.purpose() != Purpose::Signature && !fallback {
continue;
}
signatures.try_reserve(1).map_err(resource)?;
let association = if fallback {
existence_time_path(basic.signer(), path, query)
} else {
signature_time_path(basic.signer(), path, query)
};
let fact = match association.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;
}
query.admit(signature, 1).map_err(TimeError::Scope)?;
Ok(Timed {
basic,
paths,
signatures: signatures.into_owned(),
static_complete,
})
}