vitaminc_protected/
controlled.rs

1pub use crate::Protected;
2use crate::{private::ControlledPrivate, AsProtectedRef, ProtectedRef, ReplaceT};
3use zeroize::Zeroize;
4
5pub trait Controlled: ControlledPrivate {
6    type Inner;
7
8    fn init_from_inner(x: Self::Inner) -> Self;
9    fn inner_mut(&mut self) -> &mut Self::Inner;
10
11    /// Initialize a new instance of the [Controlled] type from the inner value.
12    fn new(inner: Self::Inner) -> Self
13    where
14        Self: Sized,
15    {
16        Self::init_from_inner(inner)
17    }
18
19    /// Generate a new instance of the [Controlled] type from a function that returns the inner value.
20    ///
21    /// # Example
22    ///
23    /// Generate a new [Protected] from a function that returns an array.
24    ///
25    /// ```
26    /// use vitaminc_protected::{Controlled, Protected};
27    /// fn array_gen<const N: usize>() -> [u8; N] {
28    ///     let mut input: [u8; N] = [0; N];
29    ///     input.iter_mut().enumerate().for_each(|(i, x)| {
30    ///         *x = (i + 1) as u8;
31    ///     });
32    ///     input
33    /// }
34    /// let input: Protected<[u8; 8]> = Protected::generate(array_gen);
35    /// assert_eq!(input.risky_unwrap(), [1, 2, 3, 4, 5, 6, 7, 8]);
36    /// ```
37    /// // TODO: A Generate Array could handle the MaybeUninit stuff
38    fn generate<F>(f: F) -> Self
39    where
40        Self: Sized,
41        F: FnOnce() -> Self::Inner,
42    {
43        Self::init_from_inner(f())
44    }
45
46    /// Generate a new [Controlled] type from a function that returns a `Result` with the inner value.
47    ///
48    /// # Example
49    ///
50    /// Generate a new [Protected] from a function that returns a `Result` with the inner value.
51    ///
52    /// ```
53    /// use vitaminc_protected::{Controlled, Protected};
54    /// use std::string::FromUtf8Error;
55    ///
56    /// let input: Result<Protected<String>, FromUtf8Error> = Protected::generate_ok(|| {
57    ///    String::from_utf8(vec![1, 2, 3, 4, 5, 6, 7, 8])
58    /// });
59    /// ```
60    ///
61    fn generate_ok<F, E>(f: F) -> Result<Self, E>
62    where
63        Self: Sized,
64        F: FnOnce() -> Result<Self::Inner, E>,
65    {
66        f().map(Self::init_from_inner)
67    }
68
69    /// Map the inner value of this [Controlled] type`.
70    /// Conceptually similar to `Option::map`.
71    ///
72    /// # Example
73    ///
74    /// Map the inner value of a [Protected] to a new value.
75    ///
76    /// ```
77    /// use vitaminc_protected::{Controlled, Protected};
78    /// let x = Protected::new(100u8);
79    /// let y = x.map(|x| x + 10);
80    /// assert_eq!(y.risky_unwrap(), 110);
81    /// ```
82    fn map<B, F>(self, f: F) -> <Self as ReplaceT<B>>::Output
83    where
84        Self: Sized + ReplaceT<B>,
85        F: FnOnce(<Self as Controlled>::Inner) -> B,
86        <Self as ReplaceT<B>>::Output: Controlled<Inner = B>,
87        B: Zeroize,
88    {
89        <Self as ReplaceT<B>>::Output::init_from_inner(f(self.risky_unwrap()))
90    }
91
92    /// Similar to `map` but the closure returns a `Result` with the new inner value.
93    /// The result is a `Result` with the new [Controlled] type.
94    ///
95    /// # Example
96    ///
97    /// Map the inner value of a [Protected] to a new value that is wrapped in a `Result`.
98    ///
99    /// ```
100    /// use vitaminc_protected::{Controlled, Protected};
101    /// let x = Protected::new(vec![240, 159, 146, 150]);
102    /// let y = x.map_ok(String::from_utf8);
103    /// assert!(matches!(y, Ok(_)));
104    /// assert_eq!(y.unwrap().risky_unwrap(), "💖");
105    /// ```
106    fn map_ok<B, F, E>(self, f: F) -> Result<<Self as ReplaceT<B>>::Output, E>
107    where
108        Self: Sized + ReplaceT<B>,
109        F: FnOnce(<Self as Controlled>::Inner) -> Result<B, E>,
110        <Self as ReplaceT<B>>::Output: Controlled<Inner = B>,
111        B: Zeroize,
112    {
113        f(self.risky_unwrap()).map(<Self as ReplaceT<B>>::Output::init_from_inner)
114    }
115
116    /// Zip two [Controlled] values of the same type together with a function that combines them.
117    ///
118    /// # Example
119    ///
120    /// Add two [Protected] values together.
121    ///
122    /// ```
123    /// use vitaminc_protected::{Controlled, Protected};
124    /// let x = Protected::new(1);
125    /// let y = Protected::new(2);
126    /// let z = x.zip(y, |x, y| x + y);
127    /// assert_eq!(z.risky_unwrap(), 3);
128    /// ```
129    ///
130    /// TODO: Apply Usage trait bounds to prevent accidental broadening of scope
131    /// e.g. `other` must have the same, or broader scope as `self`
132    fn zip<Other, Out, F>(self, b: Other, f: F) -> Protected<Out>
133    where
134        Self: Sized,
135        Other: Controlled,
136        Out: Zeroize,
137        F: FnOnce(Self::Inner, Other::Inner) -> Out,
138    {
139        // TODO: Use Replace private trait
140        Protected::init_from_inner(f(self.risky_unwrap(), b.risky_unwrap()))
141    }
142
143    /// Like `zip` but the second argument is a reference.
144    ///
145    /// # Example
146    ///
147    /// ```
148    /// use vitaminc_protected::{Controlled, Protected};
149    /// let x = Protected::new(String::from("hello "));
150    /// let y = Protected::new(String::from("world"));
151    /// let z = x.zip_ref(&y, |x, y| x + y);
152    /// assert_eq!(z.risky_unwrap(), "hello world");
153    /// ```
154    ///
155    fn zip_ref<'a, A, Other, Out, F>(
156        self,
157        other: &'a Other,
158        f: F,
159    ) -> <Self as ReplaceT<Out>>::Output
160    where
161        A: ?Sized + 'a,
162        Self: Sized + ReplaceT<Out>,
163        <Self as ReplaceT<Out>>::Output: Controlled<Inner = Out>,
164        Other: AsProtectedRef<'a, A>,
165        Out: Zeroize,
166        F: FnOnce(Self::Inner, &A) -> Out,
167    {
168        let arg: ProtectedRef<'a, A> = other.as_protected_ref();
169        <Self as ReplaceT<Out>>::Output::init_from_inner(f(self.risky_unwrap(), arg.inner_ref()))
170    }
171
172    /// Similar to `map` but using references to that the inner value is updated in place.
173    ///
174    /// # Example
175    ///
176    /// ```
177    /// # use vitaminc_protected::{Controlled, Protected};
178    /// let mut x = Protected::new([0u8; 4]);
179    /// x.update(|x| {
180    ///   x.iter_mut().for_each(|x| {
181    ///    *x += 1;
182    ///  });
183    /// });
184    /// assert_eq!(x.risky_unwrap(), [1, 1, 1, 1]);
185    /// ```
186    ///
187    fn update<F>(&mut self, mut f: F)
188    where
189        F: FnMut(&mut Self::Inner),
190    {
191        f(self.inner_mut());
192    }
193
194    /// Update the inner value with another [Controlled] value.
195    /// The inner value of the second argument is passed to the closure.
196    ///
197    /// # Example
198    ///
199    /// ```
200    /// use vitaminc_protected::{Controlled, Protected};
201    /// let mut x = Protected::new([0u8; 32]);
202    /// let y = Protected::new([1u8; 32]);
203    /// x.update_with(y, |x, y| {
204    ///    x.copy_from_slice(&y);
205    /// });
206    /// assert_eq!(x.risky_unwrap(), [1u8; 32]);
207    /// ```
208    ///
209    /// TODO: Apply Usage trait bounds to prevent accidental broadening of scope
210    /// e.g. `other` must have the same, or broader scope as `self`
211    fn update_with<Other, F>(&mut self, other: Other, mut f: F)
212    where
213        F: FnMut(&mut Self::Inner, Other::Inner),
214        Other: Controlled,
215    {
216        // FIXME: There's a chance here that other will be dropped and not zeroized correctly
217        // But not all Zeroize types are ZeroizeOnDrop - we may need to yield a wrapper type that Derefs to the inner value
218        // Ditto for the zip method
219        // Either that or just make sure the caller uses zeroize() on the other value :/
220        f(self.inner_mut(), other.risky_unwrap());
221    }
222
223    /// Like `update_with` but the second argument is a reference.
224    ///
225    /// # Example
226    ///
227    /// ```
228    /// # use vitaminc_protected::{Controlled, Protected};
229    /// use vitaminc_protected::AsProtectedRef;
230    ///
231    /// let mut x = Protected::new([0u8; 32]);
232    /// let y = Protected::new([1u8; 32]);
233    /// x.update_with_ref(y.as_protected_ref(), |x, y| {
234    ///   x.copy_from_slice(y);
235    /// });
236    /// assert_eq!(x.risky_unwrap(), [1u8; 32]);
237    /// ```
238    ///
239    fn update_with_ref<'a, A, F>(&mut self, other: ProtectedRef<'a, A>, mut f: F)
240    where
241        A: ?Sized + 'a,
242        F: FnMut(&mut Self::Inner, &A),
243    {
244        f(self.inner_mut(), other.inner_ref());
245    }
246
247    /// Iterate over the inner value and wrap each element in a `Protected`.
248    /// `I` must be `Copy` because [Protected] always takes ownership of the inner value.
249    fn iter<'a, I>(&'a self) -> impl Iterator<Item = Protected<I>>
250    where
251        <Self as Controlled>::Inner: AsRef<[I]>,
252        I: Copy + 'a,
253    {
254        self.risky_ref().as_ref().iter().copied().map(Protected)
255    }
256
257    /// Replace the inner value with a new one.
258    /// The new value must be `Self`.
259    ///
260    /// # Example
261    ///
262    /// ```
263    /// use vitaminc_protected::{Controlled, Protected};
264    /// let mut x = Protected::new([0u8; 32]);
265    /// let y = Protected::new([1u8; 32]);
266    /// x.replace(y);
267    /// assert_eq!(x.risky_unwrap(), [1u8; 32]);
268    /// ```
269    ///
270    fn replace(&mut self, new: Self) -> Self
271    where
272        Self: Sized,
273    {
274        Self::init_from_inner(std::mem::replace(self.inner_mut(), new.risky_unwrap()))
275    }
276
277    /// Unwraps the inner value of the [Controlled] type.
278    /// This is a risky operation because it consumes the [Controlled] type and returns the inner value
279    /// negating the protections that the [Controlled] type provides.
280    ///
281    /// **Use with caution!**
282    ///
283    // TODO: Consider feature flagging this method
284    fn risky_unwrap(self) -> Self::Inner;
285
286    /// Provides a reference to the inner value.
287    /// This is a risky operation because it bypasses the protections that the [Controlled] type provides.
288    /// **Use with caution!**
289    fn risky_ref(&self) -> &Self::Inner;
290
291    /// Provides a mutable reference to the inner value.
292    /// This is a risky operation because it bypasses the protections that the [Controlled] type provides.
293    /// **Use with caution!**
294    // TODO: This is an escape hatch until I find a way to work around it
295    // It is needed for the seal_in_place methods for ring and aws-lc
296    fn risky_inner_mut(&mut self) -> &mut Self::Inner {
297        self.inner_mut()
298    }
299}
300
301// TODO: Implement Collect for Protected (or Paranoid) so we can use collect() on iterators
302
303#[cfg(test)]
304mod tests {
305    use crate::{Controlled, Equatable, Exportable, Protected};
306
307    #[test]
308    fn test_map_homogenous_inner() {
309        let x = Protected::new(100u8);
310        let y = x.map(|x| x + 10);
311        assert_eq!(y.risky_unwrap(), 110u8);
312    }
313
314    #[test]
315    fn test_map_different_inner() {
316        let x = Protected::new(100u8);
317        let y: Protected<u16> = x.map(u16::from);
318        assert_eq!(y.risky_unwrap(), 100u16);
319    }
320
321    #[test]
322    fn test_map_adapter_homogenous_inner() {
323        assert_eq!(
324            Exportable::<Protected<u8>>::new(100)
325                .map(|x| x + 10)
326                .risky_unwrap(),
327            110u8
328        );
329        assert_eq!(
330            Equatable::<Protected<u8>>::new(100)
331                .map(|x| x + 10)
332                .risky_unwrap(),
333            110u8
334        );
335        assert_eq!(
336            Exportable::<Equatable<Protected<u8>>>::new(100)
337                .map(|x| x + 10)
338                .risky_unwrap(),
339            110u8
340        );
341        assert_eq!(
342            Equatable::<Exportable<Protected<u8>>>::new(100)
343                .map(|x| x + 10)
344                .risky_unwrap(),
345            110u8
346        );
347    }
348
349    #[test]
350    fn test_map_adapter_different_inner() {
351        assert_eq!(
352            Exportable::<Protected<u8>>::new(100)
353                .map(u16::from)
354                .risky_unwrap(),
355            100u16
356        );
357        assert_eq!(
358            Equatable::<Protected<u8>>::new(100)
359                .map(u16::from)
360                .risky_unwrap(),
361            100u16
362        );
363        assert_eq!(
364            Exportable::<Equatable<Protected<u8>>>::new(100)
365                .map(u16::from)
366                .risky_unwrap(),
367            100u16
368        );
369        assert_eq!(
370            Equatable::<Exportable<Protected<u8>>>::new(100)
371                .map(u16::from)
372                .risky_unwrap(),
373            100u16
374        );
375    }
376}