Skip to main content

structured_asic/
timestamp.rs

1//! RFC3161 checks consume the selected original member through one common pass.
2
3use crate::{
4    content::Content,
5    error::Error as ArchiveError,
6    zip::{MemberId, ReadFailure},
7};
8use structured_asn1::{Reader, memory::Execution, range::RangeSource};
9use structured_cms::{
10    provider::SignatureProvider,
11    timestamp::{CheckContext, CheckedTimestamp, Token, TokenExtension},
12};
13
14/// Explicit check purpose; absence of live request fields never selects a
15/// historical operation implicitly or relabels historical proof as freshness.
16pub enum Context<'c, 'a, 'r> {
17    /// Complete ordinary request/current-reference processing.
18    Live(&'c CheckContext<'a, 'r>),
19    /// Archived evidence; no live request, comparison instant or window allowed.
20    Historical(&'c CheckContext<'a, 'r>),
21}
22
23/// Explicit covered input role. This does not assert that a source-produced
24/// manifest is conforming, that its declared hashes match or that a TSA is trusted.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum InputKind {
27    /// Original data or ordinary assertion-manifest bytes.
28    Data,
29    /// Selected original archive-manifest bytes; coverage needs the graph proof.
30    Archive,
31}
32
33/// Archive framing, actual payload and timestamp execution remain independent.
34#[derive(Debug)]
35pub enum Error<SE, PE, RE, D, G> {
36    /// Source generation/current-source execution refused before checking.
37    Archive(ArchiveError<SE>),
38    /// The actual original member cannot be acquired completely.
39    Payload(ReadFailure<SE, D, G>),
40    /// Unchanged common check cause, including its independently retained map.
41    Check(structured_cms::timestamp::CheckError<ReadFailure<SE, D, G>, PE, RE>),
42}
43
44/// One original token's actual input association and common execution answer.
45/// This does not establish a trusted historical path or archive continuity.
46pub struct Checked<'a, D, G> {
47    token: MemberId<D, G>,
48    input: MemberId<D, G>,
49    inner: CheckedTimestamp<'a>,
50}
51
52impl<'a, D: Copy, G: Copy> Checked<'a, D, G> {
53    /// Exact original time assertion, not a filename selected by success.
54    #[must_use]
55    pub fn token(&self) -> MemberId<D, G> {
56        self.token
57    }
58    /// Exact complete original file/manifest consumed by the imprint check.
59    #[must_use]
60    pub fn input(&self) -> MemberId<D, G> {
61        self.input
62    }
63    /// Every independent common timestamp answer, including negative outcomes.
64    #[must_use]
65    pub fn common(&self) -> &CheckedTimestamp<'a> {
66        &self.inner
67    }
68    /// Transfers the original common answer without copying evidence/source maps.
69    #[must_use]
70    pub fn into_common(self) -> CheckedTimestamp<'a> {
71        self.inner
72    }
73}
74
75impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
76    /// Requires the actual carried token/source first, then invokes the shared
77    /// full asynchronous timestamp validator on the sealed original payload.
78    /// Candidate/key/time/extension recognition stay caller-provided contracts.
79    #[expect(
80        clippy::result_large_err,
81        reason = "retain original timestamp diagnostic maps on refusal without a secondary allocation"
82    )]
83    pub async fn check_timestamp<'a, P, PE, RE>(
84        &'a self,
85        token: &'a Token<'a>,
86        parent: &Reader<'a>,
87        provider: &'a P,
88        context: Context<'_, 'a, '_>,
89        input: &impl crate::payload::Payload<'v, 's, S>,
90        recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
91    ) -> Result<
92        Checked<'a, S::Document, S::Generation>,
93        Error<S::Error, PE, RE, S::Document, S::Generation>,
94    >
95    where
96        P: for<'scope> SignatureProvider<'scope, Error = PE>,
97    {
98        let execution = parent.execution();
99        self.require(token.message(), parent, &execution)
100            .map_err(Error::Archive)?;
101        if !core::ptr::eq(self.member().archive(), input.member().archive()) {
102            return Err(Error::Archive(ArchiveError::Foreign {
103                range: self.enclosure(),
104            }));
105        }
106        let mut source = input.source(&execution).map_err(Error::Payload)?;
107        let checked = match context {
108            Context::Live(context) => {
109                token
110                    .check_async(provider, parent, context, &mut source, recognizes)
111                    .await
112            }
113            Context::Historical(context) => {
114                token
115                    .check_historical_async(provider, parent, context, &mut source, recognizes)
116                    .await
117            }
118        }
119        .map_err(Error::Check)?;
120        self.member()
121            .archive()
122            .admit(&execution, self.enclosure())
123            .map_err(Error::Archive)?;
124        Ok(Checked {
125            token: self.member().id(),
126            input: input.member().id(),
127            inner: checked,
128        })
129    }
130}