Skip to main content

structured_cms/timestamp/
timeliness.rs

1//! Request freshness facts, without a replay database or implicit clock.
2
3use super::{Accuracy, BoundUncertainty, earliest, latest};
4use structured_pkix::temporal::{Instant, LeapTable, Reading};
5
6/// Caller-supplied moving window for a uniquely outstanding complete imprint.
7///
8/// RFC3161 §6 requires the requester to remember hashes and establish their
9/// uniqueness. This value asserts that caller-owned precondition; it neither
10/// keeps that history nor proves the request imprint or signature matches.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct RequestWindow {
13    start: Instant,
14    end: Instant,
15}
16
17impl RequestWindow {
18    /// Admits ordered endpoints. Caller tolerance must already be in them.
19    #[must_use]
20    pub fn for_unique_imprint(start: Instant, end: Instant) -> Option<Self> {
21        if start > end {
22            return None;
23        }
24        Some(Self { start, end })
25    }
26}
27
28/// How the supplied facts establish, fail or leave freshness unresolved.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Timeliness {
31    /// An archived token with a supplied local reference, not a live response.
32    NotRequested,
33    /// Complete accuracy/precision interval fits the supplied request window.
34    LocalWindow,
35    /// No usable window comparison; a separately checked nonce matches.
36    RequestNonce,
37    /// A performed local-reference comparison found the token in the future.
38    Future,
39    /// A performed interval comparison found it outside the request window.
40    OutsideWindow,
41    /// Live request context supplied no usable freshness evidence.
42    Unverified,
43    /// Missing accuracy or UTC facts prevent the supplied temporal comparison.
44    Uncertain(BoundUncertainty),
45}
46
47/// Evaluates explicit time facts; it does not establish signature or trust.
48///
49/// `nonce` is the result of comparing the parsed request and token nonce, not
50/// merely nonce presence. `requested` distinguishes a live request from archived
51/// evidence. No clock is read, no tolerance invented and no replay state kept.
52/// Full-interval containment is the explicit supplied-window contract, not an
53/// ASN.1 conformance rule. A completed negative comparison takes precedence over
54/// nonce fallback; missing facts remain uncertainty when no nonce can help.
55#[must_use]
56pub fn timeliness(
57    reading: Reading,
58    accuracy: Option<Accuracy>,
59    utc: &LeapTable<'_>,
60    reference: Option<Instant>,
61    requested: bool,
62    nonce: Option<bool>,
63    window: Option<RequestWindow>,
64) -> Timeliness {
65    // RFC3161 §§2.2, 6 permit nonce freshness without a usable local clock.
66    // They do not make a nonce erase an already performed negative time check.
67    // https://www.rfc-editor.org/rfc/rfc3161.html#section-6
68    let lower = earliest(reading, accuracy, utc);
69    if let (Some(reference), Ok(lower)) = (reference, lower)
70        && lower > reference
71    {
72        return Timeliness::Future;
73    }
74    if let Some(window) = window {
75        match lower.and_then(|start| latest(reading, accuracy, utc).map(|end| (start, end))) {
76            Ok((start, end)) => {
77                if start < window.start || end > window.end {
78                    return Timeliness::OutsideWindow;
79                }
80                return Timeliness::LocalWindow;
81            }
82            Err(reason) => {
83                if nonce == Some(true) {
84                    return Timeliness::RequestNonce;
85                }
86                return Timeliness::Uncertain(reason);
87            }
88        }
89    }
90    if nonce == Some(true) {
91        return Timeliness::RequestNonce;
92    }
93    if requested {
94        return Timeliness::Unverified;
95    }
96    if reference.is_some() {
97        match lower {
98            Ok(_) => Timeliness::NotRequested,
99            Err(reason) => Timeliness::Uncertain(reason),
100        }
101    } else {
102        Timeliness::Unverified
103    }
104}