use crate::source::Location;
use core::marker::PhantomData;
use structured_asn1::memory::{MemoryError, Work};
use structured_cms::timestamp::{BoundUncertainty, Evidence, earliest, latest};
use structured_pkix::temporal::{Instant, LeapTable};
use structured_pkix::{Certificate, path::WorkingKey};
mod obligation;
pub use obligation::{
ObligationError, PathBindingResult, RequiredToken, TimestampPathBinding, TimestampPathFailure,
TimestampPaths, TimestampRequirements, TimestampScope,
};
mod signature;
pub use signature::{
SignatureTime, SignatureTimeBinding, SignatureTimeError, SignatureTimeFailure,
SignatureTimeResult, signature_time, signature_time_path,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Purpose {
Content,
Signature,
ValidationData,
Archive,
}
#[derive(Debug)]
pub struct Verified<N, D, G, I> {
token: Location<N, D, G>,
signature: N,
property: N,
purpose: Purpose,
evidence: Evidence,
issuer: PhantomData<I>,
}
pub struct CheckedActor<'certificate, 'document, N, D, G, I> {
verified: Verified<N, D, G, I>,
certificate: &'certificate Certificate<'document>,
key: WorkingKey<'document>,
}
impl<'certificate, 'document, N, D, G, I> CheckedActor<'certificate, 'document, N, D, G, I> {
#[must_use]
pub fn new(
_issuer: I,
verified: Verified<N, D, G, I>,
certificate: &'certificate Certificate<'document>,
key: WorkingKey<'document>,
) -> Self {
Self {
verified,
certificate,
key,
}
}
#[must_use]
pub fn certificate(&self) -> &'certificate Certificate<'document> {
self.certificate
}
#[must_use]
pub fn key(&self) -> WorkingKey<'document> {
self.key
}
#[must_use]
pub fn verified(&self) -> &Verified<N, D, G, I> {
&self.verified
}
#[must_use]
pub fn into_verified(self) -> Verified<N, D, G, I> {
self.verified
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Interval {
pub earliest: Instant,
pub latest: Instant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BoundaryRelation {
Before,
At,
After,
Overlaps,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TemporalError {
Resource(MemoryError),
Uncertain(BoundUncertainty),
}
impl<N, D, G, I> Verified<N, D, G, I> {
#[must_use]
pub fn new(
_issuer: I,
token: Location<N, D, G>,
signature: N,
property: N,
purpose: Purpose,
evidence: Evidence,
) -> Self {
Self {
token,
signature,
property,
purpose,
evidence,
issuer: PhantomData,
}
}
}
impl<N: Copy, D: Copy, G: Copy, I> Verified<N, D, G, I> {
#[must_use]
pub fn token(&self) -> Location<N, D, G> {
self.token
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn property(&self) -> N {
self.property
}
#[must_use]
pub fn purpose(&self) -> Purpose {
self.purpose
}
#[must_use]
pub fn evidence(&self) -> &Evidence {
&self.evidence
}
#[must_use]
pub fn into_evidence(self) -> Evidence {
self.evidence
}
pub fn interval<W: Work>(
&self,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<Interval, TemporalError> {
interval(
self.evidence.facts().gen_time(),
self.evidence.facts().accuracy(),
utc,
execution,
)
}
pub fn relation<W: Work>(
&self,
boundary: Instant,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<BoundaryRelation, TemporalError> {
relation(self.interval(utc, execution)?, boundary, utc, execution)
}
}
fn relation<W: Work>(
interval: Interval,
boundary: Instant,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<BoundaryRelation, TemporalError> {
let bits = usize::BITS - utc.transitions().len().leading_zeros();
execution
.charge_work(16 + 3 * (u64::from(bits) + 1))
.map_err(TemporalError::Resource)?;
utc.shift(boundary, 0)
.map_err(|cause| TemporalError::Uncertain(BoundUncertainty::Utc(cause)))?;
Ok(if interval.latest < boundary {
BoundaryRelation::Before
} else if interval.earliest > boundary {
BoundaryRelation::After
} else if interval.earliest == boundary && interval.latest == boundary {
BoundaryRelation::At
} else {
BoundaryRelation::Overlaps
})
}
fn interval<W: Work>(
reading: structured_pkix::temporal::Reading,
accuracy: Option<structured_cms::timestamp::Accuracy>,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<Interval, TemporalError> {
let count = utc.transitions().len();
let bits = usize::BITS - count.leading_zeros();
let cost = 48 + 9 * (u64::from(bits) + 1);
execution
.charge_work(cost)
.map_err(TemporalError::Resource)?;
let earliest = earliest(reading, accuracy, utc).map_err(TemporalError::Uncertain)?;
let latest = latest(reading, accuracy, utc).map_err(TemporalError::Uncertain)?;
Ok(Interval { earliest, latest })
}
#[cfg(test)]
mod tests;