use super::MlKemError;
use super::encode;
use super::kpke;
use super::ntt;
use super::params::N;
use super::params::Params;
use super::rng::CryptoRng;
use super::sha3;
use alloc::vec::Vec;
const MAX_K: usize = 4;
const MAX_EK_LEN: usize = 384 * MAX_K + 32;
const MAX_CT_LEN: usize = 1568;
const MAX_J_INPUT_LEN: usize = 32 + MAX_CT_LEN;
pub fn keygen_internal<P: Params>(d: &[u8; 32], z: &[u8; 32]) -> (Vec<u8>, Vec<u8>) {
let k = P::K;
let ek_len = 384 * k + 32;
let dk_pke_len = 384 * k;
let mut ek_buf = [0u8; MAX_EK_LEN];
let mut dk_pke_buf = [0u8; 384 * MAX_K];
kpke::keygen::<P>(d, &mut ek_buf, &mut dk_pke_buf);
let ek = &ek_buf[..ek_len];
let h_ek = sha3::h(ek);
let mut dk = Vec::with_capacity(P::DK_LEN);
dk.extend_from_slice(&dk_pke_buf[..dk_pke_len]);
dk.extend_from_slice(ek);
dk.extend_from_slice(&h_ek);
dk.extend_from_slice(z);
(ek[..ek_len].to_vec(), dk)
}
#[cfg(feature = "sca-protected")]
fn keygen_internal_sca<P: Params>(
d: &[u8; 32],
z: &[u8; 32],
rng: &mut impl CryptoRng,
) -> Result<(Vec<u8>, Vec<u8>), MlKemError> {
let k = P::K;
let ek_len = 384 * k + 32;
let dk_pke_len = 384 * k;
let mut ek_buf = [0u8; MAX_EK_LEN];
let mut dk_pke_buf = [0u8; 384 * MAX_K];
kpke::keygen_sca::<P>(d, &mut ek_buf, &mut dk_pke_buf, rng)?;
let ek = &ek_buf[..ek_len];
let h_ek = sha3::h(ek);
let mut dk = Vec::with_capacity(P::DK_LEN);
dk.extend_from_slice(&dk_pke_buf[..dk_pke_len]);
dk.extend_from_slice(ek);
dk.extend_from_slice(&h_ek);
dk.extend_from_slice(z);
Ok((ek[..ek_len].to_vec(), dk))
}
pub fn encaps_internal<P: Params>(ek: &[u8], m: &[u8; 32]) -> ([u8; 32], Vec<u8>) {
let h_ek = sha3::h(ek);
let mut g_input = [0u8; 64];
g_input[..32].copy_from_slice(m);
g_input[32..64].copy_from_slice(&h_ek);
let (shared_key, r) = sha3::g(&g_input);
ntt::zeroize_bytes(&mut g_input);
let mut ct_buf = [0u8; MAX_CT_LEN];
let ct_len = kpke::encrypt::<P>(ek, m, &r, &mut ct_buf);
(shared_key, ct_buf[..ct_len].to_vec())
}
pub fn decaps_internal<P: Params>(dk: &[u8], c: &[u8]) -> [u8; 32] {
let k = P::K;
let dk_pke = &dk[0..384 * k];
let ek_pke = &dk[384 * k..768 * k + 32];
let h = &dk[768 * k + 32..768 * k + 64];
let z = &dk[768 * k + 64..768 * k + 96];
let mut m_prime = kpke::decrypt::<P>(dk_pke, c);
let mut g_input = [0u8; 64];
g_input[..32].copy_from_slice(&m_prime);
g_input[32..64].copy_from_slice(h);
let (k_prime, r_prime) = sha3::g(&g_input);
ntt::zeroize_bytes(&mut g_input);
let j_input_len = 32 + c.len();
let mut j_input = [0u8; MAX_J_INPUT_LEN];
j_input[..32].copy_from_slice(z);
j_input[32..j_input_len].copy_from_slice(c);
let k_bar = sha3::j(&j_input[..j_input_len]);
ntt::zeroize_bytes(&mut j_input[..j_input_len]);
let mut c_prime = [0u8; MAX_CT_LEN];
let ct_len = kpke::encrypt::<P>(ek_pke, &m_prime, &r_prime, &mut c_prime);
let eq = ct_eq(c, &c_prime[..ct_len]);
let mut result = [0u8; 32];
ct_select(&mut result, &k_prime, &k_bar, eq);
ntt::zeroize_bytes(&mut m_prime);
result
}
#[cfg(feature = "sca-protected")]
fn decaps_internal_sca<P: Params>(dk: &[u8], c: &[u8], rng: &mut impl CryptoRng) -> Result<[u8; 32], MlKemError> {
let k = P::K;
let dk_pke = &dk[0..384 * k];
let ek_pke = &dk[384 * k..768 * k + 32];
let h = &dk[768 * k + 32..768 * k + 64];
let z = &dk[768 * k + 64..768 * k + 96];
let mut m_prime = kpke::decrypt_sca::<P>(dk_pke, c, rng)?;
let mut g_input = [0u8; 64];
g_input[..32].copy_from_slice(&m_prime);
g_input[32..64].copy_from_slice(h);
let (k_prime, r_prime) = sha3::g(&g_input);
ntt::zeroize_bytes(&mut g_input);
let j_input_len = 32 + c.len();
let mut j_input = [0u8; MAX_J_INPUT_LEN];
j_input[..32].copy_from_slice(z);
j_input[32..j_input_len].copy_from_slice(c);
let k_bar = sha3::j(&j_input[..j_input_len]);
ntt::zeroize_bytes(&mut j_input[..j_input_len]);
let mut c_prime = [0u8; MAX_CT_LEN];
let ct_len = kpke::encrypt::<P>(ek_pke, &m_prime, &r_prime, &mut c_prime);
let eq = ct_eq(c, &c_prime[..ct_len]);
let mut result = [0u8; 32];
ct_select(&mut result, &k_prime, &k_bar, eq);
ntt::zeroize_bytes(&mut m_prime);
Ok(result)
}
pub fn keygen<P: Params>(rng: &mut impl CryptoRng) -> Result<(Vec<u8>, Vec<u8>), MlKemError> {
let mut d = [0u8; 32];
let mut z = [0u8; 32];
rng.fill_bytes(&mut d)?;
rng.fill_bytes(&mut z)?;
#[cfg(feature = "sca-protected")]
let result = keygen_internal_sca::<P>(&d, &z, rng)?;
#[cfg(not(feature = "sca-protected"))]
let result = keygen_internal::<P>(&d, &z);
ntt::zeroize_bytes(&mut d);
ntt::zeroize_bytes(&mut z);
Ok(result)
}
pub fn encaps<P: Params>(ek: &[u8], rng: &mut impl CryptoRng) -> Result<([u8; 32], Vec<u8>), MlKemError> {
if ek.len() != P::EK_LEN {
return Err(MlKemError::InvalidEncapsulationKey);
}
let k = P::K;
for i in 0..k {
let slice = &ek[384 * i..384 * (i + 1)];
let mut decoded = [0u16; N];
encode::byte_decode(12, slice, &mut decoded);
let mut reencoded = [0u8; 384];
encode::byte_encode(12, &decoded, &mut reencoded);
if !ct_eq(slice, &reencoded) {
return Err(MlKemError::InvalidEncapsulationKey);
}
}
let mut m = [0u8; 32];
rng.fill_bytes(&mut m)?;
let (shared_key, ct) = encaps_internal::<P>(ek, &m);
ntt::zeroize_bytes(&mut m);
Ok((shared_key, ct))
}
pub fn decaps<P: Params>(dk: &[u8], c: &[u8], _rng: &mut impl CryptoRng) -> Result<[u8; 32], MlKemError> {
let k = P::K;
if c.len() != P::CT_LEN {
return Err(MlKemError::InvalidCiphertext);
}
if dk.len() != P::DK_LEN {
return Err(MlKemError::InvalidDecapsulationKey);
}
let ek_in_dk = &dk[384 * k..768 * k + 32];
let h_stored = &dk[768 * k + 32..768 * k + 64];
let h_computed = sha3::h(ek_in_dk);
if !ct_eq(&h_computed, h_stored) {
return Err(MlKemError::InvalidDecapsulationKey);
}
#[cfg(feature = "sca-protected")]
let k1 = decaps_internal_sca::<P>(dk, c, _rng)?;
#[cfg(feature = "sca-protected")]
let k2 = decaps_internal_sca::<P>(dk, c, _rng)?;
#[cfg(not(feature = "sca-protected"))]
let k1 = decaps_internal::<P>(dk, c);
#[cfg(not(feature = "sca-protected"))]
let k2 = decaps_internal::<P>(dk, c);
let results_match = ct_eq(&k1, &k2);
let z = &dk[768 * k + 64..768 * k + 96];
let mut fault_input = [0u8; 33];
fault_input[..32].copy_from_slice(z);
fault_input[32] = 0xFF; let k_fault = sha3::h(&fault_input);
ntt::zeroize_bytes(&mut fault_input);
let mut out = [0u8; 32];
ct_select(&mut out, &k1, &k_fault, results_match);
Ok(out)
}
pub fn decaps_single<P: Params>(dk: &[u8], c: &[u8]) -> Result<[u8; 32], MlKemError> {
let k = P::K;
if c.len() != P::CT_LEN {
return Err(MlKemError::InvalidCiphertext);
}
if dk.len() != P::DK_LEN {
return Err(MlKemError::InvalidDecapsulationKey);
}
let ek_in_dk = &dk[384 * k..768 * k + 32];
let h_stored = &dk[768 * k + 32..768 * k + 64];
let h_computed = sha3::h(ek_in_dk);
if !ct_eq(&h_computed, h_stored) {
return Err(MlKemError::InvalidDecapsulationKey);
}
Ok(decaps_internal::<P>(dk, c))
}
#[inline(never)]
fn ct_eq(a: &[u8], b: &[u8]) -> bool {
silentops::ct_eq(a, b) == 1
}
#[inline(never)]
fn ct_select(out: &mut [u8; 32], a: &[u8; 32], b: &[u8; 32], condition: bool) {
let cond = condition as u8;
for i in 0..32 {
out[i] = silentops::ct_select_u8(a[i], b[i], cond);
}
}