Skip to main content

structured_cms/timestamp/
signature.rs

1//! Timestamp signature orchestration reuses the complete CMS execution kernel.
2
3use super::Token;
4use crate::{Error, ErrorCode, binding::Scope, prepare, provider::SignatureProvider, verify};
5use structured_asn1::{Reader, memory::Work};
6use structured_pkix::path::WorkingKey;
7
8/// Arithmetic/content facts, without ESS, certificate trust or timestamp proof.
9pub enum TokenSignature<'a> {
10    /// Complete CMS pass, preserving the original signer and effective key.
11    Checked(verify::Checked<'a>),
12    /// No signature state or payload pass was started for this mechanism.
13    Unsupported(prepare::Part),
14    /// Schema/algorithm preflight refused before signature input execution.
15    Malformed(Error),
16}
17
18/// Execution failures remain separate from arithmetic and conformance answers.
19#[derive(Debug)]
20pub enum SignatureError<E> {
21    /// Original common processing/resource failure.
22    Processing(Error),
23    /// Schema/provider preparation failed with its original cause.
24    Preparation(prepare::PrepareError<E>),
25    /// State/challenge admission failed with its original cause.
26    Admission(verify::CheckError<E>),
27    /// The complete original attached-content pass failed.
28    Pass(verify::PassError<crate::octets::SourceError, E>),
29}
30
31impl<'a> Token<'a> {
32    /// Executes the one TSA signer over its original attached `TSTInfo` octets.
33    ///
34    /// This shares CMS preparation and raw/prehash execution, including exact
35    /// retagged signedAttrs and its separate message-digest agreement. It never
36    /// hashes a normalized/re-encoded `TSTInfo` in place of the signed payload.
37    /// The key may borrow a shorter-lived caller descriptor and retain inherited
38    /// parameters. SID/ESS binding, TSA purpose, request/time and trust remain
39    /// independent checks; Valid alone never creates authenticated evidence.
40    /// RFC5652 §§5.1, 5.3 allow an empty advertised digest catalogue and make
41    /// rejection of an unlisted signer algorithm optional. This operation
42    /// verifies the signer's actual algorithm; catalogue-based rejection is
43    /// an application profile decision, not an unconditional CMS syntax rule.
44    /// <https://www.rfc-editor.org/rfc/rfc5652#section-5.3>
45    pub fn check_signature<'check, P: SignatureProvider<'check>>(
46        &'check self,
47        provider: &'check P,
48        key: WorkingKey<'check>,
49        parent: &Reader<'check>,
50    ) -> Result<TokenSignature<'check>, SignatureError<P::Error>>
51    where
52        'a: 'check,
53    {
54        // RFC5652 §5.3 uses SHOULD for listing and MAY for recipient refusal.
55        // The catalogue is not the signed algorithm input. Resolve that input
56        // directly, rather than turning optional profile rejection into a
57        // mandatory malformed answer or substituting an advertised algorithm.
58        let request = match prepare::resolve(
59            provider,
60            self.signer(),
61            key,
62            parent,
63            Scope::Content(self.message().content().kind()),
64        )
65        .map_err(SignatureError::Preparation)?
66        {
67            prepare::Preparation::Unsupported(part) => {
68                return Ok(TokenSignature::Unsupported(part));
69            }
70            prepare::Preparation::Malformed(error) => return Ok(TokenSignature::Malformed(error)),
71            prepare::Preparation::Ready(request) => request,
72        };
73        let field = "signedData.encapContentInfo.eContent";
74        let at = self.message().content().span().start;
75        let mut source = self.message().content().content().ok_or_else(|| {
76            SignatureError::Processing(Error::schema(ErrorCode::MissingField, field, at))
77        })?;
78        let work = parent.execution();
79        let mut requests = work.vector();
80        requests
81            .try_push(request)
82            .map_err(|reason| SignatureError::Processing(Error::resource(reason, field, at)))?;
83        let batch = verify::Batch::prepare(provider, requests.into_owned(), &work, field, at)
84            .map_err(SignatureError::Admission)?;
85        let mut results = batch
86            .run(&mut source, &work)
87            .map_err(SignatureError::Pass)?;
88        let checked = results.pop().ok_or_else(|| {
89            SignatureError::Processing(Error::schema(
90                ErrorCode::MissingField,
91                "signedData.signerInfos",
92                self.message().span().start,
93            ))
94        })?;
95        Ok(TokenSignature::Checked(checked))
96    }
97}