round5 0.1.2

Implementation of Round5 post-quantum PKE and KEM algorithms
Documentation
use crate::types::Random;
use openssl::symm::{encrypt, Cipher};
use std::io::Error;

pub const CUSTOMIZATION_STRING_LENGTH: usize = 48;
const AES256_KEY_LENGTH: usize = 32;
const AES256_V_LENGTH: usize = 16;

pub struct Aes256CtrDrbg {
    key: [u8; AES256_KEY_LENGTH],
    v: [u8; AES256_V_LENGTH],
    reseed_counter: i32,
}

impl Aes256CtrDrbg {
    pub fn new(entropy_input: &[u8], personalization_string: Option<&[u8]>) -> Aes256CtrDrbg {
        let mut seed_material: [u8; CUSTOMIZATION_STRING_LENGTH] = [0; CUSTOMIZATION_STRING_LENGTH];
        seed_material.clone_from_slice(&entropy_input[..CUSTOMIZATION_STRING_LENGTH]);
        if let Some(p_string) = personalization_string {
            array_xor_in_place!(seed_material, p_string)
        }
        let mut ctr_drbg = Aes256CtrDrbg {
            key: [0; AES256_KEY_LENGTH],
            v: [0; AES256_V_LENGTH],
            reseed_counter: 1
        };
        ctr_drbg.update(Some(&seed_material));
        ctr_drbg
    }
    
    fn update(&mut self, provided_data: Option<&[u8]>) {
        let encryptor_iter = (0..CUSTOMIZATION_STRING_LENGTH / 16)
                                .flat_map(|_| {
                                    self.inc_v();
                                    encrypt(Cipher::aes_256_ecb(), &self.key, None, &self.v)
                                        .unwrap()
                                        .into_iter()
                                        .take(16)
                                });
        let mut temp: [u8; CUSTOMIZATION_STRING_LENGTH] = array_init::from_iter(encryptor_iter).unwrap();

        if let Some(data) = provided_data {
            array_xor_in_place!(temp, data)
        }
        self.key.clone_from_slice(&temp[..32]);
        self.v.clone_from_slice(&temp[32..48]);
    }

    fn inc_v(&mut self) {
        // TO-DO consider using .to_bytes?
        for v in self.v.iter_mut().rev() {
            match *v {
                0xff => { *v = 0x00; },
                _ => {
                    *v += 1;
                    break;
                }
            };
        }
    }

    fn randombytes(&mut self, dest: &mut [u8], len: usize) -> Result<(), openssl::error::ErrorStack> {
        let mut block = [0u8; 16];
        let mut i = 0usize;
    
        let mut xlen = len;
        while xlen > 0 {
            self.inc_v();
            block.copy_from_slice(&encrypt(Cipher::aes_256_ecb(), &self.key, None, &self.v)?[..16]);
            if xlen > 15 {
                dest[i..i + 16].copy_from_slice(&block);
                i += 16;
                xlen -= 16;
            } else {
                dest[i..i + xlen as usize].copy_from_slice(&block[..xlen as usize]);
                xlen = 0;
            }
        }
        self.update(None);
        self.reseed_counter += 1;
        Ok(())
    }
}

impl rand_core::RngCore for Aes256CtrDrbg {
    fn next_u32(&mut self) -> u32 {
        rand_core::impls::next_u32_via_fill(self)
    }

    fn next_u64(&mut self) -> u64 {
        rand_core::impls::next_u64_via_fill(self)
    }

    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.try_fill_bytes(dest).unwrap();
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
        self.randombytes(dest, dest.len()).map_err(|e| rand_core::Error::new(Error::from(e)))
    }
}

impl rand_core::CryptoRng for Aes256CtrDrbg { }

impl Random for Aes256CtrDrbg { }