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