use crate::types::Random;
use crate::r5_hash::hash;
use crate::parameters::Parameters;
use crate::r5_cpa_pke::{r5_cpa_pke_keygen, r5_cpa_pke_encrypt, r5_cpa_pke_decrypt};
pub fn r5_cca_kem_keygen(pk: &mut [u8], sk: &mut [u8], drbg: &mut dyn Random, params: &Parameters) {
r5_cpa_pke_keygen(pk, sk, drbg, params);
let mut y = vec![0u8; params.kappa_bytes as usize];
drbg.fill_bytes(&mut y);
let index = params.kappa_bytes as usize;
sk[index..index * 2].copy_from_slice(y.as_slice());
sk[index * 2..].copy_from_slice(pk);
}
pub fn r5_cca_kem_encapsulate(ct: &mut [u8], k: &mut [u8], pk: &[u8],
drbg: &mut dyn Random, params: &Parameters) {
let mut m = vec![0u8; params.kappa_bytes as usize];
drbg.fill_bytes(&mut m);
let mut hash_input: Vec<u8> = Vec::with_capacity(params.kappa_bytes as usize + params.pk_size as usize);
hash_input.extend(&m);
hash_input.extend(pk);
let mut l_g_rho = vec![0u8; 3 * params.kappa_bytes as usize];
hash(&mut l_g_rho, &hash_input, params.kappa_bytes);
r5_cpa_pke_encrypt(ct, pk, &m, &l_g_rho[params.kappa_bytes as usize * 2..], params);
ct[params.ct_size as usize..].copy_from_slice(&l_g_rho[params.kappa_bytes as usize..params.kappa_bytes as usize * 2]);
hash_input.resize(2 * params.kappa_bytes as usize + params.ct_size as usize, 0);
hash_input[..params.kappa_bytes as usize].copy_from_slice(&l_g_rho[..params.kappa_bytes as usize]);
hash_input[params.kappa_bytes as usize..].copy_from_slice(&ct[..params.ct_size as usize + params.kappa_bytes as usize]);
hash(k, &hash_input, params.kappa_bytes);
}
pub fn r5_cca_kem_decapsulate(k: &mut [u8], ct: &[u8], sk: &[u8], params: &Parameters) {
let pk = &sk[2 * params.kappa_bytes as usize..];
let mut m_prime = vec![0u8; params.kappa_bytes as usize];
r5_cpa_pke_decrypt(&mut m_prime, sk, ct, params);
let mut hash_input: Vec<u8> = Vec::with_capacity(params.kappa_bytes as usize + params.pk_size as usize);
hash_input.extend(&m_prime);
hash_input.extend(pk);
let mut l_g_rho_prime = vec![0u8; 3 * params.kappa_bytes as usize];
hash(&mut l_g_rho_prime, &hash_input, params.kappa_bytes);
let mut ct_prime = vec![0u8; params.ct_size as usize + params.kappa_bytes as usize];
r5_cpa_pke_encrypt(&mut ct_prime, pk, &m_prime, &l_g_rho_prime[params.kappa_bytes as usize * 2..], params);
ct_prime[params.ct_size as usize..].copy_from_slice(&l_g_rho_prime[params.kappa_bytes as usize..params.kappa_bytes as usize * 2]);
hash_input.resize(2 * params.kappa_bytes as usize + params.ct_size as usize, 0);
let fail = verify(ct, &ct_prime, params.ct_size as usize + params.kappa_bytes as usize);
hash_input[..params.kappa_bytes as usize].copy_from_slice(&l_g_rho_prime[..params.kappa_bytes as usize]);
hash_input[params.kappa_bytes as usize..].copy_from_slice(&ct_prime[..params.ct_size as usize + params.kappa_bytes as usize]);
cctmemcpy(&mut hash_input[..params.kappa_bytes as usize], &sk[params.kappa_bytes as usize..params.kappa_bytes as usize * 2], fail);
hash(k, &hash_input, params.kappa_bytes);
}
fn verify(s1: &[u8], s2: &[u8], n: usize) -> bool {
(0..n).fold(0, |acc, i| acc | (s1[i] ^ s2[i])) != 0
}
fn cctmemcpy(output: &mut [u8], input: &[u8], flag: bool) {
assert!(output.len() == input.len());
let flag = if flag { u8::max_value() } else { 0u8 };
for i in 0..output.len() {
output[i] = output[i] ^ (flag & (output[i] ^ input[i]));
}
}