Skip to main content

structured_ltv/baseline/
timed.rs

1//! Whole original TSA-path census and root signature-time static association.
2
3use super::BasicStage;
4use crate::pki::SignerReceipt;
5use crate::source::{Query, ScopeError};
6use crate::timestamp::{
7    Purpose, SignatureTime, SignatureTimeBinding, SignatureTimeError, SignatureTimeFailure,
8    TimestampPaths, TimestampScope, signature_time_path,
9};
10use structured_asn1::memory::{MemoryError, OwnedVec, Work};
11
12/// One actual root signature timestamp's complete static certificate binding.
13/// Negative results do not disappear because a different timestamp succeeded.
14pub enum SignatureTimeFact<'a, S, N, D, G, I> {
15    /// Exact original signature input and full generation/static path interval.
16    Bound(SignatureTime<'a, S, N, D, G, I>),
17    /// Completed insufficient root certificate/interval fact, not source refusal.
18    Unproven {
19        /// Original root signature timestamp property.
20        property: N,
21        /// Exact completed negative cause.
22        cause: SignatureTimeFailure,
23    },
24}
25
26/// Whole original token/TSA path set plus every root signature-time association.
27/// This does not assert pre-revocation chronology, current/historical status,
28/// format material completeness, algorithm-obsolescence policy or caller trust.
29/// In particular it is NOT an unconditional B-T acceptance verdict.
30pub struct Timed<'a, 'certificate, 'document, B, S, N, D, G, I> {
31    basic: &'a B,
32    paths: &'a TimestampPaths<'a, 'certificate, 'document, N, D, G, I>,
33    signatures: OwnedVec<SignatureTimeFact<'a, S, N, D, G, I>>,
34    static_complete: bool,
35}
36
37impl<'a, 'c, 'd, B, S, N, D, G, I> Timed<'a, 'c, 'd, B, S, N, D, G, I> {
38    /// Same original basic stage.
39    #[must_use]
40    pub fn basic(&self) -> &'a B {
41        self.basic
42    }
43    /// Complete source-derived root AND nested-counter timestamp/TSA paths.
44    #[must_use]
45    pub fn paths(&self) -> &'a TimestampPaths<'a, 'c, 'd, N, D, G, I> {
46        self.paths
47    }
48    /// Every original root signature timestamp, including negative associations.
49    #[must_use]
50    pub fn signatures(&self) -> &[SignatureTimeFact<'a, S, N, D, G, I>] {
51        &self.signatures
52    }
53    /// At least one root signature timestamp exists and every such timestamp's
54    /// complete accuracy interval is covered by the actual static signer path.
55    /// Counter/data/archive timestamps cannot substitute for this condition.
56    #[must_use]
57    pub fn all_static_time_requirements_met(&self) -> bool {
58        self.static_complete
59    }
60}
61
62/// Terminal original source/selection/query refusal; no partial timed report.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum TimeError<N> {
65    /// Wrong scope, source or selected root/counter census.
66    Foreign {
67        /// Original selected signature.
68        signature: N,
69    },
70    /// Original current-operation/source failure, including the complete
71    /// counter-census membership query.
72    Scope(ScopeError<N>),
73    /// Original signature-time association processing failed.
74    Signature(SignatureTimeError<N>),
75    /// Work or retained report capacity was refused before use.
76    Resource {
77        /// Original selecting signature/property.
78        node: N,
79        /// Exact resource refusal.
80        cause: MemoryError,
81    },
82}
83
84/// Complete original static time facts in the query's identity domain.
85pub type TimeResult<'a, 'c, 'd, B, S, Q, I> = Result<
86    Timed<'a, 'c, 'd, B, S, <Q as Query>::Id, <Q as Query>::Document, <Q as Query>::Generation, I>,
87    TimeError<<Q as Query>::Id>,
88>;
89
90/// Bind all-token TSA-path facts to the SAME root/counter source and every actual
91/// root signature timestamp to the checked signer. Reuses previously established
92/// complete generation intervals, not another UTC parse or certificate hash.
93/// EN319132-1 ยงยง5.3/6.3(n,o) distinguish the signature timestamp from other
94/// kinds and require EACH such timestamp before expiration/revocation. This
95/// establishes the static interval part; actual revocation facts stay explicit.
96/// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
97pub fn timestamped<'a, 'c, 'd, 'path, 'document, B, Q, I>(
98    basic: &'a B,
99    paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
100    query: &Q,
101) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
102where
103    'document: 'path,
104    B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
105    Q: Query,
106{
107    let selected = basic.signer().signature();
108    let signature = selected.node;
109    let required = paths.required();
110    let resource = |cause| TimeError::Resource {
111        node: signature,
112        cause,
113    };
114    if !matches!(required.scope(), TimestampScope::All(_))
115        || required.scope().location().snapshot != query.snapshot()
116        || selected.snapshot != query.snapshot()
117        || !query
118            .equal(required.signature(), signature)
119            .map_err(resource)?
120        || !required
121            .includes_counters(basic.counters(), query)
122            .map_err(TimeError::Scope)?
123    {
124        return Err(TimeError::Foreign { signature });
125    }
126    query.admit(signature, 1).map_err(TimeError::Scope)?;
127    let mut signatures = query.execution().vector();
128    let mut static_complete = basic.all_requirements_met();
129    for path in paths.paths() {
130        query.execution().charge_work(1).map_err(resource)?;
131        let verified = path.actor().verified();
132        if verified.purpose() != Purpose::Signature
133            || !query
134                .equal(verified.signature(), signature)
135                .map_err(resource)?
136        {
137            continue;
138        }
139        signatures.try_reserve(1).map_err(resource)?;
140        let fact =
141            match signature_time_path(basic.signer(), path, query).map_err(TimeError::Signature)? {
142                SignatureTimeBinding::Bound(value) => SignatureTimeFact::Bound(value),
143                SignatureTimeBinding::Unproven(cause) => {
144                    static_complete = false;
145                    SignatureTimeFact::Unproven {
146                        property: verified.property(),
147                        cause,
148                    }
149                }
150            };
151        signatures.try_push(fact).map_err(resource)?;
152    }
153    if signatures.is_empty() {
154        static_complete = false;
155    }
156    query.admit(signature, 1).map_err(TimeError::Scope)?;
157    Ok(Timed {
158        basic,
159        paths,
160        signatures: signatures.into_owned(),
161        static_complete,
162    })
163}