structured_cms/provider.rs
1//! Selected-provider identities and distinct raw versus finalized-prehash inputs.
2//!
3//! Digest execution is the shared [`structured_asn1::digest::Digest`]
4//! contract; ASN.1 digest identifiers resolve through
5//! [`structured_pkix::algorithm::digest`].
6
7use crate::Octets;
8use core::cmp::Ordering;
9use structured_asn1::digest::Resolution;
10use structured_asn1::memory::Work;
11use structured_asn1::{Encoding, Reader, Span};
12use structured_pkix::AlgorithmIdentifier;
13use structured_pkix::algorithm::digest::{self as asn_digest, Algorithm, AsnDigest};
14use structured_pkix::path::WorkingKey;
15mod session;
16pub use session::ProviderSession;
17
18pub(crate) fn require_instance<P>(_: &P, field: &'static str, at: u64) -> crate::Result<()> {
19 // Distinct ZST objects may have the same address. Their adapters must use
20 // the explicit nonzero ProviderSession owner, not an invented object ID.
21 // This constant condition is removed for ordinary monomorphized adapters.
22 if core::mem::size_of::<P>() == 0 {
23 return Err(crate::Error::schema(
24 crate::ErrorCode::ProviderMismatch,
25 field,
26 at,
27 ));
28 }
29 Ok(())
30}
31
32/// Digest algorithms supplied by ESS schema without an encoded identifier.
33/// These select identity, not a product policy permission or an engine fallback.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum ImpliedDigest {
36 /// `ESSCertID` Hash is SHA-1 (RFC2634 §5.4.1).
37 EssSha1,
38 /// Omitted `ESSCertIDv2` hashAlgorithm is SHA-256 with absent parameters
39 /// (RFC5035 §4, corrected certHash description in verified erratum2364).
40 EssV2Sha256,
41}
42
43/// Original written algorithm or a normative schema-supplied identity.
44///
45/// Implied inputs have no encoded `AlgorithmIdentifier` and no parameter TLV.
46/// A written unsupported/malformed identifier must never become an implied
47/// default. The enclosing field/span locate the implied identity with real
48/// document provenance, without synthesizing bytes or a new Parser.
49#[derive(Debug, Clone, Copy)]
50pub enum DigestAlgorithm<'a> {
51 /// The complete original identifier, encoding and parameter provenance.
52 Written(AlgorithmIdentifier<'a>),
53 /// A schema default/mandatory identity without an original identifier TLV.
54 Implied {
55 /// Identity selected by the ESS schema, with no implicit NULL fallback.
56 algorithm: ImpliedDigest,
57 /// Original enclosing ESS field.
58 field: &'static str,
59 /// Original enclosing ESS identifier span.
60 span: Span,
61 },
62}
63
64impl ImpliedDigest {
65 /// The algorithm the ESS schema selects.
66 #[must_use]
67 pub const fn algorithm(self) -> Algorithm {
68 match self {
69 Self::EssSha1 => Algorithm::Sha1,
70 Self::EssV2Sha256 => Algorithm::Sha256,
71 }
72 }
73}
74
75/// Resolves a written identifier through the shared ASN.1 resolver, under the
76/// encoding of the document it came from, or an implied ESS algorithm
77/// directly, then asks the provider for its identity.
78pub(crate) fn resolve_digest<'a, P: AsnDigest<'a>>(
79 provider: &P,
80 algorithm: DigestAlgorithm<'_>,
81 parent: &Reader<'_>,
82) -> crate::Result<Resolution<P::Identity>> {
83 match algorithm {
84 DigestAlgorithm::Written(identifier) => {
85 asn_digest::resolve(provider, &identifier, &parent.execution())
86 .map_err(crate::Error::pkix)
87 }
88 DigestAlgorithm::Implied { algorithm, .. } => {
89 Ok(asn_digest::supported(provider, algorithm.algorithm()))
90 }
91 }
92}
93
94/// A cryptographic signature answer after complete input processing.
95#[derive(Debug, Clone, Copy, PartialEq, Eq)]
96pub enum SignatureAnswer {
97 /// Signature arithmetic accepted the exact selected input and key.
98 Valid,
99 /// Signature arithmetic rejected that input/key, independently of resources.
100 Invalid,
101}
102
103/// Fully resolved signature input contract; raw never silently becomes prehash.
104#[derive(Debug)]
105pub enum SignatureMode<T> {
106 /// Original logical message octets, as required by `PureEdDSA` (RFC8419 §3).
107 Raw(T),
108 /// One finalized digest with a typed, fully resolved identity.
109 Prehashed(T),
110}
111
112/// A finalized output and its complete algorithm identity, without a cloneable state.
113#[derive(Debug)]
114pub struct Finalized<'p, P, I, O> {
115 provider: &'p P,
116 identity: I,
117 output: O,
118}
119
120/// A finalized prehash with independent short identity/output borrows.
121///
122/// Only common processing constructs this view from a completed state bound
123/// to the same resolved identity/provider. Byte width alone never establishes
124/// that provenance. There is no public arbitrary digest-plus-OID constructor.
125#[derive(Debug)]
126pub struct Prehash<'i, 'o, P, I> {
127 provider: &'i P,
128 identity: &'i I,
129 bytes: &'o [u8],
130}
131
132/// A finished digest: the provider's own output and identity, and the
133/// instance that produced them. Only common processing constructs it, from a
134/// state it completed; there is no constructor from arbitrary bytes.
135#[derive(Debug)]
136pub struct Finished<'v, P, I, O> {
137 provider: &'v P,
138 identity: &'v I,
139 output: &'v O,
140}
141
142// Only shared references: copying needs no bound on the referenced types.
143impl<P, I, O> Clone for Finished<'_, P, I, O> {
144 fn clone(&self) -> Self {
145 *self
146 }
147}
148impl<P, I, O> Copy for Finished<'_, P, I, O> {}
149
150impl<'v, P, I, O: AsRef<[u8]>> Finished<'v, P, I, O> {
151 /// The actual instance which completed this output. Borrowing it avoids
152 /// accepting a second selected provider or guessing provider provenance.
153 #[must_use]
154 pub fn provider(&self) -> &'v P {
155 self.provider
156 }
157 pub(crate) fn new(provider: &'v P, identity: &'v I, output: &'v O) -> Self {
158 Self {
159 provider,
160 identity,
161 output,
162 }
163 }
164 /// Whether the supplied instance actually completed this output.
165 #[must_use]
166 pub fn from_provider(&self, provider: &P) -> bool {
167 core::ptr::eq(self.provider, provider)
168 }
169 /// Full selected-provider algorithm identity.
170 #[must_use]
171 pub fn identity(&self) -> &'v I {
172 self.identity
173 }
174 /// The provider's own finished output.
175 #[must_use]
176 pub fn output(&self) -> &'v O {
177 self.output
178 }
179}
180
181impl<'i, 'o, P, I> Prehash<'i, 'o, P, I> {
182 pub(crate) fn new(provider: &'i P, identity: &'i I, bytes: &'o [u8]) -> Self {
183 Self {
184 provider,
185 identity,
186 bytes,
187 }
188 }
189 /// Whether the supplied instance actually completed this finalized input.
190 #[must_use]
191 pub fn from_provider(&self, provider: &P) -> bool {
192 core::ptr::eq(self.provider, provider)
193 }
194 /// Full selected-provider algorithm identity, including parameters/defaults.
195 #[must_use]
196 pub fn identity(&self) -> &'i I {
197 self.identity
198 }
199
200 /// Original finalized output bytes; accessing them performs no new hashing.
201 #[must_use]
202 pub fn as_bytes(&self) -> &'o [u8] {
203 self.bytes
204 }
205}
206
207impl<'p, P, I, O: AsRef<[u8]>> Finalized<'p, P, I, O> {
208 /// Resolved identity, including parameters and output-length semantics.
209 #[must_use]
210 pub fn identity(&self) -> &I {
211 &self.identity
212 }
213 /// Borrowed final output; providers must implement this as constant-time access.
214 #[must_use]
215 pub fn as_bytes(&self) -> &[u8] {
216 self.output.as_ref()
217 }
218
219 /// Borrows a typed prehash without cloning identity, state or output.
220 #[must_use]
221 pub fn prehash(&self) -> Prehash<'_, '_, P, I> {
222 Prehash::new(self.provider, &self.identity, self.output.as_ref())
223 }
224
225 /// Borrows the provider's own output for its comparison, with provenance.
226 #[must_use]
227 pub fn finished(&self) -> Finished<'_, P, I, O> {
228 Finished {
229 provider: self.provider,
230 identity: &self.identity,
231 output: &self.output,
232 }
233 }
234
235 pub(crate) fn new(provider: &'p P, identity: I, output: O) -> Self {
236 Self {
237 provider,
238 identity,
239 output,
240 }
241 }
242}
243
244/// Signature operations that stay bound to the same selected provider and key.
245///
246/// Digest execution and identities come from the provider's
247/// [`structured_asn1::digest::Digest`] and [`AsnDigest`] implementations; CMS
248/// payload message-digest, ESS certificate hashes and TSP imprints all run
249/// through them with their distinct original covered inputs.
250///
251/// The effective key retains its original SPKI and inherited parameters.
252/// Resolution validates key/algorithm compatibility before raw initialization
253/// or payload I/O. A contiguous-only backend may explicitly buffer with full
254/// admission in its adapter, or report unsupported; it cannot downgrade raw.
255///
256/// Common processing borrows a stable nonzero-sized selection owner. Ordinary
257/// adapters containing engine/context references already satisfy this. Wrap a
258/// zero-sized stateless adapter in [`ProviderSession`] before using common
259/// processing; otherwise it returns a processing `ProviderMismatch` before
260/// dispatch. Distinct ZST addresses cannot supply instance provenance.
261pub trait SignatureProvider<'a>: AsnDigest<'a> + Sized {
262 /// Fully resolved signature identity, including algorithm-specific defaults.
263 type SignatureIdentity;
264 /// Raw verification state, initialized with original signature/key first.
265 type RawState;
266
267 /// Chooses raw versus prehashed only after complete algorithm/key resolution.
268 fn resolve_signature(
269 &'a self,
270 identifier: AlgorithmIdentifier<'a>,
271 digest: &Self::Identity,
272 key: WorkingKey<'a>,
273 encoding: Encoding,
274 parent: &Reader<'a>,
275 ) -> core::result::Result<Resolution<SignatureMode<Self::SignatureIdentity>>, Self::Error>;
276 /// Semantic signature/default comparison for protected versus outer identifiers.
277 fn compare_signature<W: Work>(
278 &self,
279 left: &Self::SignatureIdentity,
280 right: &Self::SignatureIdentity,
281 execution: &W,
282 ) -> core::result::Result<Ordering, Self::Error>;
283 /// Starts the raw challenge with the original signature and effective key.
284 fn start_raw<W: Work>(
285 &'a self,
286 identity: &Self::SignatureIdentity,
287 key: WorkingKey<'a>,
288 signature: &'a Octets<'a>,
289 execution: &W,
290 ) -> core::result::Result<Self::RawState, Self::Error>;
291 /// Admits and processes original raw message chunks, never digest bytes.
292 fn update_raw<W: Work>(
293 &self,
294 state: &mut Self::RawState,
295 bytes: &[u8],
296 execution: &W,
297 ) -> core::result::Result<(), Self::Error>;
298 /// Completes a raw signature only after successful complete source EOF.
299 fn finish_raw<W: Work>(
300 &self,
301 state: Self::RawState,
302 execution: &W,
303 ) -> core::result::Result<SignatureAnswer, Self::Error>;
304 /// Verifies one borrowed finalized digest with its full identity; no rehash.
305 fn verify_prehashed<W: Work>(
306 &'a self,
307 identity: &Self::SignatureIdentity,
308 key: WorkingKey<'a>,
309 signature: &Octets<'a>,
310 digest: Prehash<'_, '_, Self, Self::Identity>,
311 execution: &W,
312 ) -> core::result::Result<SignatureAnswer, Self::Error>;
313}