use super::Authenticated;
use crate::error::{Error, Result};
use crate::temporal::{LeapTable, Reading, Uncertainty};
use structured_asn1::memory::Work;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Freshness {
Current,
AtNextUpdate,
NotYetCurrent,
Stale,
MissingNextUpdate,
InvalidUpdateOrder,
Uncertain(Uncertainty),
}
pub(crate) fn update_window<W: Work>(
this: crate::Time,
next: Option<crate::Time>,
now: Reading,
utc: &LeapTable<'_>,
execution: &W,
origin: (&'static str, u64),
) -> Result<Freshness> {
let (field, at) = origin;
execution
.charge_work(1)
.map_err(|reason| Error::resource(reason, field, at))?;
let searches = u64::from(usize::BITS - utc.transitions().len().leading_zeros() + 1);
let comparisons = if next.is_some() { 9 } else { 6 };
execution
.charge_work(searches * comparisons + 64)
.map_err(|reason| Error::resource(reason, field, at))?;
let this = this.instant();
let remainder = now.has_subnanosecond_remainder();
let now = now.start();
for label in [this, now] {
if let Err(reason) = utc.shift(label, 0) {
return Ok(Freshness::Uncertain(reason));
}
}
let Some(next) = next else {
return Ok(if now < this {
Freshness::NotYetCurrent
} else {
Freshness::MissingNextUpdate
});
};
let next = next.instant();
if let Err(reason) = utc.shift(next, 0) {
return Ok(Freshness::Uncertain(reason));
}
Ok(if next <= this {
Freshness::InvalidUpdateOrder
} else if now < this {
Freshness::NotYetCurrent
} else if now == next && !remainder {
Freshness::AtNextUpdate
} else if now >= next {
Freshness::Stale
} else {
Freshness::Current
})
}
impl Authenticated<'_, '_> {
pub fn freshness<W: Work>(
&self,
now: Reading,
utc: &LeapTable<'_>,
execution: &W,
) -> Result<Freshness> {
update_window(
self.crl().tbs().this_update(),
self.crl().tbs().next_update(),
now,
utc,
execution,
("certificateList.freshness", self.crl().span().start),
)
}
}