raycrypt 0.3.1

Encrypt at the speed of light
Documentation
use crate::utils::from_le_bytes;

fn quarter_round(a: usize, b: usize, c: usize, d: usize, block: &mut [u32; 16]) {
    block[a] = block[a].wrapping_add(block[b]);
    block[d] ^= block[a];
    block[d] = block[d].rotate_left(16);

    block[c] = block[c].wrapping_add(block[d]);
    block[b] ^= block[c];
    block[b] = block[b].rotate_left(12);

    block[a] = block[a].wrapping_add(block[b]);
    block[d] ^= block[a];
    block[d] = block[d].rotate_left(8);

    block[c] = block[c].wrapping_add(block[d]);
    block[b] ^= block[c];
    block[b] = block[b].rotate_left(7);
}

fn double_round(mut block: [u32; 16]) -> [u32; 16] {
    quarter_round(0, 4, 8, 12, &mut block);
    quarter_round(1, 5, 9, 13, &mut block);
    quarter_round(2, 6, 10, 14, &mut block);
    quarter_round(3, 7, 11, 15, &mut block);

    quarter_round(0, 5, 10, 15, &mut block);
    quarter_round(1, 6, 11, 12, &mut block);
    quarter_round(2, 7, 8, 13, &mut block);
    quarter_round(3, 4, 9, 14, &mut block);

    block
}

pub struct ChaCha20 {
    key: Vec<u8>,
}

impl ChaCha20 {
    pub fn new(key: &[u8]) -> ChaCha20 {
        ChaCha20 { key: key.to_vec() }
    }

    pub fn keystream(&self, nonce: &[u8], counter: u32) -> [u8; 64] {
        let mut state = [
            0x61707865,
            0x3320646e,
            0x79622d32,
            0x6b206574,
            from_le_bytes(&self.key[0..4]),
            from_le_bytes(&self.key[4..8]),
            from_le_bytes(&self.key[8..12]),
            from_le_bytes(&self.key[12..16]),
            from_le_bytes(&self.key[16..20]),
            from_le_bytes(&self.key[20..24]),
            from_le_bytes(&self.key[24..28]),
            from_le_bytes(&self.key[28..]),
            counter,
            from_le_bytes(&nonce[4..8]),
            from_le_bytes(&nonce[8..12]),
            from_le_bytes(&nonce[12..]),
        ];

        let mut original_state = state.clone();

        for _ in 0..10 {
            state = double_round(state);
        }

        for (i, j) in state.iter_mut().zip(original_state.iter()) {
            *i += *j;
        }

        let mut result = [0u8; 64];

        for (index, chunk) in state.iter().enumerate() {
            result[index * 4..].copy_from_slice(&chunk.to_le_bytes());
        }

        result
    }

    pub fn encrypt(&self, plaintext: &[u8], nonce: &[u8]) -> Vec<u8> {
        let mut ciphertext: Vec<u8> = Vec::new();

        for block in plaintext.chunks(64) {
            let keystream = self.keystream(nonce, 1);

            for (key, chunk) in block.iter().zip(keystream) {
                ciphertext.push(chunk ^ key);
            }
        }

        ciphertext
    }
}

pub struct HChaCha20 {
    state: [u32; 12],
}

impl HChaCha20 {
    pub fn new(key: &[u8]) -> HChaCha20 {
        let state = [
            0x61707865,
            0x3320646e,
            0x79622d32,
            0x6b206574,
            from_le_bytes(&key[0..4]),
            from_le_bytes(&key[4..8]),
            from_le_bytes(&key[8..12]),
            from_le_bytes(&key[12..16]),
            from_le_bytes(&key[16..20]),
            from_le_bytes(&key[20..24]),
            from_le_bytes(&key[24..28]),
            from_le_bytes(&key[28..32]),
        ];

        HChaCha20 { state }
    }

    pub fn keystream(&self, nonce: &[u8]) -> [u8; 32] {
        let mut state = [0u32; 16];
        state[..12].copy_from_slice(&self.state);

        state[12..16].copy_from_slice(&[
            from_le_bytes(&nonce[0..4]),
            from_le_bytes(&nonce[4..8]),
            from_le_bytes(&nonce[8..12]),
            from_le_bytes(&nonce[12..16]),
        ]);

        for _ in 0..10 {
            state = double_round(state);
        }

        let mut result = [0u8; 32];

        for (result_chunk, chunk) in result
            .chunks_exact_mut(4)
            .zip(state[0..4].iter().chain(state[12..16].iter()))
        {
            result_chunk.copy_from_slice(&chunk.to_le_bytes());
        }

        result
    }
}