Skip to main content

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}