vitaminc_protected/protected/
mod.rs

1use super::Controlled;
2use crate::private::ControlledPrivate;
3use zeroize::{Zeroize, ZeroizeOnDrop};
4
5/// The most basic controlled type.
6/// It ensures inner types are `Zeroize` and implements `Debug` and `Display` safely (i.e. inner sensitive values are redacted).
7#[derive(Zeroize)]
8pub struct Protected<T>(pub(crate) T);
9
10opaque_debug::implement!(Protected<T>);
11
12impl<T> Protected<T> {
13    /// Create a new [Protected] from an inner value.
14    pub const fn new(x: T) -> Self
15    where
16        T: Zeroize,
17    {
18        Self(x)
19    }
20}
21
22impl<T> Protected<Protected<T>> {
23    #[inline]
24    /// Flatten a [Protected] of [Protected] into a single [Protected].
25    /// Similar to `Option::flatten`.
26    ///
27    /// ```
28    /// use vitaminc_protected::{Controlled, Protected};
29    /// let x = Protected::new(Protected::new([0u8; 32]));
30    /// let y = x.flatten();
31    /// assert_eq!(y.risky_unwrap(), [0u8; 32]);
32    /// ```
33    ///
34    /// Like [Option], flattening only removes one level of nesting at a time.
35    ///
36    pub fn flatten(self) -> Protected<T> {
37        self.0
38    }
39}
40
41impl<T> Protected<Option<T>> {
42    #[inline]
43    /// Transpose a [Protected] of `Option` into an `Option` of [Protected].
44    /// Similar to `Option::transpose`.
45    ///
46    /// ```
47    /// use vitaminc_protected::Protected;
48    /// let x = Protected::new(Some([0u8; 32]));
49    /// let y = x.transpose();
50    /// assert!(y.is_some())
51    /// ```
52    pub fn transpose(self) -> Option<Protected<T>> {
53        self.0.map(Protected)
54    }
55}
56
57impl<T: Zeroize> ZeroizeOnDrop for Protected<T> {}
58
59impl<T> ControlledPrivate for Protected<T> {}
60
61impl<T> Controlled for Protected<T>
62where
63    T: Zeroize,
64{
65    fn risky_unwrap(self) -> Self::Inner {
66        self.0
67    }
68
69    type Inner = T;
70
71    fn init_from_inner(x: Self::Inner) -> Self {
72        Self(x)
73    }
74
75    fn risky_ref(&self) -> &T {
76        &self.0
77    }
78
79    fn inner_mut(&mut self) -> &mut Self::Inner {
80        &mut self.0
81    }
82}
83
84impl<T> Copy for Protected<T> where T: Copy {}
85
86impl<T> Clone for Protected<T>
87where
88    T: Clone,
89{
90    fn clone(&self) -> Self {
91        Self(self.0.clone())
92    }
93}
94
95impl<T, A> Extend<A> for Protected<T>
96where
97    T: Extend<A>,
98{
99    fn extend<I>(&mut self, iter: I)
100    where
101        I: IntoIterator<Item = A>,
102    {
103        self.0.extend(iter);
104    }
105}
106
107#[cfg(feature = "arbitrary")]
108impl<T> quickcheck::Arbitrary for Protected<T>
109where
110    T: quickcheck::Arbitrary + Zeroize,
111{
112    fn arbitrary(g: &mut quickcheck::Gen) -> Self {
113        let inner = T::arbitrary(g);
114        Self::new(inner)
115    }
116}
117
118/// Convenience function to flatten an array of [Protected] into a [Protected] array.
119///
120/// # Example
121///
122/// ```
123/// use vitaminc_protected::{flatten_array, Controlled, Protected};
124/// let x = Protected::new(1);
125/// let y = Protected::new(2);
126/// let z = Protected::new(3);
127/// let array: [Protected<u8>; 3] = [x, y, z];
128/// let flattened = flatten_array(array);
129/// assert!(matches!(flattened, Protected));
130/// assert_eq!(flattened.risky_unwrap(), [1, 2, 3]);
131/// ```
132pub fn flatten_array<const N: usize, T>(array: [Protected<T>; N]) -> Protected<[T; N]>
133where
134    T: Zeroize + Default + Copy, // TODO: Default won't be needed if we use MaybeUninit
135{
136    let mut out: [T; N] = [Default::default(); N];
137    array.iter().enumerate().for_each(|(i, x)| {
138        out[i] = x.risky_unwrap();
139    });
140    Protected::new(out)
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn test_new_array() {
149        let x = Protected::new([0u8; 32]);
150        assert_eq!(x.0, [0u8; 32]);
151    }
152
153    #[test]
154    fn test_opaque_debug() {
155        let x = Protected::new([0u8; 32]);
156        assert_eq!(format!("{x:?}"), "Protected<[u8; 32]> { ... }");
157    }
158
159    #[test]
160    fn test_flatten() {
161        let x = Protected::new(Protected::new([0u8; 32]));
162        let y = x.flatten();
163        assert_eq!(y.risky_unwrap(), [0u8; 32]);
164    }
165
166    #[test]
167    fn test_flatten_array() {
168        let x = Protected::new(1);
169        let y = Protected::new(2);
170        let z = Protected::new(3);
171        let array: [Protected<u8>; 3] = [x, y, z];
172        let flattened = flatten_array(array);
173        assert!(matches!(flattened, Protected(_)));
174        assert_eq!(flattened.risky_unwrap(), [1, 2, 3]);
175    }
176}