Skip to main content

spideroak_crypto/
keys.rs

1//! Basic keys and key material.
2
3use core::{borrow::Borrow, fmt, iter::IntoIterator, mem, result::Result};
4
5use generic_array::{ArrayLength, GenericArray, IntoArrayLength};
6use subtle::{Choice, ConstantTimeEq};
7use typenum::{generic_const_mappings::Const, IsLess, U65536};
8
9use crate::{
10    csprng::{Csprng, Random},
11    import::{ExportError, Import},
12    kdf::{Expand, Kdf, KdfError, Prk},
13    zeroize::{zeroize_flat_type, ZeroizeOnDrop},
14};
15
16/// A fixed-length secret key.
17///
18/// Secret keys are either symmetric keys (e.g., for AES) or
19/// asymmetric private keys (e.g., for ECDH).
20pub trait SecretKey:
21    Clone + ConstantTimeEq + for<'a> Import<&'a [u8]> + Random + ZeroizeOnDrop
22{
23    /// The size in octets of the key.
24    type Size: ArrayLength + 'static;
25
26    /// Attempts to export the key's secret data.
27    fn try_export_secret(&self) -> Result<SecretKeyBytes<Self::Size>, ExportError>;
28}
29
30/// Provides access to a secret's byte encoding.
31pub trait RawSecretBytes {
32    /// Returns the secret's byte encoding.
33    fn raw_secret_bytes(&self) -> &[u8];
34}
35
36impl<T: RawSecretBytes> RawSecretBytes for &T {
37    #[inline]
38    fn raw_secret_bytes(&self) -> &[u8] {
39        (**self).raw_secret_bytes()
40    }
41}
42
43impl RawSecretBytes for [u8] {
44    #[inline]
45    fn raw_secret_bytes(&self) -> &[u8] {
46        self
47    }
48}
49
50/// A fixed-length byte encoding of a [`SecretKey`]'s data.
51#[derive(Clone, Default)]
52#[repr(transparent)]
53pub struct SecretKeyBytes<N: ArrayLength>(GenericArray<u8, N>);
54
55impl<N: ArrayLength> fmt::Debug for SecretKeyBytes<N> {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("SecretKeyBytes").finish_non_exhaustive()
58    }
59}
60
61impl<N: ArrayLength> ZeroizeOnDrop for SecretKeyBytes<N> {}
62impl<N: ArrayLength> Drop for SecretKeyBytes<N> {
63    fn drop(&mut self) {
64        // SAFETY:
65        // - `self.0` does not contain references or dynamically
66        //   sized data.
67        // - `self.0` does not have a `Drop` impl.
68        // - `self.0` is not used after this function returns.
69        // - The bit pattern of all zeros is valid for `self.0`.
70        unsafe {
71            zeroize_flat_type(&mut self.0);
72        }
73    }
74}
75
76impl<N: ArrayLength> SecretKeyBytes<N> {
77    /// The size in bytes of the secret key.
78    pub const SIZE: usize = N::USIZE;
79
80    /// Creates a new secret.
81    #[inline]
82    pub const fn new(secret: GenericArray<u8, N>) -> Self {
83        Self(secret)
84    }
85
86    /// Returns the size in bytes of the secret key.
87    #[allow(clippy::len_without_is_empty)]
88    #[inline]
89    pub const fn len(&self) -> usize {
90        N::USIZE
91    }
92
93    /// Returns a reference to the secret key bytes as an array.
94    pub(crate) fn as_array<const U: usize>(&self) -> &[u8; U]
95    where
96        Const<U>: IntoArrayLength<ArrayLength = N>,
97    {
98        self.0.as_ref()
99    }
100
101    /// Returns the secret key bytes as a byte slice.
102    #[inline]
103    pub const fn as_bytes(&self) -> &[u8] {
104        self.0.as_slice()
105    }
106
107    /// Returns the secret as a mutable byte slice.
108    pub(crate) fn as_bytes_mut(&mut self) -> &mut [u8] {
109        &mut self.0
110    }
111
112    /// Converts the secret key bytes to an array.
113    #[inline]
114    pub fn into_bytes(mut self) -> GenericArray<u8, N> {
115        // This is fine since we're consuming the receiver. If
116        // the receiver were an exclusive reference this would be
117        // very wrong since it'd be replacing the secret key with
118        // all zeros.
119        mem::take(&mut self.0)
120    }
121}
122
123impl<N: ArrayLength> ConstantTimeEq for SecretKeyBytes<N> {
124    #[inline]
125    fn ct_eq(&self, other: &Self) -> Choice {
126        self.0.ct_eq(&other.0)
127    }
128}
129
130impl<N: ArrayLength> Random for SecretKeyBytes<N> {
131    fn random<R: Csprng>(rng: R) -> Self {
132        Self(Random::random(rng))
133    }
134}
135
136impl<N: ArrayLength> Expand for SecretKeyBytes<N>
137where
138    N: IsLess<U65536>,
139{
140    type Size = N;
141
142    fn expand_multi<'a, K, I>(prk: &Prk<K::PrkSize>, info: I) -> Result<Self, KdfError>
143    where
144        K: Kdf,
145        I: IntoIterator<Item = &'a [u8]>,
146        I::IntoIter: Clone,
147    {
148        Ok(Self(Expand::expand_multi::<K, I>(prk, info)?))
149    }
150}
151
152impl<N: ArrayLength> RawSecretBytes for SecretKeyBytes<N> {
153    #[inline]
154    fn raw_secret_bytes(&self) -> &[u8] {
155        self.as_bytes()
156    }
157}
158
159/// A fixed-length asymmetric public key.
160pub trait PublicKey: Clone + fmt::Debug + Eq + for<'a> Import<&'a [u8]> {
161    /// The fixed-length byte encoding of the key.
162    type Data: Borrow<[u8]> + Clone + Sized;
163
164    /// Returns the byte representation of the public key.
165    fn export(&self) -> Self::Data;
166}
167
168raw_key! {
169    /// A generic secret key.
170    pub RawKey,
171}
172
173/// Creates a "raw" (i.e., a byte array) key.
174///
175/// # Example
176///
177/// ```
178/// use spideroak_crypto::raw_key;
179///
180/// raw_key! {
181///     /// Some documentation.
182///     pub MyRawKey,
183///     /// Some more documentation.
184///     pub AnotherKey,
185/// }
186/// ```
187#[macro_export]
188macro_rules! raw_key {
189    () => {};
190    (
191        $(#[$meta:meta])*
192        $vis:vis $name:ident,
193        $($tail:tt)*
194    ) => {
195        $(#[$meta])*
196        #[derive(::core::clone::Clone)]
197        #[repr(transparent)]
198        $vis struct $name<N: $crate::generic_array::ArrayLength>($crate::keys::SecretKeyBytes<N>);
199
200        impl<N: ::generic_array::ArrayLength> $name<N> {
201            /// Creates a new raw key.
202            #[inline]
203            pub const fn new(key: $crate::keys::SecretKeyBytes<N>) -> Self {
204                Self(key)
205            }
206
207            /// Returns the length in bytes of the key.
208            ///
209            /// Will always be exactly `N`.
210            #[allow(clippy::len_without_is_empty)]
211            #[inline]
212            pub const fn len(&self) -> usize {
213                self.0.len()
214            }
215
216            /// Returns the raw key bytes.
217            #[inline]
218            pub const fn as_slice(&self) -> &[u8] {
219                self.0.as_bytes()
220            }
221
222            /// Returns the raw key bytes.
223            #[inline]
224            pub const fn as_bytes(&self) -> &$crate::keys::SecretKeyBytes<N> {
225                &self.0
226            }
227
228            /// Converts the key into its raw key bytes.
229            #[inline]
230            pub fn into_bytes(mut self) -> $crate::keys::SecretKeyBytes<N> {
231                // This is fine since we're consuming the
232                // receiver. If the receiver were an exclusive
233                // reference this would be very wrong since it'd
234                // be replacing the secret key with all zeros.
235                ::core::mem::take(&mut self.0)
236            }
237        }
238
239        impl<N: $crate::generic_array::ArrayLength> $crate::keys::SecretKey for $name<N> {
240            type Size = N;
241
242            #[inline]
243            fn try_export_secret(&self) -> ::core::result::Result<
244                $crate::keys::SecretKeyBytes<Self::Size>,
245                $crate::import::ExportError,
246            > {
247                ::core::result::Result::Ok(self.0.clone())
248            }
249        }
250
251        impl<N: $crate::generic_array::ArrayLength> $crate::csprng::Random for $name<N> {
252            fn random<R: $crate::csprng::Csprng>(rng: R) -> Self {
253                let sk = <$crate::keys::SecretKeyBytes<N> as $crate::csprng::Random>::random(rng);
254                Self(sk)
255            }
256        }
257
258        impl<N: $crate::generic_array::ArrayLength> $crate::keys::RawSecretBytes for $name<N> {
259            #[inline]
260            fn raw_secret_bytes(&self) -> &[u8] {
261                $crate::keys::RawSecretBytes::raw_secret_bytes(&self.0)
262            }
263        }
264
265        impl<N: $crate::generic_array::ArrayLength> $crate::kdf::Expand for $name<N>
266        where
267            N: ::typenum::IsLess<::typenum::U65536>
268        {
269            type Size = N;
270
271            fn expand_multi<'a, K, I>(
272                prk: &$crate::kdf::Prk<K::PrkSize>,
273                info: I,
274            ) -> ::core::result::Result<Self, $crate::kdf::KdfError>
275            where
276                K: $crate::kdf::Kdf,
277                I: ::core::iter::IntoIterator<Item = &'a [u8]>,
278                I::IntoIter: ::core::clone::Clone,
279            {
280                ::core::result::Result::Ok(Self($crate::kdf::Expand::expand_multi::<K, I>(prk, info)?))
281            }
282        }
283
284        impl<N: $crate::generic_array::ArrayLength> ::subtle::ConstantTimeEq for $name<N> {
285            #[inline]
286            fn ct_eq(&self, other: &Self) -> ::subtle::Choice {
287                self.0.ct_eq(&other.0)
288            }
289        }
290
291        impl<N, const U: usize> $crate::import::Import<[u8; U]> for $name<N>
292        where
293            N: $crate::generic_array::ArrayLength,
294            ::typenum::generic_const_mappings::Const<U>: $crate::generic_array::IntoArrayLength<ArrayLength = N>,
295        {
296            #[inline]
297            fn import(key: [u8; U]) -> ::core::result::Result<Self, $crate::import::ImportError> {
298                let sk = $crate::keys::SecretKeyBytes::new(key.into());
299                ::core::result::Result::Ok(Self(sk))
300            }
301        }
302
303        impl<N: $crate::generic_array::ArrayLength> $crate::import::Import<&[u8]> for $name<N> {
304            #[inline]
305            fn import(data: &[u8]) -> ::core::result::Result<Self, $crate::import::ImportError> {
306                let bytes = $crate::import::Import::<_>::import(data)?;
307                let sk = $crate::keys::SecretKeyBytes::new(bytes);
308                ::core::result::Result::Ok(Self(sk))
309            }
310        }
311
312        impl<N: ::generic_array::ArrayLength> ::core::fmt::Debug for $name<N> {
313            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
314                f.debug_struct(stringify!($name)).finish_non_exhaustive()
315            }
316        }
317
318        impl<N: $crate::generic_array::ArrayLength> $crate::zeroize::ZeroizeOnDrop for $name<N> {}
319        impl<N: $crate::generic_array::ArrayLength> Drop for $name<N> {
320            fn drop(&mut self) {
321                // SAFETY:
322                // - `self.0` does not contain references or
323                //   dynamically sized data.
324                // - `self.0` does not have a `Drop` impl.
325                // - `self.0` is not used after this function
326                //   returns.
327                // - The bit pattern of all zeros is valid for
328                //   `self.0`.
329                unsafe {
330                    $crate::zeroize::zeroize_flat_type(&mut self.0);
331                }
332            }
333        }
334
335        raw_key!{ $($tail)* }
336    };
337}
338pub(crate) use raw_key;
339
340/// The provided key is invalid.
341// TODO(eric): move this somewhere else.
342#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
343#[error("invalid key length")]
344pub struct InvalidKey;