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}