#![forbid(unsafe_code)]
use core::{
array::TryFromSliceError,
borrow::Borrow,
fmt::{self, Debug, Display},
marker::PhantomData,
result::Result,
};
pub use crate::hpke::KemId;
use crate::{
csprng::Csprng,
import::{Import, ImportError},
kdf::{Kdf, KdfError, Prk},
keys::{PublicKey, SecretKey},
signer::PkError,
zeroize::ZeroizeOnDrop,
};
#[derive(Debug, Eq, PartialEq)]
pub enum KemError {
InvalidDecapKeyFormat,
InvalidEncapKeyFormat,
Encap,
Decapsulation,
DhKem(DhKemError),
Import(ImportError),
PublicKey(PkError),
}
impl Display for KemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidDecapKeyFormat => write!(f, "invalid secret key data"),
Self::InvalidEncapKeyFormat => write!(f, "invalid public key data"),
Self::Encap => write!(f, "encapsulation failed"),
Self::Decapsulation => write!(f, "unable to decapsulate symmetric key"),
Self::DhKem(err) => write!(f, "{}", err),
Self::Import(err) => write!(f, "{}", err),
Self::PublicKey(err) => write!(f, "{}", err),
}
}
}
impl core::error::Error for KemError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::DhKem(err) => Some(err),
Self::Import(err) => Some(err),
_ => None,
}
}
}
impl From<DhKemError> for KemError {
fn from(err: DhKemError) -> Self {
Self::DhKem(err)
}
}
impl From<ImportError> for KemError {
fn from(err: ImportError) -> Self {
Self::Import(err)
}
}
impl From<PkError> for KemError {
fn from(err: PkError) -> Self {
Self::PublicKey(err)
}
}
#[allow(non_snake_case)]
pub trait Kem {
const ID: KemId;
type DecapKey: DecapKey<EncapKey = Self::EncapKey>;
type EncapKey: EncapKey;
type Secret: AsRef<[u8]> + ZeroizeOnDrop;
type Encap: Borrow<[u8]> + for<'a> Import<&'a [u8]>;
fn encap<R: Csprng>(
rng: &mut R,
pkR: &Self::EncapKey,
) -> Result<(Self::Secret, Self::Encap), KemError>;
fn encap_deterministically(
pkR: &Self::EncapKey,
skE: Self::DecapKey,
) -> Result<(Self::Secret, Self::Encap), KemError>;
fn decap(enc: &Self::Encap, skR: &Self::DecapKey) -> Result<Self::Secret, KemError>;
fn auth_encap<R: Csprng>(
rng: &mut R,
pkR: &Self::EncapKey,
skS: &Self::DecapKey,
) -> Result<(Self::Secret, Self::Encap), KemError>;
fn auth_encap_deterministically(
pkR: &Self::EncapKey,
skS: &Self::DecapKey,
skE: Self::DecapKey,
) -> Result<(Self::Secret, Self::Encap), KemError>;
fn auth_decap(
enc: &Self::Encap,
skR: &Self::DecapKey,
pkS: &Self::EncapKey,
) -> Result<Self::Secret, KemError>;
}
pub trait DecapKey: SecretKey {
type EncapKey: EncapKey;
fn public(&self) -> Result<Self::EncapKey, PkError>;
}
pub trait EncapKey: PublicKey {}
#[derive(Debug, Eq, PartialEq)]
pub enum EcdhError {
Other(&'static str),
}
impl Display for EcdhError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Other(msg) => write!(f, "{}", msg),
}
}
}
impl core::error::Error for EcdhError {}
pub trait Ecdh {
const SCALAR_SIZE: usize;
type PrivateKey: DecapKey<EncapKey = Self::PublicKey>;
type PublicKey: EncapKey;
type SharedSecret: Borrow<[u8]> + ZeroizeOnDrop;
fn ecdh(
local: &Self::PrivateKey,
remote: &Self::PublicKey,
) -> Result<Self::SharedSecret, EcdhError>;
}
#[derive(ZeroizeOnDrop)]
pub struct SharedSecret<const N: usize>([u8; N]);
impl<const N: usize> SharedSecret<N> {
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.0.as_mut_ptr()
}
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
self.0.len()
}
}
impl<const N: usize> Default for SharedSecret<N> {
fn default() -> Self {
Self([0u8; N])
}
}
impl<const N: usize> Borrow<[u8]> for SharedSecret<N> {
fn borrow(&self) -> &[u8] {
&self.0
}
}
impl<const N: usize> TryFrom<&[u8]> for SharedSecret<N> {
type Error = TryFromSliceError;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(data.try_into()?))
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum DhKemError {
Ecdh(EcdhError),
Kdf(KdfError),
Import(ImportError),
}
impl Display for DhKemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Ecdh(err) => write!(f, "{}", err),
Self::Kdf(err) => write!(f, "{}", err),
Self::Import(err) => write!(f, "{}", err),
}
}
}
impl core::error::Error for DhKemError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Ecdh(err) => Some(err),
Self::Kdf(err) => Some(err),
Self::Import(err) => Some(err),
}
}
}
pub struct DhKem<E, F> {
id: KemId,
_e: PhantomData<E>,
_f: PhantomData<F>,
}
#[allow(non_snake_case)]
impl<E: Ecdh, F: Kdf> DhKem<E, F> {
pub fn new(id: KemId) -> Self {
Self {
id,
_e: PhantomData,
_f: PhantomData,
}
}
pub fn encap<R: Csprng>(
&self,
rng: &mut R,
pkR: &E::PublicKey,
) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
let skE = E::PrivateKey::new(rng);
self.encap_deterministically(pkR, skE)
}
pub fn encap_deterministically(
&self,
pkR: &E::PublicKey,
skE: E::PrivateKey,
) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
let pkE = skE.public()?;
let dh = (E::ecdh(&skE, pkR).map_err(DhKemError::Ecdh)?, None);
let enc = pkE.export();
let pkRm = pkR.export();
let shared_secret =
Self::extract_and_expand(&dh, &enc, &pkRm, None, self.id).map_err(DhKemError::Kdf)?;
Ok((shared_secret, enc))
}
pub fn decap(
&self,
enc: &PubKeyData<E>,
skR: &E::PrivateKey,
) -> Result<Prk<F::PrkSize>, KemError> {
let pkE = E::PublicKey::import(enc.borrow())?;
let dh = (E::ecdh(skR, &pkE).map_err(DhKemError::Ecdh)?, None);
let pkRm = skR.public()?.export();
let shared_secret =
Self::extract_and_expand(&dh, enc, &pkRm, None, self.id).map_err(DhKemError::Kdf)?;
Ok(shared_secret)
}
pub fn auth_encap<R: Csprng>(
&self,
rng: &mut R,
pkR: &E::PublicKey,
skS: &E::PrivateKey,
) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
let skE = E::PrivateKey::new(rng);
self.auth_encap_deterministically(pkR, skS, skE)
}
pub fn auth_encap_deterministically(
&self,
pkR: &E::PublicKey,
skS: &E::PrivateKey,
skE: E::PrivateKey,
) -> Result<(Prk<F::PrkSize>, PubKeyData<E>), KemError> {
let pkE = skE.public()?;
let dh = (
E::ecdh(&skE, pkR).map_err(DhKemError::Ecdh)?,
Some(E::ecdh(skS, pkR).map_err(DhKemError::Ecdh)?),
);
let enc = pkE.export();
let pkRm = pkR.export();
let pkSm = skS.public()?.export();
let shared_secret = Self::extract_and_expand(&dh, &enc, &pkRm, Some(&pkSm), self.id)
.map_err(DhKemError::Kdf)?;
Ok((shared_secret, enc))
}
pub fn auth_decap(
&self,
enc: &PubKeyData<E>,
skR: &E::PrivateKey,
pkS: &E::PublicKey,
) -> Result<Prk<F::PrkSize>, KemError> {
let pkE = E::PublicKey::import(enc.borrow())?;
let dh = (
E::ecdh(skR, &pkE).map_err(DhKemError::Ecdh)?,
Some(E::ecdh(skR, pkS).map_err(DhKemError::Ecdh)?),
);
let pkRm = skR.public()?.export();
let pkSm = pkS.export();
let shared_secret = Self::extract_and_expand(&dh, enc, &pkRm, Some(&pkSm), self.id)
.map_err(DhKemError::Kdf)?;
Ok(shared_secret)
}
fn extract_and_expand(
dh: &(E::SharedSecret, Option<E::SharedSecret>),
enc: &PubKeyData<E>,
pkRm: &PubKeyData<E>,
pkSm: Option<&PubKeyData<E>>,
id: KemId,
) -> Result<Prk<F::PrkSize>, KdfError> {
let mut out = Prk::<F::PrkSize>::default();
let labeled_ikm = [
"HPKE-v1".as_bytes(),
"KEM".as_bytes(),
&id.to_be_bytes(),
"eae_prk".as_bytes(),
dh.0.borrow(),
dh.1.as_ref().map_or(&[], |v| v.borrow()),
];
let labeled_info = [
&(F::PRK_SIZE as u16).to_be_bytes()[..],
"HPKE-v1".as_bytes(),
"KEM".as_bytes(),
&id.to_be_bytes(),
"shared_secret".as_bytes(),
enc.borrow(),
pkRm.borrow(),
pkSm.map_or(&[], |v| v.borrow()),
];
F::extract_and_expand_multi(out.as_bytes_mut(), &labeled_ikm, &[], &labeled_info)?;
Ok(out)
}
}
type PubKeyData<T> = <<T as Ecdh>::PublicKey as PublicKey>::Data;
#[cfg_attr(feature = "hazmat", macro_export)]
#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
macro_rules! dhkem_impl {
($name:ident, $doc:expr, $ecdh:ty, $kdf:ty, $sk:ident, $pk:ident $(,)?) => {
#[doc = concat!($doc, ".")]
#[derive(Debug)]
pub struct $name;
#[allow(non_snake_case)]
impl $crate::kem::Kem for $name {
const ID: $crate::kem::KemId = $crate::kem::KemId::$name;
type DecapKey = $sk;
type EncapKey = $pk;
type Secret = $crate::kdf::Prk<<$kdf as $crate::kdf::Kdf>::PrkSize>;
type Encap = <$pk as $crate::keys::PublicKey>::Data;
fn encap<R: $crate::csprng::Csprng>(
rng: &mut R,
pkR: &Self::EncapKey,
) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
$crate::kem::DhKem::<$ecdh, $kdf>::new(Self::ID).encap(rng, pkR)
}
fn encap_deterministically(
pkR: &Self::EncapKey,
skE: Self::DecapKey,
) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
$crate::kem::DhKem::<$ecdh, $kdf>::new(Self::ID).encap_deterministically(pkR, skE)
}
fn decap(
enc: &Self::Encap,
skR: &Self::DecapKey,
) -> ::core::result::Result<Self::Secret, $crate::kem::KemError> {
$crate::kem::DhKem::<$ecdh, $kdf>::new(Self::ID).decap(enc, skR)
}
fn auth_encap<R: $crate::csprng::Csprng>(
rng: &mut R,
pkR: &Self::EncapKey,
skS: &Self::DecapKey,
) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
$crate::kem::DhKem::<$ecdh, $kdf>::new(Self::ID).auth_encap(rng, pkR, skS)
}
fn auth_encap_deterministically(
pkR: &Self::EncapKey,
skS: &Self::DecapKey,
skE: Self::DecapKey,
) -> ::core::result::Result<(Self::Secret, Self::Encap), $crate::kem::KemError> {
$crate::kem::DhKem::<$ecdh, $kdf>::new(Self::ID)
.auth_encap_deterministically(pkR, skS, skE)
}
fn auth_decap(
enc: &Self::Encap,
skR: &Self::DecapKey,
pkS: &Self::EncapKey,
) -> ::core::result::Result<Self::Secret, $crate::kem::KemError> {
$crate::kem::DhKem::<$ecdh, $kdf>::new(Self::ID).auth_decap(enc, skR, pkS)
}
}
};
}
pub(crate) use dhkem_impl;