use core::fmt;
use libcrux_ml_kem::mlkem768::portable;
use libcrux_ml_kem::mlkem768::{MlKem768Ciphertext, MlKem768PrivateKey, MlKem768PublicKey};
use crate::error::{Error, KeyFormatError};
use crate::low;
use crate::mid::rng::{RandomSource, SystemRandom};
pub(crate) struct DecapKey {
dk: MlKem768PrivateKey,
ek: EncapKey,
}
impl DecapKey {
pub(crate) fn generate() -> Result<Self, Error> {
let _entry = low::Entry::new_secret();
let mut seed = [0u8; 64];
SystemRandom.fill(&mut seed)?;
Ok(Self::keygen_internal(&seed))
}
pub(crate) fn decaps(self, c: &Ciphertext) -> SharedSecret {
let _entry = low::Entry::new_secret();
self.decaps_internal(c)
}
pub(crate) fn encapsulation_key(&self) -> EncapKey {
let _entry = low::Entry::new_public();
self.ek.clone()
}
#[cfg(test)]
pub(crate) fn as_bytes(&self) -> [u8; 2400] {
let _entry = low::Entry::new_secret();
*self.dk.as_slice()
}
pub(crate) fn keygen_internal(seed: &[u8; 64]) -> Self {
let (dk, ek) = portable::generate_key_pair(*seed).into_parts();
low::ct::secret_slice(&dk.as_slice()[..1152]);
low::ct::secret_slice(&dk.as_slice()[2368..]);
Self {
dk,
ek: EncapKey(ek),
}
}
fn decaps_internal(&self, c: &Ciphertext) -> SharedSecret {
let c = MlKem768Ciphertext::from(&c.0);
SharedSecret(portable::decapsulate(&self.dk, &c))
}
}
impl Drop for DecapKey {
fn drop(&mut self) {
low::zeroise(&mut self.dk[0..]);
low::zeroise(&mut self.ek.0[0..]);
}
}
#[derive(Clone)]
pub(crate) struct EncapKey(MlKem768PublicKey);
impl EncapKey {
pub(crate) fn from_bytes(input: &[u8; 1184]) -> Result<Self, Error> {
let _entry = low::Entry::new_public();
let ek = MlKem768PublicKey::from(input);
if !portable::validate_public_key(&ek) {
return Err(KeyFormatError::MlKemModulus.into());
}
Ok(Self(ek))
}
pub(crate) fn encaps(&self) -> Result<(SharedSecret, Ciphertext), Error> {
let _entry = low::Entry::new_secret();
let mut m = Message([0; 32]);
SystemRandom.fill(&mut m.0)?;
Ok(self.encaps_internal(m))
}
pub(crate) fn as_bytes(&self) -> [u8; 1184] {
let _entry = low::Entry::new_public();
*self.0.as_slice()
}
pub(crate) fn encaps_internal(&self, m: Message) -> (SharedSecret, Ciphertext) {
low::ct::secret_slice(&m.0);
let (c, k) = portable::encapsulate(&self.0, m.0);
(
SharedSecret(k),
Ciphertext(low::ct::into_public(*c.as_slice())),
)
}
}
#[derive(Debug, Clone)]
pub(crate) struct Ciphertext([u8; 1088]);
impl From<[u8; 1088]> for Ciphertext {
fn from(value: [u8; 1088]) -> Self {
Self(value)
}
}
impl AsRef<[u8; 1088]> for Ciphertext {
fn as_ref(&self) -> &[u8; 1088] {
&self.0
}
}
pub(crate) struct Message(pub(crate) [u8; 32]);
pub(crate) struct SharedSecret([u8; 32]);
impl AsRef<[u8; 32]> for SharedSecret {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Debug for SharedSecret {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("SharedSecret").finish_non_exhaustive()
}
}
impl Drop for SharedSecret {
fn drop(&mut self) {
low::zeroise(&mut self.0);
}
}