use crate::{DecapsulationKey, EncapsulationKey, Error, HpkeKemId};
use ctutils::CtEq;
use kem::{
Decapsulator, Encapsulate, Generate, InvalidKey, Kem, Key, KeyExport, KeyInit, KeySizeUser,
TryDecapsulate, TryKeyInit,
common::array::{Array, sizes::U32},
};
use rand_core::{CryptoRng, TryCryptoRng};
use x25519::{PublicKey, StaticSecret};
#[cfg(doc)]
use crate::Expander;
type Ciphertext = Array<u8, U32>;
type SharedKey = Array<u8, U32>;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
pub struct X25519Kem;
impl Kem for X25519Kem {
type DecapsulationKey = X25519DecapsulationKey;
type EncapsulationKey = X25519EncapsulationKey;
type CiphertextSize = U32;
type SharedKeySize = U32;
}
impl HpkeKemId for X25519Kem {
const KEM_ID: u16 = 0x20;
}
pub type X25519DecapsulationKey = DecapsulationKey<StaticSecret, PublicKey>;
impl Decapsulator for X25519DecapsulationKey {
type Kem = X25519Kem;
fn encapsulation_key(&self) -> &X25519EncapsulationKey {
&self.ek
}
}
impl KeySizeUser for X25519DecapsulationKey {
type KeySize = U32;
}
impl KeyInit for X25519DecapsulationKey {
fn new(key: &Key<Self>) -> Self {
StaticSecret::from(key.0).into()
}
}
impl KeyExport for X25519DecapsulationKey {
fn to_bytes(&self) -> Key<Self> {
self.dk.to_bytes().into()
}
}
impl Generate for X25519DecapsulationKey {
fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
let key = Key::<Self>::try_generate_from_rng(rng)?;
Ok(StaticSecret::from(key.0).into())
}
}
impl TryDecapsulate for X25519DecapsulationKey {
type Error = Error;
#[inline]
fn try_decapsulate(&self, encapsulated_key: &Ciphertext) -> Result<SharedKey, Error> {
let public_key = PublicKey::from(encapsulated_key.0);
let sk = self.dk.diffie_hellman(&public_key).to_bytes();
if sk.ct_eq(&[0u8; 32]).into() {
return Err(Error::Decapsulation);
}
Ok(sk.into())
}
}
pub type X25519EncapsulationKey = EncapsulationKey<PublicKey>;
impl KeySizeUser for X25519EncapsulationKey {
type KeySize = U32;
}
impl TryKeyInit for X25519EncapsulationKey {
fn new(encapsulation_key: &Key<Self>) -> Result<Self, InvalidKey> {
Ok(Self(PublicKey::from(encapsulation_key.0)))
}
}
impl KeyExport for X25519EncapsulationKey {
fn to_bytes(&self) -> Key<Self> {
self.0.to_bytes().into()
}
}
impl Encapsulate for X25519EncapsulationKey {
type Kem = X25519Kem;
fn encapsulate_with_rng<R>(&self, rng: &mut R) -> (Ciphertext, SharedKey)
where
R: CryptoRng + ?Sized,
{
let sk = StaticSecret::random_from_rng(rng);
let pk = PublicKey::from(&sk);
let ss = sk.diffie_hellman(&self.0);
(pk.to_bytes().into(), ss.to_bytes().into())
}
}