#[cfg(feature = "sca-protected")]
use super::MlKemError;
use super::encode;
#[cfg(feature = "sca-protected")]
use super::masked::{self, MaskedPoly};
use super::ntt;
use super::params::{N, Params};
#[cfg(feature = "sca-protected")]
use super::rng::CryptoRng;
use super::sample;
use super::sha3;
#[cfg(feature = "sca-protected")]
use super::shuffle;
const MAX_K: usize = 4;
const MAX_PRF_LEN: usize = 192;
pub fn keygen<P: Params>(d: &[u8; 32], ek_out: &mut [u8], dk_out: &mut [u8]) -> (usize, usize) {
let k = P::K;
let ek_len = 384 * k + 32;
let dk_len = 384 * k;
let mut g_input = [0u8; 33];
g_input[..32].copy_from_slice(d);
g_input[32] = k as u8;
let (rho, sigma) = sha3::g(&g_input);
ntt::zeroize_bytes(&mut g_input);
let mut a_hat = [[0i16; N]; MAX_K * MAX_K];
for i in 0..k {
for j in 0..k {
let mut seed = [0u8; 34];
seed[..32].copy_from_slice(&rho);
seed[32] = j as u8;
seed[33] = i as u8;
a_hat[i * k + j] = sample::sample_ntt(&seed);
}
}
let mut n_counter = 0u8;
let mut s_hat = [[0i16; N]; MAX_K];
let mut prf_buf = [0u8; MAX_PRF_LEN];
for i in 0..k {
sha3::prf(P::ETA1, &sigma, n_counter, &mut prf_buf);
s_hat[i] = sample::sample_poly_cbd(P::ETA1, &prf_buf[..64 * P::ETA1]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA1]);
ntt::ntt(&mut s_hat[i]);
n_counter += 1;
}
let mut e_hat = [[0i16; N]; MAX_K];
for i in 0..k {
sha3::prf(P::ETA1, &sigma, n_counter, &mut prf_buf);
e_hat[i] = sample::sample_poly_cbd(P::ETA1, &prf_buf[..64 * P::ETA1]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA1]);
ntt::ntt(&mut e_hat[i]);
n_counter += 1;
}
let mut t_hat = [[0i16; N]; MAX_K];
for i in 0..k {
for j in 0..k {
let mut tmp = [0i16; N];
ntt::multiply_ntts(&a_hat[i * k + j], &s_hat[j], &mut tmp);
for l in 0..N {
t_hat[i][l] = t_hat[i][l] + tmp[l];
}
}
ntt::to_mont_poly(&mut t_hat[i]); for l in 0..N {
t_hat[i][l] = t_hat[i][l] + e_hat[i][l];
}
}
for i in 0..k {
let mut t_u16 = [0u16; N];
for l in 0..N {
t_u16[l] = ntt::barrett_reduce(t_hat[i][l]) as u16;
}
encode::byte_encode(12, &t_u16, &mut ek_out[384 * i..384 * (i + 1)]);
}
ek_out[384 * k..384 * k + 32].copy_from_slice(&rho);
for i in 0..k {
let mut s_u16 = [0u16; N];
for l in 0..N {
s_u16[l] = ntt::barrett_reduce(s_hat[i][l]) as u16;
}
encode::byte_encode(12, &s_u16, &mut dk_out[384 * i..384 * (i + 1)]);
}
for poly in s_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
for poly in e_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
(ek_len, dk_len)
}
#[cfg(feature = "sca-protected")]
pub fn keygen_sca<P: Params>(
d: &[u8; 32],
ek_out: &mut [u8],
dk_out: &mut [u8],
rng: &mut impl CryptoRng,
) -> Result<(usize, usize), MlKemError> {
let k = P::K;
let ek_len = 384 * k + 32;
let dk_len = 384 * k;
let mut g_input = [0u8; 33];
g_input[..32].copy_from_slice(d);
g_input[32] = k as u8;
let (rho, sigma) = sha3::g(&g_input);
ntt::zeroize_bytes(&mut g_input);
let mut a_hat = [[0i16; N]; MAX_K * MAX_K];
for i in 0..k {
for j in 0..k {
let mut seed = [0u8; 34];
seed[..32].copy_from_slice(&rho);
seed[32] = j as u8;
seed[33] = i as u8;
a_hat[i * k + j] = sample::sample_ntt(&seed);
}
}
let mut n_counter = 0u8;
let mut s_hat = [[0i16; N]; MAX_K];
let mut prf_buf = [0u8; MAX_PRF_LEN];
for i in 0..k {
sha3::prf(P::ETA1, &sigma, n_counter, &mut prf_buf);
s_hat[i] = sample::sample_poly_cbd(P::ETA1, &prf_buf[..64 * P::ETA1]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA1]);
shuffle::ntt_shuffled(&mut s_hat[i], rng)?;
n_counter += 1;
}
let mut e_hat = [[0i16; N]; MAX_K];
for i in 0..k {
sha3::prf(P::ETA1, &sigma, n_counter, &mut prf_buf);
e_hat[i] = sample::sample_poly_cbd(P::ETA1, &prf_buf[..64 * P::ETA1]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA1]);
shuffle::ntt_shuffled(&mut e_hat[i], rng)?;
n_counter += 1;
}
let mut t_hat = [[0i16; N]; MAX_K];
for i in 0..k {
for j in 0..k {
let mut tmp = [0i16; N];
ntt::multiply_ntts(&a_hat[i * k + j], &s_hat[j], &mut tmp);
for l in 0..N {
t_hat[i][l] = t_hat[i][l] + tmp[l];
}
}
ntt::to_mont_poly(&mut t_hat[i]);
for l in 0..N {
t_hat[i][l] = t_hat[i][l] + e_hat[i][l];
}
}
for i in 0..k {
let mut t_u16 = [0u16; N];
for l in 0..N {
t_u16[l] = ntt::barrett_reduce(t_hat[i][l]) as u16;
}
encode::byte_encode(12, &t_u16, &mut ek_out[384 * i..384 * (i + 1)]);
}
ek_out[384 * k..384 * k + 32].copy_from_slice(&rho);
for i in 0..k {
let mut s_u16 = [0u16; N];
for l in 0..N {
s_u16[l] = ntt::barrett_reduce(s_hat[i][l]) as u16;
}
encode::byte_encode(12, &s_u16, &mut dk_out[384 * i..384 * (i + 1)]);
}
for poly in s_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
for poly in e_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
Ok((ek_len, dk_len))
}
pub fn encrypt<P: Params>(ek_pke: &[u8], m: &[u8; 32], r: &[u8; 32], ct_out: &mut [u8]) -> usize {
let k = P::K;
let du = P::DU;
let dv = P::DV;
let mut t_hat = [[0i16; N]; MAX_K];
for i in 0..k {
let mut t_decoded = [0u16; N];
encode::byte_decode(12, &ek_pke[384 * i..384 * (i + 1)], &mut t_decoded);
for l in 0..N {
t_hat[i][l] = t_decoded[l] as i16;
}
}
let rho = &ek_pke[384 * k..384 * k + 32];
let mut a_hat = [[0i16; N]; MAX_K * MAX_K];
for i in 0..k {
for j in 0..k {
let mut seed = [0u8; 34];
seed[..32].copy_from_slice(rho);
seed[32] = j as u8;
seed[33] = i as u8;
a_hat[i * k + j] = sample::sample_ntt(&seed);
}
}
let mut n_counter = 0u8;
let mut y_hat = [[0i16; N]; MAX_K];
let mut prf_buf = [0u8; MAX_PRF_LEN];
for i in 0..k {
sha3::prf(P::ETA1, r, n_counter, &mut prf_buf);
y_hat[i] = sample::sample_poly_cbd(P::ETA1, &prf_buf[..64 * P::ETA1]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA1]);
ntt::ntt(&mut y_hat[i]);
n_counter += 1;
}
let mut e1 = [[0i16; N]; MAX_K];
for i in 0..k {
sha3::prf(P::ETA2, r, n_counter, &mut prf_buf);
e1[i] = sample::sample_poly_cbd(P::ETA2, &prf_buf[..64 * P::ETA2]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA2]);
n_counter += 1;
}
sha3::prf(P::ETA2, r, n_counter, &mut prf_buf);
let mut e2 = sample::sample_poly_cbd(P::ETA2, &prf_buf[..64 * P::ETA2]);
ntt::zeroize_bytes(&mut prf_buf[..64 * P::ETA2]);
let mut u = [[0i16; N]; MAX_K];
for i in 0..k {
let mut acc = [0i16; N];
for j in 0..k {
let mut tmp = [0i16; N];
ntt::multiply_ntts(&a_hat[j * k + i], &y_hat[j], &mut tmp);
for l in 0..N {
acc[l] = acc[l].wrapping_add(tmp[l]);
}
}
ntt::ntt_inv(&mut acc);
ntt::poly_add(&acc, &e1[i], &mut u[i]);
}
let mut mu = [0u16; N];
encode::byte_decode(1, m, &mut mu);
let mut v = [0i16; N];
{
let mut acc = [0i16; N];
for j in 0..k {
let mut tmp = [0i16; N];
ntt::multiply_ntts(&t_hat[j], &y_hat[j], &mut tmp);
for l in 0..N {
acc[l] = acc[l].wrapping_add(tmp[l]);
}
}
ntt::ntt_inv(&mut acc);
for l in 0..N {
let mu_dec = encode::decompress(1, mu[l]) as i16;
v[l] = acc[l].wrapping_add(e2[l]).wrapping_add(mu_dec);
}
}
let ct_len = 32 * (du * k + dv);
for i in 0..k {
let mut u_comp = [0u16; N];
for l in 0..N {
u_comp[l] = encode::compress(du as u32, ntt::barrett_reduce(u[i][l]) as u16);
}
encode::byte_encode(du, &u_comp, &mut ct_out[32 * du * i..32 * du * (i + 1)]);
}
let c2_off = 32 * du * k;
let mut v_comp = [0u16; N];
for l in 0..N {
v_comp[l] = encode::compress(dv as u32, ntt::barrett_reduce(v[l]) as u16);
}
encode::byte_encode(dv, &v_comp, &mut ct_out[c2_off..c2_off + 32 * dv]);
for poly in y_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
for poly in e1[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
ntt::zeroize_poly(&mut e2);
ct_len
}
pub fn decrypt<P: Params>(dk_pke: &[u8], c: &[u8]) -> [u8; 32] {
let k = P::K;
let du = P::DU;
let dv = P::DV;
let mut u = [[0i16; N]; MAX_K];
for i in 0..k {
let mut u_comp = [0u16; N];
encode::byte_decode(du, &c[32 * du * i..32 * du * (i + 1)], &mut u_comp);
for l in 0..N {
u[i][l] = encode::decompress(du as u32, u_comp[l]) as i16;
}
}
let c2_off = 32 * du * k;
let mut v_comp = [0u16; N];
encode::byte_decode(dv, &c[c2_off..c2_off + 32 * dv], &mut v_comp);
let mut v = [0i16; N];
for l in 0..N {
v[l] = encode::decompress(dv as u32, v_comp[l]) as i16;
}
let mut s_hat = [[0i16; N]; MAX_K];
for i in 0..k {
let mut s_dec = [0u16; N];
encode::byte_decode(12, &dk_pke[384 * i..384 * (i + 1)], &mut s_dec);
for l in 0..N {
s_hat[i][l] = s_dec[l] as i16;
}
}
for poly in u[..k].iter_mut() {
ntt::ntt(poly);
}
let mut acc = [0i16; N];
for j in 0..k {
let mut tmp = [0i16; N];
ntt::multiply_ntts(&s_hat[j], &u[j], &mut tmp);
for l in 0..N {
acc[l] = acc[l].wrapping_add(tmp[l]);
}
}
ntt::reduce(&mut acc);
ntt::ntt_inv(&mut acc);
let mut w = [0i16; N];
ntt::poly_sub(&v, &acc, &mut w);
let mut w_comp = [0u16; N];
for l in 0..N {
w_comp[l] = encode::compress(1, ntt::barrett_reduce(w[l]) as u16);
}
let mut m = [0u8; 32];
encode::byte_encode(1, &w_comp, &mut m);
for poly in s_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
ntt::zeroize_poly(&mut acc);
m
}
#[cfg(feature = "sca-protected")]
pub fn decrypt_sca<P: Params>(dk_pke: &[u8], c: &[u8], rng: &mut impl CryptoRng) -> Result<[u8; 32], MlKemError> {
let k = P::K;
let du = P::DU;
let dv = P::DV;
let mut u = [[0i16; N]; MAX_K];
for i in 0..k {
let mut u_comp = [0u16; N];
encode::byte_decode(du, &c[32 * du * i..32 * du * (i + 1)], &mut u_comp);
for l in 0..N {
u[i][l] = encode::decompress(du as u32, u_comp[l]) as i16;
}
}
let c2_off = 32 * du * k;
let mut v_comp = [0u16; N];
encode::byte_decode(dv, &c[c2_off..c2_off + 32 * dv], &mut v_comp);
let mut v = [0i16; N];
for l in 0..N {
v[l] = encode::decompress(dv as u32, v_comp[l]) as i16;
}
let mut s_hat = [[0i16; N]; MAX_K];
for i in 0..k {
let mut s_dec = [0u16; N];
encode::byte_decode(12, &dk_pke[384 * i..384 * (i + 1)], &mut s_dec);
for l in 0..N {
s_hat[i][l] = s_dec[l] as i16;
}
}
for poly in u[..k].iter_mut() {
shuffle::ntt_shuffled(poly, rng)?;
}
let mut acc_masked = MaskedPoly {
share0: [0i16; N],
share1: [0i16; N],
};
for j in 0..k {
let s_masked = MaskedPoly::mask(&s_hat[j], rng)?;
masked::masked_multiply_accumulate(&mut acc_masked, &s_masked, &u[j]);
}
let mut acc = acc_masked.unmask();
acc_masked.zeroize();
ntt::reduce(&mut acc);
ntt::ntt_inv(&mut acc);
let mut w = [0i16; N];
ntt::poly_sub(&v, &acc, &mut w);
let mut w_comp = [0u16; N];
for l in 0..N {
w_comp[l] = encode::compress(1, ntt::barrett_reduce(w[l]) as u16);
}
let mut m = [0u8; 32];
encode::byte_encode(1, &w_comp, &mut m);
for poly in s_hat[..k].iter_mut() {
ntt::zeroize_poly(poly);
}
ntt::zeroize_poly(&mut acc);
Ok(m)
}