purecrypt 0.1.0-alpha

Minimal, readable, dependency-free implementations of modern cryptographic primitives in pure Rust
Documentation
#[cfg(feature = "zeroize")]
use zeroize::{Zeroize, ZeroizeOnDrop};

use super::COUNTER_LEN;
use super::{COUNTER_RANGE, NONCE_LEN, NONCE_RANGE};
use crate::chacha::full_round;
use crate::chacha::types::Key;
use crate::chacha::{Constants, consts::*};
use crate::utils::{bytes_to_words, words_to_bytes};

#[cfg_attr(feature = "zeroize", derive(Zeroize, ZeroizeOnDrop))]
pub struct DjbChaChaCore<const ROUNDS: usize>([u32; STATE_LEN_WORDS]);

impl<const ROUNDS: usize> DjbChaChaCore<ROUNDS> {
    #[allow(unused_mut)]
    pub fn new(key: &Key, mut nonce: u64) -> Self {
        let mut state = [0_u32; STATE_LEN_WORDS];

        bytes_to_words(&DEFAULT_CONSTANTS, &mut state[CONSTANTS_RANGE]);
        bytes_to_words(key.bytes(), &mut state[KEY_RANGE]);
        bytes_to_words(&nonce.to_le_bytes(), &mut state[NONCE_RANGE]);

        #[cfg(feature = "zeroize")]
        nonce.zeroize();

        Self(state)
    }

    #[inline(always)]
    pub fn generate_block(&mut self, dst: &mut [u8]) {
        let mut working = self.0;

        for _ in 0..(ROUNDS / 2) {
            full_round(&mut working);
        }

        (0..STATE_LEN_WORDS).for_each(|i| {
            working[i] = working[i].wrapping_add(self.0[i]);
        });

        const COUNTER_L: usize = COUNTER_RANGE.start;
        const COUNTER_H: usize = COUNTER_L + 1;

        self.0[COUNTER_L] = self.0[COUNTER_L].wrapping_add(1);

        if self.0[COUNTER_L] == 0 {
            self.0[COUNTER_H] = self.0[COUNTER_H].wrapping_add(1)
        }

        words_to_bytes(&working, dst);

        #[cfg(feature = "zeroize")]
        working.zeroize();
    }

    pub fn get_state(&self) -> &[u32; STATE_LEN_WORDS] {
        &self.0
    }

    pub fn get_key(&self) -> Key {
        let mut key = Key::default();
        words_to_bytes(&self.0[KEY_RANGE], key.bytes_mut());

        key
    }

    pub fn set_key(&mut self, key: &Key) {
        bytes_to_words(key.bytes(), &mut self.0[KEY_RANGE]);
    }

    pub fn get_constants(&self) -> Constants {
        let mut constants = Constants::default();
        words_to_bytes(&self.0[CONSTANTS_RANGE], constants.bytes_mut());

        constants
    }

    pub fn set_constants(&mut self, constants: &Constants) {
        bytes_to_words(constants.bytes(), &mut self.0[CONSTANTS_RANGE]);
    }

    pub fn get_counter(&self) -> u64 {
        let mut buf = [0; COUNTER_LEN];
        words_to_bytes(&self.0[COUNTER_RANGE], &mut buf);

        u64::from_le_bytes(buf)
    }

    pub fn set_counter(&mut self, counter: u64) {
        let bytes = counter.to_le_bytes();

        bytes_to_words(&bytes, &mut self.0[COUNTER_RANGE]);
    }

    pub fn get_nonce(&self) -> u64 {
        let mut buf = [0; NONCE_LEN];
        words_to_bytes(&self.0[NONCE_RANGE], &mut buf);

        u64::from_le_bytes(buf)
    }

    pub fn set_nonce(&mut self, nonce: u64) {
        let bytes = nonce.to_le_bytes();

        bytes_to_words(&bytes, &mut self.0[NONCE_RANGE]);
    }
}