use super::Role;
use crate::binary::Location;
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};
#[derive(Debug)]
pub struct Verified<N, D, G> {
pub(super) token: Location<N, D, G>,
pub(super) signature: N,
pub(super) property: N,
pub(super) role: Role,
pub(super) evidence: Evidence,
}
pub struct CheckedActor<'certificate, 'document, N, D, G> {
pub(super) verified: Verified<N, D, G>,
pub(super) certificate: &'certificate Certificate<'document>,
pub(super) key: WorkingKey<'document>,
}
impl<'certificate, 'document, N, D, G> CheckedActor<'certificate, 'document, N, D, G> {
#[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> {
&self.verified
}
#[must_use]
pub fn into_verified(self) -> Verified<N, D, G> {
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: Copy, D: Copy, G: Copy> Verified<N, D, G> {
#[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 role(&self) -> Role {
self.role
}
#[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 {
use super::*;
use alloc::sync::Arc;
use structured_asn1::{
Limits, Parser,
memory::{Memory, MemoryAccount},
};
use structured_cms::timestamp::Accuracy;
use structured_pkix::temporal::{Reading, Transition, Uncertainty};
fn funded() -> Memory {
Memory::new(Arc::new(MemoryAccount::new(4096, 10_000)))
}
#[test]
fn accuracy_absence_never_creates_an_exact_interval_or_extends_utc_coverage() {
let work = funded();
let reading = Reading::from(Instant::new(100, 0).unwrap());
let utc = LeapTable::new(0, 10, &[]).unwrap();
assert_eq!(
interval(reading, None, &utc, &work),
Err(TemporalError::Uncertain(BoundUncertainty::AccuracyUnstated))
);
assert_eq!(
interval(reading, Some(Accuracy::new(0, 0, 0)), &utc, &work),
Err(TemporalError::Uncertain(BoundUncertainty::Utc(
Uncertainty::OutsideCoverage
)))
);
}
#[test]
fn generation_interval_uses_common_elapsed_utc_arithmetic_across_inserted_second() {
let work = funded();
let changes = [Transition::new(78_796_800, 1)];
let utc = LeapTable::new(78_710_400, 78_883_200, &changes).unwrap();
let reading = Reading::from(Instant::leap_after(78_796_799, 0).unwrap());
assert_eq!(
interval(reading, Some(Accuracy::new(1, 0, 0)), &utc, &work).unwrap(),
Interval {
earliest: Instant::new(78_796_799, 0).unwrap(),
latest: Instant::new(78_796_800, 0).unwrap(),
}
);
}
#[test]
fn current_local_and_aggregate_refusals_precede_utc_lookup() {
let work = funded();
let parser = Parser::new(
structured_asn1::Encoding::Der,
Limits {
max_work: 0,
..Limits::default()
},
work,
);
let reader = parser.read(&[], 0).unwrap();
let utc = LeapTable::new(0, 10, &[]).unwrap();
let reading = Reading::from(Instant::new(100, 0).unwrap());
assert!(matches!(
interval(reading, None, &utc, &reader.execution()),
Err(TemporalError::Resource(MemoryError::WorkExceeded))
));
let aggregate = Memory::new(Arc::new(MemoryAccount::new(0, 0)));
assert!(matches!(
interval(reading, None, &utc, &aggregate),
Err(TemporalError::Resource(MemoryError::WorkExceeded))
));
}
#[test]
fn interval_chronology_does_not_turn_touching_accuracy_into_strict_order() {
let utc = LeapTable::new(0, 100_000, &[]).unwrap();
let work = funded();
let boundary = Instant::new(100, 0).unwrap();
for (first, last, expected) in [
(90, 99, BoundaryRelation::Before),
(90, 100, BoundaryRelation::Overlaps),
(90, 110, BoundaryRelation::Overlaps),
(100, 100, BoundaryRelation::At),
(101, 110, BoundaryRelation::After),
] {
let interval = Interval {
earliest: Instant::new(first, 0).unwrap(),
latest: Instant::new(last, 0).unwrap(),
};
assert_eq!(relation(interval, boundary, &utc, &work).unwrap(), expected);
}
let interval = Interval {
earliest: Instant::new(99, 999_999_999).unwrap(),
latest: Instant::new(99, 999_999_999).unwrap(),
};
assert_eq!(
relation(interval, boundary, &utc, &work).unwrap(),
BoundaryRelation::Before
);
let inserted = Instant::leap_after(86_399, 0).unwrap();
assert!(matches!(
relation(interval, inserted, &utc, &work),
Err(TemporalError::Uncertain(BoundUncertainty::Utc(_)))
));
let denied = Parser::new(
structured_asn1::Encoding::Der,
Limits {
max_work: 0,
..Limits::default()
},
funded(),
);
let query = denied.read(&[], 0).unwrap();
assert_eq!(
relation(interval, inserted, &utc, &query.execution()),
Err(TemporalError::Resource(MemoryError::WorkExceeded))
);
assert_eq!(work.used(), 0);
}
}