Skip to main content

vitaminc_prf/
traits.rs

1use std::{any::Any, borrow::Cow, future::IntoFuture};
2
3use vitaminc_protected::{Controlled, Protected};
4
5use crate::BlockVisitor;
6use crate::{Context, IntoPrfContext, PrfEncoding, PrfError, PrfVisitor};
7
8/// A type that can describe its structure to a [`Prf`] backend.
9///
10/// The caller supplies a visitor because PRF output is policy-neutral: the
11/// same resolved block can become an equality term, Bloom positions, or an
12/// application-specific record.
13///
14/// Every entry point borrows the backend. A derivation is a pure function of
15/// the key and the input, so nothing is consumed; one backend instance serves
16/// any number of derivations without being cloned.
17pub trait PrfValue {
18    /// Derive one raw backend block with no context.
19    fn prf<P>(self, prf: &P) -> P::Ok<P::Block>
20    where
21        Self: Sized,
22        P: Prf,
23    {
24        self.prf_visit(prf, BlockVisitor)
25    }
26
27    /// Derive a structured result with no context and interpret it with
28    /// `visitor`.
29    fn prf_visit<P, V>(self, prf: &P, visitor: V) -> P::Ok<V::Value>
30    where
31        Self: Sized,
32        P: Prf,
33        V: PrfVisitor<P::Block, P::Passthrough>,
34    {
35        self.prf_visit_with_context(prf, Context::empty(), visitor)
36    }
37
38    /// Derive one raw backend block under `context`.
39    fn prf_with_context<'a, P, C>(self, prf: &P, context: C) -> P::Ok<P::Block>
40    where
41        Self: Sized,
42        P: Prf,
43        C: IntoPrfContext<'a>,
44    {
45        self.prf_visit_with_context(prf, context, BlockVisitor)
46    }
47
48    /// Derive a structured result under `context` and interpret it with
49    /// `visitor`. Implementations provide this method; the other entry points
50    /// are conveniences built on it.
51    fn prf_visit_with_context<'a, P, V, C>(
52        self,
53        prf: &P,
54        context: C,
55        visitor: V,
56    ) -> P::Ok<V::Value>
57    where
58        P: Prf,
59        V: PrfVisitor<P::Block, P::Passthrough>,
60        C: IntoPrfContext<'a>;
61}
62
63/// Construction of a [`Prf`] backend from key material.
64///
65/// This is the one place the key changes hands. Both constructors take the
66/// key **by value**, so the material moves into the backend rather than being
67/// copied at the boundary, and the backend owns it for the rest of its life:
68/// when the backend drops, the key is wiped. Backends must not hand out
69/// shared handles to the key (for example behind an `Arc`), because that
70/// turns "wiped when this backend drops" into "wiped when the last
71/// outstanding handle drops", which no call site can see.
72///
73/// The shape mirrors RustCrypto's `KeyInit`, with the deliberate difference
74/// that the key is owned rather than borrowed and copied in.
75///
76/// [`Prf`] is a supertrait, so `P: PrfKeyInit` alone says "a PRF I can
77/// build from a key"; generic code does not need to spell out `P: Prf` as
78/// well.
79pub trait PrfKeyInit: Prf {
80    /// Fixed-size key type whose length is guaranteed by construction, so
81    /// [`new`](PrfKeyInit::new) cannot fail. Always a controlled type; a
82    /// bare array cannot key a backend.
83    type Key: Controlled;
84
85    /// Reason [`try_from_bytes`](PrfKeyInit::try_from_bytes) can reject key
86    /// material, typically because it is too short.
87    type KeyError: std::error::Error + Send + Sync + 'static;
88
89    /// Key the backend with a full-strength key of the statically known
90    /// length.
91    fn new(key: Self::Key) -> Self;
92
93    /// Key the backend with material whose length is only known at runtime,
94    /// such as a KMS response or an environment variable.
95    ///
96    /// # Errors
97    ///
98    /// Returns [`KeyError`](PrfKeyInit::KeyError) if the material is not
99    /// acceptable as a key.
100    fn try_from_bytes(key: Protected<Vec<u8>>) -> Result<Self, Self::KeyError>;
101}
102
103/// Backend for structured pseudorandom derivation.
104///
105/// Every method borrows the backend. The structural drivers returned by
106/// [`prf_seq`](Prf::prf_seq) and [`prf_map`](Prf::prf_map) borrow it for the
107/// length of one derivation, which is why they carry a lifetime. Results do
108/// not: [`Ok`](Prf::Ok) has no lifetime parameter, so no borrow of the backend
109/// can escape into an awaitable output.
110pub trait Prf: Sized {
111    type Block: Send + 'static;
112    type BackendError: std::error::Error + Send + Sync + 'static;
113    type Passthrough: Send + 'static;
114
115    /// Driver for sequence-shaped values, borrowing the backend for one
116    /// derivation.
117    type SeqPrf<'a>: SeqPrf<
118        Prf = Self,
119        Block = Self::Block,
120        BackendError = Self::BackendError,
121        Passthrough = Self::Passthrough,
122    >
123    where
124        Self: 'a;
125
126    /// Driver for map-shaped values, borrowing the backend for one
127    /// derivation.
128    type MapPrf<'a>: MapPrf<
129        Prf = Self,
130        Block = Self::Block,
131        BackendError = Self::BackendError,
132        Passthrough = Self::Passthrough,
133    >
134    where
135        Self: 'a;
136
137    /// Awaitable output. It carries no lifetime, so it must own everything it
138    /// needs to resolve; a deferred backend that resolves later must hold its
139    /// own key material rather than borrow this backend's.
140    type Ok<T>: IntoFuture<Output = Result<T, PrfError<Self::BackendError>>>
141    where
142        T: Send + 'static;
143
144    /// Derive a protected byte vector in an explicit semantic `encoding`
145    /// domain. Backends must bind the encoding, context, and input with
146    /// prefix-free framing.
147    fn prf_bytes_vec<V>(
148        &self,
149        data: Protected<Vec<u8>>,
150        encoding: PrfEncoding,
151        context: Context<'static>,
152        visitor: V,
153    ) -> Self::Ok<V::Value>
154    where
155        V: PrfVisitor<Self::Block, Self::Passthrough>;
156
157    /// Fixed-array counterpart to [`prf_bytes_vec`](Prf::prf_bytes_vec).
158    fn prf_bytes_array<const N: usize, V>(
159        &self,
160        data: Protected<[u8; N]>,
161        encoding: PrfEncoding,
162        context: Context<'static>,
163        visitor: V,
164    ) -> Self::Ok<V::Value>
165    where
166        V: PrfVisitor<Self::Block, Self::Passthrough>,
167    {
168        self.prf_bytes_vec(
169            Protected::new(data.risky_ref().to_vec()),
170            encoding,
171            context,
172            visitor,
173        )
174    }
175
176    fn prf_seq(&self, size_hint: Option<usize>) -> Self::SeqPrf<'_>;
177    fn prf_map(&self, size_hint: Option<usize>) -> Self::MapPrf<'_>;
178
179    fn prf_some<T, V>(&self, value: T, context: Context<'static>, visitor: V) -> Self::Ok<V::Value>
180    where
181        T: PrfValue,
182        V: PrfVisitor<Self::Block, Self::Passthrough>,
183    {
184        value.prf_visit_with_context(self, context, visitor)
185    }
186
187    fn prf_none<V>(&self, context: Context<'static>, visitor: V) -> Self::Ok<V::Value>
188    where
189        V: PrfVisitor<Self::Block, Self::Passthrough>;
190
191    /// Explicit, non-secret output channel. The value is not processed by the
192    /// PRF and must never contain secret material.
193    fn passthrough<V>(&self, value: Self::Passthrough, visitor: V) -> Self::Ok<V::Value>
194    where
195        V: PrfVisitor<Self::Block, Self::Passthrough>;
196
197    /// Type-erased passthrough for generic [`PrfValue`] implementations.
198    fn passthrough_boxed<V>(
199        &self,
200        value: Box<dyn Any + Send + 'static>,
201        visitor: V,
202    ) -> Self::Ok<V::Value>
203    where
204        V: PrfVisitor<Self::Block, Self::Passthrough>;
205
206    /// Construct an already-failed deferred output. This lets structural
207    /// drivers preserve the uniform awaitable return type.
208    fn failure<T>(&self, error: PrfError<Self::BackendError>) -> Self::Ok<T>
209    where
210        T: Send + 'static;
211}
212
213/// Sequence driver. A driver is owned by one caller for the length of one
214/// derivation, so its builder methods consume and return `Self`; it borrows
215/// the backend it was created from.
216pub trait SeqPrf: Sized {
217    type Prf: Prf<
218        Block = Self::Block,
219        BackendError = Self::BackendError,
220        Passthrough = Self::Passthrough,
221    >;
222    type Block: Send + 'static;
223    type BackendError: std::error::Error + Send + Sync + 'static;
224    type Passthrough: Send + 'static;
225
226    /// Sequence positions deliberately do not refine `context`. Equal values
227    /// in one equality-search domain therefore derive equal terms.
228    fn prf_next<T>(self, value: T, context: Context<'static>) -> Self
229    where
230        T: PrfValue;
231
232    fn passthrough_next(self, value: Self::Passthrough) -> Self;
233    fn passthrough_next_boxed(self, value: Box<dyn Any + Send + 'static>) -> Self;
234
235    fn end<V>(self, visitor: V) -> <Self::Prf as Prf>::Ok<V::Value>
236    where
237        V: PrfVisitor<Self::Block, Self::Passthrough>;
238}
239
240/// Map driver. See [`SeqPrf`] for the ownership convention.
241pub trait MapPrf: Sized {
242    type Prf: Prf<
243        Block = Self::Block,
244        BackendError = Self::BackendError,
245        Passthrough = Self::Passthrough,
246    >;
247    type Block: Send + 'static;
248    type BackendError: std::error::Error + Send + Sync + 'static;
249    type Passthrough: Send + 'static;
250
251    fn prf_key<K>(self, key: K) -> Self
252    where
253        K: Into<Cow<'static, str>>;
254
255    /// Implementations automatically refine the supplied context with the
256    /// pending map key through `Context::for_map_entry`.
257    fn prf_value<T>(self, value: T, context: Context<'static>) -> Self
258    where
259        T: PrfValue;
260
261    fn prf_entry<K, T>(self, key: K, value: T, context: Context<'static>) -> Self
262    where
263        K: Into<Cow<'static, str>>,
264        T: PrfValue,
265    {
266        self.prf_key(key).prf_value(value, context)
267    }
268
269    fn passthrough_entry<K>(self, key: K, value: Self::Passthrough) -> Self
270    where
271        K: Into<Cow<'static, str>>;
272
273    fn passthrough_entry_boxed<K>(self, key: K, value: Box<dyn Any + Send + 'static>) -> Self
274    where
275        K: Into<Cow<'static, str>>;
276
277    fn end<V>(self, visitor: V) -> <Self::Prf as Prf>::Ok<V::Value>
278    where
279        V: PrfVisitor<Self::Block, Self::Passthrough>;
280}