use super::indcpa::{self, POLYBYTES, du_bytes, dv_bytes};
use crate::ct::{ConditionallySelectable, ConstantTimeEq};
use crate::hash::{ExtendableOutput, Shake256, XofReader, sha3_256, sha3_512};
pub(crate) const fn ek_bytes(k: usize) -> usize {
POLYBYTES * k + 32
}
pub(crate) const fn dk_bytes(k: usize) -> usize {
POLYBYTES * k + ek_bytes(k) + 64
}
pub(crate) const fn ct_bytes(k: usize, du: usize, dv: usize) -> usize {
du_bytes(du) * k + dv_bytes(dv)
}
const MAX_CT_BYTES: usize = 1568;
pub(crate) fn keygen<const K: usize, const ETA1: usize>(
d: &[u8; 32],
z: &[u8; 32],
ek: &mut [u8],
dk: &mut [u8],
) {
debug_assert_eq!(ek.len(), ek_bytes(K));
debug_assert_eq!(dk.len(), dk_bytes(K));
let pke_dk = POLYBYTES * K;
let pke_ek = ek_bytes(K);
indcpa::keygen::<K, ETA1>(d, ek, &mut dk[..pke_dk]);
dk[pke_dk..pke_dk + pke_ek].copy_from_slice(ek);
dk[pke_dk + pke_ek..pke_dk + pke_ek + 32].copy_from_slice(&sha3_256(ek));
let total = dk.len();
dk[total - 32..].copy_from_slice(z);
}
pub(crate) fn encaps<
const K: usize,
const ETA1: usize,
const ETA2: usize,
const DU: usize,
const DV: usize,
>(
ek: &[u8],
m: &[u8; 32],
ct: &mut [u8],
) -> [u8; 32] {
debug_assert_eq!(ek.len(), ek_bytes(K));
debug_assert_eq!(ct.len(), ct_bytes(K, DU, DV));
let mut g_in = [0u8; 64];
g_in[..32].copy_from_slice(m);
g_in[32..].copy_from_slice(&sha3_256(ek));
let g = sha3_512(&g_in);
let mut shared = [0u8; 32];
shared.copy_from_slice(&g[..32]);
let mut r = [0u8; 32];
r.copy_from_slice(&g[32..]);
indcpa::encrypt::<K, ETA1, ETA2, DU, DV>(ek, m, &r, ct);
shared
}
pub(crate) fn decaps<
const K: usize,
const ETA1: usize,
const ETA2: usize,
const DU: usize,
const DV: usize,
>(
dk: &[u8],
ct: &[u8],
) -> [u8; 32] {
debug_assert_eq!(dk.len(), dk_bytes(K));
debug_assert_eq!(ct.len(), ct_bytes(K, DU, DV));
let pke_dk = POLYBYTES * K;
let pke_ek = ek_bytes(K);
let dk_pke = &dk[..pke_dk];
let ek = &dk[pke_dk..pke_dk + pke_ek];
let hek = &dk[pke_dk + pke_ek..pke_dk + pke_ek + 32];
let z = &dk[dk.len() - 32..];
let m_prime = indcpa::decrypt::<K, DU, DV>(dk_pke, ct);
let mut g_in = [0u8; 64];
g_in[..32].copy_from_slice(&m_prime);
g_in[32..].copy_from_slice(hek);
let g = sha3_512(&g_in);
let mut k_prime = [0u8; 32];
k_prime.copy_from_slice(&g[..32]);
let mut r_prime = [0u8; 32];
r_prime.copy_from_slice(&g[32..]);
let mut k_bar = [0u8; 32];
let mut shake = Shake256::new();
shake.update(z);
shake.update(ct);
shake.finalize_xof().read(&mut k_bar);
let mut ct_cmp = [0u8; MAX_CT_BYTES];
let ct_len = ct_bytes(K, DU, DV);
indcpa::encrypt::<K, ETA1, ETA2, DU, DV>(ek, &m_prime, &r_prime, &mut ct_cmp[..ct_len]);
let matches = ct.ct_eq(&ct_cmp[..ct_len]);
let mut out = k_bar;
out.conditional_assign(&k_prime, matches);
out
}