yafo 0.1.1

Yet another file obfuscator
Documentation
//! Cipher algorithms for both encryption and decryption.
//!
//! This module contains the [`Cipher`] trait which is implemented by
//! [`EncryptState`] (for encryption) and [`DecryptState`] (for decryption).
//! They can be used with [`Pipeline`] for any specified purpose.
//!
//! To create a cipher, you should use [`KeyInit::with_key`] method of
//! the types that implement [`KeyInit`] trait.
//!
//! [`Pipeline`]: crate::pipeline::Pipeline
//! [`KeyInit`]: crate::key_init::KeyInit
//! [`KeyInit::with_key`]: crate::key_init::KeyInit::with_key

use crate::types::DataChunk;

/// Trait implemented by types that provide cipher algorithm.
pub trait Cipher {
    /// Processes a chunk of data.
    fn process_chunk(&mut self, chunk: &mut DataChunk);
}

macro_rules! impl_state {
    ($($name:ident),*) => {
        $(impl_state!(@ $name);)*
    };

    (@ $name:ident) => {
        pub struct $name {
            current_key: DataChunk,
        }

        impl $crate::key_init::KeyInit for $name {
            fn with_key(key: DataChunk) -> Self {
                Self { current_key: key }
            }
        }
    };
}

impl_state!(EncryptState, DecryptState);

impl Cipher for EncryptState {
    fn process_chunk(&mut self, chunk: &mut DataChunk) {
        let c_sum = calculate_sum(chunk);
        let k_sum = calculate_sum(&self.current_key);

        for (chunk_byte, key_byte) in chunk.0.iter_mut().zip(&self.current_key) {
            let factor_a = (k_sum ^ key_byte) as u32;
            *chunk_byte = chunk_byte.rotate_left(factor_a) ^ key_byte;
        }

        rotate_key(&mut self.current_key, c_sum);
    }
}

impl Cipher for DecryptState {
    fn process_chunk(&mut self, chunk: &mut DataChunk) {
        let k_sum = calculate_sum(&self.current_key);

        for (chunk_byte, key_byte) in chunk.0.iter_mut().zip(&self.current_key) {
            let factor_a = (k_sum ^ key_byte) as u32;
            *chunk_byte = (*chunk_byte ^ key_byte).rotate_right(factor_a);
        }

        let c_sum = calculate_sum(chunk);
        rotate_key(&mut self.current_key, c_sum);
    }
}

impl<T> Cipher for &mut T
where
    T: Cipher + ?Sized,
{
    fn process_chunk(&mut self, chunk: &mut DataChunk) {
        (*self).process_chunk(chunk)
    }
}

#[inline(always)]
fn calculate_sum(chunk: &DataChunk) -> u8 {
    let mut sum = 0;
    for byte in chunk {
        sum ^= byte;
    }
    sum
}

#[inline(always)]
fn rotate_key(key: &mut DataChunk, mut sum: u8) {
    for byte in key.as_mut_slice() {
        *byte ^= sum;
        sum = sum.rotate_left(1);
    }

    // Rotate the chunk left by byte.
    key.0.rotate_left(1);
}

#[cfg(test)]
mod tests {
    extern crate test;
    use test::Bencher;

    use super::{Cipher, EncryptState};
    use crate::key_init::KeyInit;
    use crate::types::DataChunk;

    #[test]
    fn test_encrypt() {
        let mut state = EncryptState::with_key_from([1, 2, 4, 8, 16, 32, 64, 128]);
        let mut plain_data_chunk_1 = DataChunk::from([1, 1, 1, 1, 2, 2, 2, 2]);
        let mut plain_data_chunk_2 = DataChunk::from([3, 3, 3, 3, 4, 4, 4, 4]);
        state.process_chunk(&mut plain_data_chunk_1);
        state.process_chunk(&mut plain_data_chunk_2);

        assert_eq!(plain_data_chunk_1.0, [65, 34, 12, 136, 17, 33, 65, 129]);
        assert_eq!(plain_data_chunk_2.0, [98, 28, 137, 145, 34, 66, 130, 0]);
    }

    #[bench]
    fn bench_our_algorithm(b: &mut Bencher) {
        let mut state = EncryptState::with_key_from(test::black_box([1, 2, 4, 8, 16, 32, 64, 128]));
        let mut plain_data_chunk_1 = DataChunk::from(test::black_box([1, 1, 1, 1, 2, 2, 2, 2]));
        let mut plain_data_chunk_2 = DataChunk::from(test::black_box([3, 3, 3, 3, 4, 4, 4, 4]));

        b.iter(|| {
            state.process_chunk(&mut plain_data_chunk_1);
            state.process_chunk(&mut plain_data_chunk_2);
        });
    }

    #[bench]
    fn bench_aes(b: &mut Bencher) {
        use aes::cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit};
        use aes::Aes128;

        let key = GenericArray::from(test::black_box([1u8; 16]));
        let mut block = GenericArray::from(test::black_box([42u8; 16]));
        let cipher = Aes128::new(&key);

        b.iter(|| {
            cipher.encrypt_block(&mut block);
        });
    }

    #[bench]
    fn bench_tea(b: &mut Bencher) {
        use tea_soft::block_cipher::{generic_array::GenericArray, BlockCipher, NewBlockCipher};
        use tea_soft::Tea16;

        let key = GenericArray::from(test::black_box([1u8; 16]));
        let mut block_1 = GenericArray::from(test::black_box([42u8; 8]));
        let mut block_2 = GenericArray::from(test::black_box([42u8; 8]));
        let cipher = Tea16::new(&key);

        b.iter(|| {
            cipher.encrypt_block(&mut block_1);
            cipher.encrypt_block(&mut block_2);
        });
    }
}