round5 0.1.2

Implementation of Round5 post-quantum PKE and KEM algorithms
Documentation
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);

    // Encrypt m: ct = (U^T,v)
    r5_cpa_pke_encrypt(ct, pk, &m, &l_g_rho[params.kappa_bytes as usize * 2..], params);

    // Append g: ct = (U^T,v,g)
    ct[params.ct_size as usize..].copy_from_slice(&l_g_rho[params.kappa_bytes as usize..params.kappa_bytes as usize * 2]);
    
    // k = H(L, ct)
    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];

    // Encrypt m: ct' = (U'^T,v')
    r5_cpa_pke_encrypt(&mut ct_prime, pk, &m_prime, &l_g_rho_prime[params.kappa_bytes as usize * 2..], params);

    // Append g': ct' = (U'^T,v',g')
    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]));
    }
}