Skip to main content

aranya_crypto/
engine.rs

1//! The cryptography engine.
2//!
3//! # Warning
4//!
5//! This is a low-level module. You should not be using it
6//! directly unless you are implementing an engine.
7
8#![forbid(unsafe_code)]
9
10#[cfg(feature = "alloc")]
11extern crate alloc;
12
13#[cfg(feature = "alloc")]
14use alloc::{boxed::Box, rc::Rc, sync::Arc};
15use core::{convert::Infallible, fmt::Debug, hash::Hash, result::Result};
16
17use buggy::Bug;
18use serde::{Serialize, de::DeserializeOwned};
19use spideroak_crypto::{
20    aead::{Aead, OpenError, SealError},
21    csprng::Csprng,
22    import::{ExportError, ImportError},
23    kdf::{Kdf, Prk},
24    kem::Kem,
25    mac::Mac,
26    oid::{self, Identified as _, Oid},
27    signer::Signer,
28};
29
30use crate::{
31    ciphersuite::CipherSuite,
32    id::{IdError, Identified},
33};
34
35/// The core trait used by the cryptography engine APIs.
36pub trait Engine: Csprng + RawSecretWrap<Self> + Sized {
37    /// The engine's [`CipherSuite`].
38    type CS: CipherSuite;
39
40    /// An encrypted, authenticated key that can only be
41    /// decrypted with [`Engine::unwrap`].
42    type WrappedKey: WrappedKey;
43
44    /// Encrypts and authenticates an unwrapped key.
45    fn wrap<T>(&self, key: T) -> Result<Self::WrappedKey, WrapError>
46    where
47        T: UnwrappedKey<Self::CS>,
48    {
49        let id = key.id()?;
50        let secret = key.into_secret();
51        self.wrap_secret::<T>(&id, secret.0)
52    }
53
54    /// Decrypts and authenticates the wrapped key.
55    fn unwrap<T>(&self, key: &Self::WrappedKey) -> Result<T, UnwrapError>
56    where
57        T: UnwrappedKey<Self::CS>,
58    {
59        let secret = self.unwrap_secret::<T>(key)?;
60        Ok(T::try_from_secret(UnwrappedSecret(secret))?)
61    }
62}
63
64/// An encrypted, authenticated key created by [`Engine::wrap`]
65/// that can only be decrypted by [`Engine::unwrap`].
66///
67/// It need not directly contain the ciphertext. For example,
68/// it might only contain an identifier used to look up the
69/// key in an HSM.
70pub trait WrappedKey: Identified + Serialize + DeserializeOwned + Sized {}
71
72/// A key that an [`Engine`] can wrap.
73pub trait UnwrappedKey<CS: CipherSuite>: Sized + Identified {
74    /// The key's algorithm identifier.
75    const ID: AlgId;
76
77    /// Converts itself into the underlying [`Secret`].
78    fn into_secret(self) -> Secret<CS>;
79
80    /// Converts itself from a [`UnwrappedSecret`].
81    fn try_from_secret(key: UnwrappedSecret<CS>) -> Result<Self, WrongKeyType>;
82}
83
84/// A cryptographic secret underlying an [`UnwrappedKey`].
85///
86/// It is intentionally opaque; only [`Engine::wrap`] can access
87/// the internal [`RawSecret`].
88pub struct Secret<CS: CipherSuite>(RawSecret<CS>);
89
90impl<CS: CipherSuite> Secret<CS> {
91    /// Creates a new [`Secret`].
92    pub const fn new(secret: RawSecret<CS>) -> Self {
93        Self(secret)
94    }
95}
96
97/// A cryptographic secret as unwrapped by an [`Engine`].
98///
99/// It is intentionally opaque; only [`Engine::unwrap`] can
100/// construct this type.
101pub struct UnwrappedSecret<CS: CipherSuite>(RawSecret<CS>);
102
103impl<CS: CipherSuite> UnwrappedSecret<CS> {
104    /// Returns the underlying [`RawSecret`].
105    pub fn into_raw(self) -> RawSecret<CS> {
106        self.0
107    }
108}
109
110/// Encrypts and authenticates [`RawSecret`]s from
111/// [`UnwrappedKey`]s.
112pub trait RawSecretWrap<E: Engine> {
113    /// Encrypts and authenticates an unwrapped key.
114    ///
115    /// # Warning
116    ///
117    /// This method is used by [`Engine::wrap`] and should not be
118    /// called manually.
119    fn wrap_secret<T>(
120        &self,
121        id: &<T as Identified>::Id,
122        secret: RawSecret<E::CS>,
123    ) -> Result<E::WrappedKey, WrapError>
124    where
125        T: UnwrappedKey<E::CS>;
126
127    /// Decrypts and authenticates the wrapped key.
128    ///
129    /// # Warning
130    ///
131    /// This method is used by [`Engine::unwrap`] and should not
132    /// be called manually.
133    fn unwrap_secret<T>(&self, key: &E::WrappedKey) -> Result<RawSecret<E::CS>, UnwrapError>
134    where
135        T: UnwrappedKey<E::CS>;
136}
137
138/// A raw, unwrapped secret.
139pub enum RawSecret<CS: CipherSuite> {
140    /// A symmetric AEAD key.
141    Aead(<CS::Aead as Aead>::Key),
142    /// An asymmetric decapsulation key.
143    Decap(<CS::Kem as Kem>::DecapKey),
144    /// A MAC key.
145    Mac(<CS::Mac as Mac>::Key),
146    /// A PRK.
147    Prk(Prk<<CS::Kdf as Kdf>::PrkSize>),
148    /// Cryptographic seeds.
149    Seed([u8; 64]),
150    /// An asymmetric signing key.
151    Signing(<CS::Signer as Signer>::SigningKey),
152}
153
154impl<CS: CipherSuite> RawSecret<CS> {
155    /// Returns the string name of the key.
156    pub const fn name(&self) -> &'static str {
157        self.alg_id().name()
158    }
159
160    /// Returns the secret's algorithm identifier.
161    pub const fn alg_id(&self) -> AlgId {
162        match self {
163            Self::Aead(_) => AlgId::Aead(CS::Aead::OID),
164            Self::Decap(_) => AlgId::Decap(CS::Kem::OID),
165            Self::Mac(_) => AlgId::Mac(CS::Mac::OID),
166            Self::Prk(_) => AlgId::Prk(CS::Kdf::OID),
167            Self::Seed(_) => AlgId::Seed(()),
168            Self::Signing(_) => AlgId::Signing(CS::Signer::OID),
169        }
170    }
171}
172
173/// An algorithm identifier for [`UnwrappedKey`].
174#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
175pub enum AlgId {
176    /// See [`RawSecret::Aead`].
177    Aead(&'static Oid),
178    /// See [`RawSecret::Decap`].
179    Decap(&'static Oid),
180    /// See [`RawSecret::Mac`].
181    Mac(&'static Oid),
182    /// See [`RawSecret::Prk`].
183    Prk(&'static Oid),
184    /// See [`RawSecret::Seed`].
185    Seed(()),
186    /// See [`RawSecret::Signing`].
187    Signing(&'static Oid),
188}
189
190impl AlgId {
191    /// Returns the string name of the key.
192    #[inline]
193    pub const fn name(&self) -> &'static str {
194        match self {
195            Self::Aead(_) => "Aead",
196            Self::Decap(_) => "Decap",
197            Self::Mac(_) => "Mac",
198            Self::Prk(_) => "Prk",
199            Self::Seed(()) => "Seed",
200            Self::Signing(_) => "Signing",
201        }
202    }
203
204    pub(crate) const fn as_bytes(&self) -> &[u8] {
205        match self {
206            Self::Aead(id)
207            | Self::Decap(id)
208            | Self::Mac(id)
209            | Self::Prk(id)
210            | Self::Signing(id) => id.as_bytes(),
211            Self::Seed(()) => b"64 byte Seed",
212        }
213    }
214}
215
216macro_rules! alg_id_from_impl {
217    ($($name:ident => $ty:ident),* $(,)?) => {
218        $(impl AlgId {
219            #[doc(hidden)]
220            // Not part of the public API. Do not use.
221            pub const fn $name<CS: CipherSuite>() -> &'static Oid {
222                <CS::$ty as oid::Identified>::OID
223            }
224        })*
225    }
226}
227alg_id_from_impl! {
228    _from_aead => Aead,
229    _from_kem => Kem,
230    _from_mac => Mac,
231    _from_kdf => Kdf,
232    _from_signer => Signer,
233}
234
235/// Implements [`UnwrappedKey`] for `$name`.
236///
237/// - `$type` identifies which variant should be used.
238/// - `$into` is a function that takes `Self` and returns the
239///   inner value for the `$type` variant.
240/// - `$from` is a function that takes the inner value for the
241///   `$type` variant and returns `Self`.
242#[macro_export]
243macro_rules! unwrapped {
244    { name: $name:ident; type: Aead; into: $into:expr; from: $from:expr $(;)? } => {
245        $crate::__unwrapped_inner!(Aead, $crate::engine::AlgId::_from_aead::<CS>(), $name, $into, $from);
246    };
247    { name: $name:ident; type: Decap; into: $into:expr; from: $from:expr $(;)? } => {
248        $crate::__unwrapped_inner!(Decap, $crate::engine::AlgId::_from_kem::<CS>(), $name, $into, $from);
249    };
250    { name: $name:ident; type: Mac; into: $into:expr; from: $from:expr $(;)? } => {
251        $crate::__unwrapped_inner!(Mac, $crate::engine::AlgId::_from_mac::<CS>(), $name, $into, $from);
252    };
253    { name: $name:ident; type: Prk; into: $into:expr; from: $from:expr $(;)? } => {
254        $crate::__unwrapped_inner!(Prk, $crate::engine::AlgId::_from_kdf::<CS>(), $name, $into, $from);
255    };
256    { name: $name:ident; type: Seed; into: $into:expr; from: $from:expr $(;)? } => {
257        $crate::__unwrapped_inner!(Seed, (), $name, $into, $from);
258    };
259    { name: $name:ident; type: Signing; into: $into:expr; from: $from:expr $(;)? } => {
260        $crate::__unwrapped_inner!(Signing, $crate::engine::AlgId::_from_signer::<CS>(), $name, $into, $from);
261    };
262    ($($fallthrough:tt)*) => {
263        ::core::compile_error!("unknown variant");
264    };
265}
266pub(crate) use unwrapped;
267
268#[doc(hidden)]
269#[macro_export]
270macro_rules! __unwrapped_inner {
271    ($enum:ident, $id:expr, $name:ident, $into:expr, $from:expr) => {
272        impl<CS: $crate::CipherSuite> $crate::engine::UnwrappedKey<CS> for $name<CS> {
273            const ID: $crate::engine::AlgId = $crate::engine::AlgId::$enum($id);
274
275            #[inline]
276            fn into_secret(self) -> $crate::engine::Secret<CS> {
277                $crate::engine::Secret::new($crate::engine::RawSecret::$enum(
278                    #[allow(clippy::redundant_closure_call)]
279                    $into(self),
280                ))
281            }
282
283            #[inline]
284            fn try_from_secret(
285                key: $crate::engine::UnwrappedSecret<CS>,
286            ) -> ::core::result::Result<Self, $crate::engine::WrongKeyType> {
287                match key.into_raw() {
288                    $crate::engine::RawSecret::$enum(key) => ::core::result::Result::Ok(
289                        #[allow(clippy::redundant_closure_call)]
290                        $from(key),
291                    ),
292                    got => ::core::result::Result::Err($crate::engine::WrongKeyType {
293                        got: got.name(),
294                        expected: ::core::stringify!($name),
295                    }),
296                }
297            }
298        }
299    };
300}
301
302/// Returned when converting [`UnwrappedKey`]s to concrete key
303/// types.
304#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
305#[error("wrong key type: got {got}, expected {expected}")]
306pub struct WrongKeyType {
307    /// The type of key received.
308    pub got: &'static str,
309    /// The expected key type.
310    pub expected: &'static str,
311}
312
313/// An error from [`Engine::wrap`].
314#[derive(Debug, Eq, PartialEq, thiserror::Error)]
315pub enum WrapError {
316    /// An unknown or internal error has occurred.
317    #[error("unable to wrap key: {0}")]
318    Other(&'static str),
319    /// The secret key data cannot be exported.
320    #[error("unable to wrap key: {0}")]
321    Export(#[from] ExportError),
322    /// The encoded secret key cannot be encrypted.
323    #[error("unable to wrap key: {0}")]
324    Seal(#[from] SealError),
325    /// A bug was discovered.
326    #[error("unable to wrap key: {0}")]
327    Bug(#[from] Bug),
328    /// An error occurred accessing the unique ID.
329    #[error("unable to wrap key: {0}")]
330    Id(#[from] IdError),
331}
332
333impl From<Infallible> for WrapError {
334    fn from(err: Infallible) -> Self {
335        match err {}
336    }
337}
338
339/// An error from [`Engine::unwrap`].
340#[derive(Debug, Eq, PartialEq, thiserror::Error)]
341pub enum UnwrapError {
342    /// An unknown or internal error has occurred.
343    #[error("unable to unwrap key: {0}")]
344    Other(&'static str),
345    /// The wrapped key could not be decrypted.
346    #[error("unable to unwrap key: {0}")]
347    Open(#[from] OpenError),
348    /// The unwrapped secret key data cannot be imported.
349    #[error("unable to unwrap key: {0}")]
350    Import(#[from] ImportError),
351    /// Could not convert the [`UnwrappedKey`] to `T`.
352    #[error("unable to unwrap key: {0}")]
353    WrongKeyType(#[from] WrongKeyType),
354    /// A bug was discovered.
355    #[error("unable to unwrap key: {0}")]
356    Bug(#[from] Bug),
357}
358
359impl From<Infallible> for UnwrapError {
360    fn from(err: Infallible) -> Self {
361        match err {}
362    }
363}
364
365macro_rules! blanket_impl {
366    ($e:ident => $ty:ty) => {
367        impl<$e: Engine> Engine for $ty {
368            type CS = $e::CS;
369            type WrappedKey = $e::WrappedKey;
370        }
371
372        impl<$e: Engine> RawSecretWrap<Self> for $ty {
373            fn unwrap_secret<T>(
374                &self,
375                key: &$e::WrappedKey,
376            ) -> Result<RawSecret<$e::CS>, UnwrapError>
377            where
378                T: UnwrappedKey<$e::CS>,
379            {
380                $e::unwrap_secret::<T>(self, key)
381            }
382
383            fn wrap_secret<T>(
384                &self,
385                id: &<T as Identified>::Id,
386                secret: RawSecret<$e::CS>,
387            ) -> Result<$e::WrappedKey, WrapError>
388            where
389                T: UnwrappedKey<$e::CS>,
390            {
391                $e::wrap_secret::<T>(self, id, secret)
392            }
393        }
394    };
395}
396
397blanket_impl! { E => &E }
398blanket_impl! { E => &mut E }
399
400#[cfg(feature = "alloc")]
401blanket_impl! { E => Box<E> }
402#[cfg(feature = "alloc")]
403blanket_impl! { E => Rc<E> }
404#[cfg(feature = "alloc")]
405blanket_impl! { E => Arc<E> }