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}