#![forbid(unsafe_code)]
use core::{
convert::Infallible,
fmt::{self, Debug, Display},
hash::Hash,
result::Result,
};
use aranya_buggy::Bug;
use postcard::experimental::max_size::MaxSize;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{
aead::{Aead, AeadId, OpenError, SealError},
ciphersuite::CipherSuite,
csprng::Csprng,
id::{IdError, Identified},
import::{ExportError, ImportError},
kdf::{Kdf, KdfId, Prk},
kem::{Kem, KemId},
mac::{Mac, MacId},
signer::{Signer, SignerId},
};
pub trait Engine: Csprng + RawSecretWrap<Self> + Sized {
type CS: CipherSuite;
type WrappedKey: WrappedKey;
fn wrap<T>(&mut self, key: T) -> Result<Self::WrappedKey, WrapError>
where
T: UnwrappedKey<Self::CS>,
{
let id = key.id()?;
let secret = key.into_secret();
self.wrap_secret::<T>(&id, secret.0)
}
fn unwrap<T>(&self, key: &Self::WrappedKey) -> Result<T, UnwrapError>
where
T: UnwrappedKey<Self::CS>,
{
let secret = self.unwrap_secret::<T>(key)?;
Ok(T::try_from_secret(UnwrappedSecret(secret))?)
}
fn destroy(self) {}
}
pub trait WrappedKey: Identified + Serialize + DeserializeOwned + Sized {}
pub trait UnwrappedKey<CS: CipherSuite>: Sized + Identified {
const ID: AlgId;
fn into_secret(self) -> Secret<CS>;
fn try_from_secret(key: UnwrappedSecret<CS>) -> Result<Self, WrongKeyType>;
}
pub struct Secret<CS: CipherSuite>(RawSecret<CS>);
impl<CS: CipherSuite> Secret<CS> {
pub const fn new(secret: RawSecret<CS>) -> Self {
Self(secret)
}
}
pub struct UnwrappedSecret<CS: CipherSuite>(RawSecret<CS>);
impl<CS: CipherSuite> UnwrappedSecret<CS> {
pub fn into_raw(self) -> RawSecret<CS> {
self.0
}
}
pub trait RawSecretWrap<E: Engine> {
fn wrap_secret<T>(
&mut self,
id: &<T as Identified>::Id,
secret: RawSecret<E::CS>,
) -> Result<E::WrappedKey, WrapError>
where
T: UnwrappedKey<E::CS>;
fn unwrap_secret<T>(&self, key: &E::WrappedKey) -> Result<RawSecret<E::CS>, UnwrapError>
where
T: UnwrappedKey<E::CS>;
}
pub enum RawSecret<CS: CipherSuite> {
Aead(<CS::Aead as Aead>::Key),
Decap(<CS::Kem as Kem>::DecapKey),
Mac(<CS::Mac as Mac>::Key),
Prk(Prk<<CS::Kdf as Kdf>::PrkSize>),
Seed([u8; 64]),
Signing(<CS::Signer as Signer>::SigningKey),
}
impl<CS: CipherSuite> RawSecret<CS> {
pub const fn name(&self) -> &'static str {
self.alg_id().name()
}
pub const fn alg_id(&self) -> AlgId {
match self {
Self::Aead(_) => AlgId::Aead(<CS::Aead as Aead>::ID),
Self::Decap(_) => AlgId::Decap(<CS::Kem as Kem>::ID),
Self::Mac(_) => AlgId::Mac(<CS::Mac as Mac>::ID),
Self::Prk(_) => AlgId::Prk(<CS::Kdf as Kdf>::ID),
Self::Seed(_) => AlgId::Seed(()),
Self::Signing(_) => AlgId::Signing(<CS::Signer as Signer>::ID),
}
}
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, MaxSize)]
pub enum AlgId {
Aead(AeadId),
Decap(KemId),
Mac(MacId),
Prk(KdfId),
Seed(()),
Signing(SignerId),
}
impl AlgId {
#[inline]
pub const fn name(&self) -> &'static str {
match self {
Self::Aead(_) => "Aead",
Self::Decap(_) => "Decap",
Self::Mac(_) => "Mac",
Self::Prk(_) => "Prk",
Self::Seed(_) => "Seed",
Self::Signing(_) => "Signing",
}
}
}
#[macro_export]
macro_rules! unwrapped {
{ name: $name:ident; type: Aead; into: $into:expr; from: $from:expr $(;)? } => {
$crate::engine::__unwrapped_inner!(Aead, <CS::Aead as $crate::aead::Aead>::ID, $name, $into, $from);
};
{ name: $name:ident; type: Decap; into: $into:expr; from: $from:expr $(;)? } => {
$crate::engine::__unwrapped_inner!(Decap, <CS::Kem as $crate::kem::Kem>::ID, $name, $into, $from);
};
{ name: $name:ident; type: Mac; into: $into:expr; from: $from:expr $(;)? } => {
$crate::engine::__unwrapped_inner!(Mac, <CS::Mac as $crate::mac::Mac>::ID, $name, $into, $from);
};
{ name: $name:ident; type: Prk; into: $into:expr; from: $from:expr $(;)? } => {
$crate::engine::__unwrapped_inner!(Prk, <CS::Kdf as $crate::kdf::Kdf>::ID, $name, $into, $from);
};
{ name: $name:ident; type: Seed; into: $into:expr; from: $from:expr $(;)? } => {
$crate::engine::__unwrapped_inner!(Seed, (), $name, $into, $from);
};
{ name: $name:ident; type: Signing; into: $into:expr; from: $from:expr $(;)? } => {
$crate::engine::__unwrapped_inner!(Signing, <CS::Signer as $crate::signer::Signer>::ID, $name, $into, $from);
};
($($fallthrough:tt)*) => {
::core::compile_error!("unknown variant");
};
}
pub(crate) use unwrapped;
#[doc(hidden)]
macro_rules! __unwrapped_inner {
($enum:ident, $id:expr, $name:ident, $into:expr, $from:expr) => {
impl<CS: $crate::CipherSuite> $crate::engine::UnwrappedKey<CS> for $name<CS> {
const ID: $crate::engine::AlgId = $crate::engine::AlgId::$enum($id);
#[inline]
fn into_secret(self) -> $crate::engine::Secret<CS> {
$crate::engine::Secret::new($crate::engine::RawSecret::$enum(
#[allow(clippy::redundant_closure_call)]
$into(self),
))
}
#[inline]
fn try_from_secret(
key: $crate::engine::UnwrappedSecret<CS>,
) -> ::core::result::Result<Self, $crate::engine::WrongKeyType> {
match key.into_raw() {
$crate::engine::RawSecret::$enum(key) => ::core::result::Result::Ok(
#[allow(clippy::redundant_closure_call)]
$from(key),
),
got => ::core::result::Result::Err($crate::engine::WrongKeyType {
got: got.name(),
expected: ::core::stringify!($name),
}),
}
}
}
};
}
pub(crate) use __unwrapped_inner;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct WrongKeyType {
pub got: &'static str,
pub expected: &'static str,
}
impl Display for WrongKeyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"wrong key type: got {}, expected {}",
self.got, self.expected
)
}
}
impl core::error::Error for WrongKeyType {}
#[derive(Debug, Eq, PartialEq)]
pub enum WrapError {
Other(&'static str),
Export(ExportError),
Seal(SealError),
Bug(Bug),
Id(IdError),
}
impl Display for WrapError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unable to wrap key: ")?;
match self {
Self::Other(msg) => write!(f, "{}", msg),
Self::Export(err) => write!(f, "{}", err),
Self::Seal(err) => write!(f, "{}", err),
Self::Bug(err) => write!(f, "{}", err),
Self::Id(err) => write!(f, "{}", err),
}
}
}
impl core::error::Error for WrapError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Export(err) => Some(err),
Self::Seal(err) => Some(err),
Self::Bug(err) => Some(err),
_ => None,
}
}
}
impl From<SealError> for WrapError {
fn from(err: SealError) -> Self {
Self::Seal(err)
}
}
impl From<ExportError> for WrapError {
fn from(err: ExportError) -> Self {
Self::Export(err)
}
}
impl From<IdError> for WrapError {
fn from(err: IdError) -> Self {
Self::Id(err)
}
}
impl From<Bug> for WrapError {
fn from(err: Bug) -> Self {
Self::Bug(err)
}
}
impl From<Infallible> for WrapError {
fn from(err: Infallible) -> Self {
match err {}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum UnwrapError {
Other(&'static str),
Open(OpenError),
Import(ImportError),
WrongKeyType(WrongKeyType),
Bug(Bug),
}
impl Display for UnwrapError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unable to unwrap key: ")?;
match self {
Self::Other(msg) => write!(f, "{}", msg),
Self::Open(err) => write!(f, "{}", err),
Self::Import(err) => write!(f, "{}", err),
Self::WrongKeyType(err) => write!(f, "{}", err),
Self::Bug(err) => write!(f, "{}", err),
}
}
}
impl core::error::Error for UnwrapError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Import(err) => Some(err),
_ => None,
}
}
}
impl From<OpenError> for UnwrapError {
fn from(err: OpenError) -> Self {
Self::Open(err)
}
}
impl From<ImportError> for UnwrapError {
fn from(err: ImportError) -> Self {
Self::Import(err)
}
}
impl From<WrongKeyType> for UnwrapError {
fn from(err: WrongKeyType) -> Self {
Self::WrongKeyType(err)
}
}
impl From<Bug> for UnwrapError {
fn from(err: Bug) -> Self {
Self::Bug(err)
}
}
impl From<Infallible> for UnwrapError {
fn from(err: Infallible) -> Self {
match err {}
}
}