1#![forbid(unsafe_code)]
9
10use core::{convert::Infallible, fmt::Debug, hash::Hash, result::Result};
11
12use buggy::Bug;
13use serde::{Serialize, de::DeserializeOwned};
14use spideroak_crypto::{
15 aead::{Aead, OpenError, SealError},
16 csprng::Csprng,
17 import::{ExportError, ImportError},
18 kdf::{Kdf, Prk},
19 kem::Kem,
20 mac::Mac,
21 oid::{self, Identified as _, Oid},
22 signer::Signer,
23};
24
25use crate::{
26 ciphersuite::CipherSuite,
27 id::{IdError, Identified},
28};
29
30pub trait Engine: Csprng + RawSecretWrap<Self> + Sized {
32 type CS: CipherSuite;
34
35 type WrappedKey: WrappedKey;
38
39 fn wrap<T>(&mut self, key: T) -> Result<Self::WrappedKey, WrapError>
41 where
42 T: UnwrappedKey<Self::CS>,
43 {
44 let id = key.id()?;
45 let secret = key.into_secret();
46 self.wrap_secret::<T>(&id, secret.0)
47 }
48
49 fn unwrap<T>(&self, key: &Self::WrappedKey) -> Result<T, UnwrapError>
51 where
52 T: UnwrappedKey<Self::CS>,
53 {
54 let secret = self.unwrap_secret::<T>(key)?;
55 Ok(T::try_from_secret(UnwrappedSecret(secret))?)
56 }
57
58 fn destroy(self) {}
64}
65
66pub trait WrappedKey: Identified + Serialize + DeserializeOwned + Sized {}
73
74pub trait UnwrappedKey<CS: CipherSuite>: Sized + Identified {
76 const ID: AlgId;
78
79 fn into_secret(self) -> Secret<CS>;
81
82 fn try_from_secret(key: UnwrappedSecret<CS>) -> Result<Self, WrongKeyType>;
84}
85
86pub struct Secret<CS: CipherSuite>(RawSecret<CS>);
91
92impl<CS: CipherSuite> Secret<CS> {
93 pub const fn new(secret: RawSecret<CS>) -> Self {
95 Self(secret)
96 }
97}
98
99pub struct UnwrappedSecret<CS: CipherSuite>(RawSecret<CS>);
104
105impl<CS: CipherSuite> UnwrappedSecret<CS> {
106 pub fn into_raw(self) -> RawSecret<CS> {
108 self.0
109 }
110}
111
112pub trait RawSecretWrap<E: Engine> {
115 fn wrap_secret<T>(
122 &mut self,
123 id: &<T as Identified>::Id,
124 secret: RawSecret<E::CS>,
125 ) -> Result<E::WrappedKey, WrapError>
126 where
127 T: UnwrappedKey<E::CS>;
128
129 fn unwrap_secret<T>(&self, key: &E::WrappedKey) -> Result<RawSecret<E::CS>, UnwrapError>
136 where
137 T: UnwrappedKey<E::CS>;
138}
139
140pub enum RawSecret<CS: CipherSuite> {
142 Aead(<CS::Aead as Aead>::Key),
144 Decap(<CS::Kem as Kem>::DecapKey),
146 Mac(<CS::Mac as Mac>::Key),
148 Prk(Prk<<CS::Kdf as Kdf>::PrkSize>),
150 Seed([u8; 64]),
152 Signing(<CS::Signer as Signer>::SigningKey),
154}
155
156impl<CS: CipherSuite> RawSecret<CS> {
157 pub const fn name(&self) -> &'static str {
159 self.alg_id().name()
160 }
161
162 pub const fn alg_id(&self) -> AlgId {
164 match self {
165 Self::Aead(_) => AlgId::Aead(CS::Aead::OID),
166 Self::Decap(_) => AlgId::Decap(CS::Kem::OID),
167 Self::Mac(_) => AlgId::Mac(CS::Mac::OID),
168 Self::Prk(_) => AlgId::Prk(CS::Kdf::OID),
169 Self::Seed(_) => AlgId::Seed(()),
170 Self::Signing(_) => AlgId::Signing(CS::Signer::OID),
171 }
172 }
173}
174
175#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
177pub enum AlgId {
178 Aead(&'static Oid),
180 Decap(&'static Oid),
182 Mac(&'static Oid),
184 Prk(&'static Oid),
186 Seed(()),
188 Signing(&'static Oid),
190}
191
192impl AlgId {
193 #[inline]
195 pub const fn name(&self) -> &'static str {
196 match self {
197 Self::Aead(_) => "Aead",
198 Self::Decap(_) => "Decap",
199 Self::Mac(_) => "Mac",
200 Self::Prk(_) => "Prk",
201 Self::Seed(()) => "Seed",
202 Self::Signing(_) => "Signing",
203 }
204 }
205
206 pub(crate) const fn as_bytes(&self) -> &[u8] {
207 match self {
208 Self::Aead(id)
209 | Self::Decap(id)
210 | Self::Mac(id)
211 | Self::Prk(id)
212 | Self::Signing(id) => id.as_bytes(),
213 Self::Seed(()) => b"64 byte Seed",
214 }
215 }
216}
217
218macro_rules! alg_id_from_impl {
219 ($($name:ident => $ty:ident),* $(,)?) => {
220 $(impl AlgId {
221 #[doc(hidden)]
222 pub const fn $name<CS: CipherSuite>() -> &'static Oid {
224 <CS::$ty as oid::Identified>::OID
225 }
226 })*
227 }
228}
229alg_id_from_impl! {
230 _from_aead => Aead,
231 _from_kem => Kem,
232 _from_mac => Mac,
233 _from_kdf => Kdf,
234 _from_signer => Signer,
235}
236
237#[macro_export]
245macro_rules! unwrapped {
246 { name: $name:ident; type: Aead; into: $into:expr; from: $from:expr $(;)? } => {
247 $crate::__unwrapped_inner!(Aead, $crate::engine::AlgId::_from_aead::<CS>(), $name, $into, $from);
248 };
249 { name: $name:ident; type: Decap; into: $into:expr; from: $from:expr $(;)? } => {
250 $crate::__unwrapped_inner!(Decap, $crate::engine::AlgId::_from_kem::<CS>(), $name, $into, $from);
251 };
252 { name: $name:ident; type: Mac; into: $into:expr; from: $from:expr $(;)? } => {
253 $crate::__unwrapped_inner!(Mac, $crate::engine::AlgId::_from_mac::<CS>(), $name, $into, $from);
254 };
255 { name: $name:ident; type: Prk; into: $into:expr; from: $from:expr $(;)? } => {
256 $crate::__unwrapped_inner!(Prk, $crate::engine::AlgId::_from_kdf::<CS>(), $name, $into, $from);
257 };
258 { name: $name:ident; type: Seed; into: $into:expr; from: $from:expr $(;)? } => {
259 $crate::__unwrapped_inner!(Seed, (), $name, $into, $from);
260 };
261 { name: $name:ident; type: Signing; into: $into:expr; from: $from:expr $(;)? } => {
262 $crate::__unwrapped_inner!(Signing, $crate::engine::AlgId::_from_signer::<CS>(), $name, $into, $from);
263 };
264 ($($fallthrough:tt)*) => {
265 ::core::compile_error!("unknown variant");
266 };
267}
268pub(crate) use unwrapped;
269
270#[doc(hidden)]
271#[macro_export]
272macro_rules! __unwrapped_inner {
273 ($enum:ident, $id:expr, $name:ident, $into:expr, $from:expr) => {
274 impl<CS: $crate::CipherSuite> $crate::engine::UnwrappedKey<CS> for $name<CS> {
275 const ID: $crate::engine::AlgId = $crate::engine::AlgId::$enum($id);
276
277 #[inline]
278 fn into_secret(self) -> $crate::engine::Secret<CS> {
279 $crate::engine::Secret::new($crate::engine::RawSecret::$enum(
280 #[allow(clippy::redundant_closure_call)]
281 $into(self),
282 ))
283 }
284
285 #[inline]
286 fn try_from_secret(
287 key: $crate::engine::UnwrappedSecret<CS>,
288 ) -> ::core::result::Result<Self, $crate::engine::WrongKeyType> {
289 match key.into_raw() {
290 $crate::engine::RawSecret::$enum(key) => ::core::result::Result::Ok(
291 #[allow(clippy::redundant_closure_call)]
292 $from(key),
293 ),
294 got => ::core::result::Result::Err($crate::engine::WrongKeyType {
295 got: got.name(),
296 expected: ::core::stringify!($name),
297 }),
298 }
299 }
300 }
301 };
302}
303
304#[derive(Copy, Clone, Debug, Eq, PartialEq, thiserror::Error)]
307#[error("wrong key type: got {got}, expected {expected}")]
308pub struct WrongKeyType {
309 pub got: &'static str,
311 pub expected: &'static str,
313}
314
315#[derive(Debug, Eq, PartialEq, thiserror::Error)]
317pub enum WrapError {
318 #[error("unable to wrap key: {0}")]
320 Other(&'static str),
321 #[error("unable to wrap key: {0}")]
323 Export(#[from] ExportError),
324 #[error("unable to wrap key: {0}")]
326 Seal(#[from] SealError),
327 #[error("unable to wrap key: {0}")]
329 Bug(#[from] Bug),
330 #[error("unable to wrap key: {0}")]
332 Id(#[from] IdError),
333}
334
335impl From<Infallible> for WrapError {
336 fn from(err: Infallible) -> Self {
337 match err {}
338 }
339}
340
341#[derive(Debug, Eq, PartialEq, thiserror::Error)]
343pub enum UnwrapError {
344 #[error("unable to unwrap key: {0}")]
346 Other(&'static str),
347 #[error("unable to unwrap key: {0}")]
349 Open(#[from] OpenError),
350 #[error("unable to unwrap key: {0}")]
352 Import(#[from] ImportError),
353 #[error("unable to unwrap key: {0}")]
355 WrongKeyType(#[from] WrongKeyType),
356 #[error("unable to unwrap key: {0}")]
358 Bug(#[from] Bug),
359}
360
361impl From<Infallible> for UnwrapError {
362 fn from(err: Infallible) -> Self {
363 match err {}
364 }
365}