1#![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
35pub trait Engine: Csprng + RawSecretWrap<Self> + Sized {
37 type CS: CipherSuite;
39
40 type WrappedKey: WrappedKey;
43
44 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 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
64pub trait WrappedKey: Identified + Serialize + DeserializeOwned + Sized {}
71
72pub trait UnwrappedKey<CS: CipherSuite>: Sized + Identified {
74 const ID: AlgId;
76
77 fn into_secret(self) -> Secret<CS>;
79
80 fn try_from_secret(key: UnwrappedSecret<CS>) -> Result<Self, WrongKeyType>;
82}
83
84pub struct Secret<CS: CipherSuite>(RawSecret<CS>);
89
90impl<CS: CipherSuite> Secret<CS> {
91 pub const fn new(secret: RawSecret<CS>) -> Self {
93 Self(secret)
94 }
95}
96
97pub struct UnwrappedSecret<CS: CipherSuite>(RawSecret<CS>);
102
103impl<CS: CipherSuite> UnwrappedSecret<CS> {
104 pub fn into_raw(self) -> RawSecret<CS> {
106 self.0
107 }
108}
109
110pub trait RawSecretWrap<E: Engine> {
113 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 fn unwrap_secret<T>(&self, key: &E::WrappedKey) -> Result<RawSecret<E::CS>, UnwrapError>
134 where
135 T: UnwrappedKey<E::CS>;
136}
137
138pub enum RawSecret<CS: CipherSuite> {
140 Aead(<CS::Aead as Aead>::Key),
142 Decap(<CS::Kem as Kem>::DecapKey),
144 Mac(<CS::Mac as Mac>::Key),
146 Prk(Prk<<CS::Kdf as Kdf>::PrkSize>),
148 Seed([u8; 64]),
150 Signing(<CS::Signer as Signer>::SigningKey),
152}
153
154impl<CS: CipherSuite> RawSecret<CS> {
155 pub const fn name(&self) -> &'static str {
157 self.alg_id().name()
158 }
159
160 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#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
175pub enum AlgId {
176 Aead(&'static Oid),
178 Decap(&'static Oid),
180 Mac(&'static Oid),
182 Prk(&'static Oid),
184 Seed(()),
186 Signing(&'static Oid),
188}
189
190impl AlgId {
191 #[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 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#[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#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
305#[error("wrong key type: got {got}, expected {expected}")]
306pub struct WrongKeyType {
307 pub got: &'static str,
309 pub expected: &'static str,
311}
312
313#[derive(Debug, Eq, PartialEq, thiserror::Error)]
315pub enum WrapError {
316 #[error("unable to wrap key: {0}")]
318 Other(&'static str),
319 #[error("unable to wrap key: {0}")]
321 Export(#[from] ExportError),
322 #[error("unable to wrap key: {0}")]
324 Seal(#[from] SealError),
325 #[error("unable to wrap key: {0}")]
327 Bug(#[from] Bug),
328 #[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#[derive(Debug, Eq, PartialEq, thiserror::Error)]
341pub enum UnwrapError {
342 #[error("unable to unwrap key: {0}")]
344 Other(&'static str),
345 #[error("unable to unwrap key: {0}")]
347 Open(#[from] OpenError),
348 #[error("unable to unwrap key: {0}")]
350 Import(#[from] ImportError),
351 #[error("unable to unwrap key: {0}")]
353 WrongKeyType(#[from] WrongKeyType),
354 #[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> }