ferric_crypto_lib 0.2.7

A library for Ferric Crypto
Documentation
use crate::crypto_systems::hill_crypto::*;
use crate::prelude::*;
use crate::utils::BaseString;
use crate::Traits::Encrypt;
use nalgebra::DMatrix;

impl Encrypt<HillError, String, String> for Hill {
    /// Performs Hill cipher encryption on a given text.
    ///
    /// This function takes a text, and returns the encrypted text using the Hill cipher method.
    /// The type of operation (vertical or horizontal) is determined by the `direction` field of the `Hill` struct.
    ///
    /// # Arguments
    ///
    /// * `input` - A `String` that holds the original text to be encrypted.
    ///
    /// # Returns
    ///
    /// * A `Result<String, HillError>` which is `Ok` if the encryption is successful, and `Err` otherwise.
    ///   The `Ok` variant contains the encrypted text, and the `Err` variant contains an error type.
    ///
    /// # Example
    ///
    /// ## Vertical operation
    ///
    /// ```
    /// # use ferric_crypto_lib::crypto_systems::hill_crypto::*;
    /// # use ferric_crypto_lib::Traits::Encrypt;
    ///
    /// let t = "act";
    /// let x = 'x';
    /// let typ = HillDirection::Vertical;
    /// let mtrx = vec![vec![5, 17, 6], vec![2, 21, 14], vec![19, 3, 11]];
    /// let hill_crypto = Hill::new(mtrx, x, typ);
    /// let encrypted = hill_crypto.encrypt(t.to_string()).unwrap();
    ///
    /// println!("{}", encrypted);
    /// ```
    ///
    /// This will print `bpn`.
    ///
    /// ## Horizontal operation
    ///
    /// **Note:** This is currently not working with any key matrix that is not of size 2x2 and input of len 2.
    ///
    /// ```
    /// # use ferric_crypto_lib::crypto_systems::hill_crypto::*;
    /// # use ferric_crypto_lib::Traits::Encrypt;
    ///
    /// let t = "dy";
    /// let x = 'x';
    /// let typ = HillDirection::Horizontal;
    /// let mtrx = vec![vec![6, 25], vec![3, 11]];
    /// let hill_crypto = Hill::new(mtrx, x, typ);
    /// let encrypted = hill_crypto.encrypt(t.to_string()).unwrap();
    ///
    /// println!("{}", encrypted);
    /// ```
    ///
    /// This will print `fk`.
    fn encrypt(&self, input: String) -> Result<String, HillError> {
        // TODO key err check should be done in new not here
        // Check if data is valid, if not return error
        if !self.is_valid_key() {
            return Err(HillError::InvalidKey);
        }
        if !self.is_valid_filler() {
            return Err(HillError::InvalidFiller);
        }

        let m = 28; // Assume m is fixed at 28 // TODO: make this the size of the alphabet

        let j = self.key.len(); // block length, i.e., number of columns

        // Calculate and add padding characters
        let padding_len = if input.len() % j == 0 {
            0
        } else {
            j - (input.len() % j)
        };
        let t = input
            + &std::iter::repeat(self.filler)
                .take(padding_len)
                .collect::<String>();

        let i = t.len() / j; // number of rows

        // must be done in a better way later!
        let t = BaseString::new(t); // Convert the string to a BaseString,
        let p = match t.encode() {
            Ok(p) => p,
            Err(e) => return Err(HillError::CharacterParseError(e)),
        }
        .data; // Encode the text to a matrix representation

        let c: Vec<Vec<usize>> = match self.direction {
            HillDirection::Vertical => {
                let p_matrix = DMatrix::from_row_slice(i, j, &p).map(|x| x as f64);
                let key_matrix = DMatrix::from_fn(j, j, |r, c| self.key[r][c] as f64);

                let result_matrix = (&p_matrix * &key_matrix).map(|x| (x as usize) % m);

                result_matrix
                    .row_iter()
                    .map(|row| row.iter().map(|&x| x % m).collect())
                    .collect()
            }
            HillDirection::Horizontal => {
                let p_matrix = DMatrix::from_row_slice(i, j, &p).map(|x| x as f64);
                let key_matrix = DMatrix::from_fn(j, j, |r, c| self.key[r][c] as f64).transpose();
                let result_matrix = (&p_matrix * &key_matrix).map(|x| (x as usize) % m);

                result_matrix
                    .row_iter()
                    .map(|row| row.iter().map(|&x| x % m).collect())
                    .collect()
            }
        };

        let c: Vec<usize> = c.into_iter().flatten().collect();
        let mut encrypted_str = match decode_list(c) {
            Ok(s) => s,
            Err(e) => return Err(HillError::CharacterParseError(e)),
        };

        // Remove padding characters from the end of the encrypted string
        encrypted_str.truncate(encrypted_str.len() - padding_len);

        Ok(encrypted_str)
    }
}

// test code
#[cfg(test)]
mod test {
    use super::*;
    use crate::Traits::Encrypt;

    /// Macro to generate a test function for the Hill cipher.
    ///
    /// This macro generates a test function that creates a new `Hill` instance with the provided parameters,
    /// performs encryption on a given text, and asserts that the result is equal to the expected output.
    ///
    /// # Parameters
    ///
    /// * `$name` - The name of the test function.
    /// * `$t` - The text to be encrypted.
    /// * `$x` - The filler character.
    /// * `$typ` - The direction of the operation (`HillDirection::Vertical` or `HillDirection::Horizontal`).
    /// * `$mtrx` - The key matrix.
    /// * `$expected` - The expected result of the encryption.
    ///
    /// # Example
    ///
    /// ```
    /// # #[macro_use] extern crate ferric_crypto_lib;
    /// # use ferric_crypto_lib::crypto_systems::hill_crypto::*;
    ///
    /// generate_hill_test!(
    ///     test_hill_crypto_V,                                     // name of the test function
    ///     "act",                                                  // text to be encrypted
    ///     'x',                                                    // filler character to use
    ///     HillDirection::Vertical,                                // direction of the operation
    ///     vec![vec![5, 17, 6], vec![2, 21, 14], vec![19, 3, 11]], // key matrix
    ///     "bpn"                                                   // expected result
    /// );
    /// ```
    ///
    /// This will generate a test function named `test_hill_crypto_V` that tests the Hill cipher encryption with vertical operation.
    #[macro_export]
    macro_rules! generate_hill_test {
        ($name:ident, $t:expr, $x:expr, $typ:expr, $mtrx:expr, $expected:expr) => {
            #[test]
            fn $name() {
                let t = $t;
                let x = $x;
                let typ = $typ;
                let mtrx = $mtrx;
                let crypto = Hill::new(mtrx, x, typ);

                // Ensure the key is valid before encrypting
                assert!(crypto.is_valid_key());

                // Perform encryption
                let c = crypto.encrypt(t.to_string()).unwrap();

                assert_eq!(c, $expected);
            }
        };
    }

    // test 3x3 matrix both directions
    generate_hill_test!(
        test_hill_crypto_V,
        "act",
        'x',
        HillDirection::Vertical,
        vec![vec![5, 17, 6], vec![2, 21, 14], vec![19, 3, 11]],
        "bpn"
    );

    generate_hill_test!(
        test_hill_crypto_H,
        "act",
        'x',
        HillDirection::Horizontal,
        vec![vec![5, 17, 6], vec![2, 21, 14], vec![19, 3, 11]],
        "iat"
    );

    // test 2x2 matrix both directions
    generate_hill_test!(
        test_hill_crypto_2_V,
        "dy",
        'x',
        HillDirection::Vertical,
        vec![vec![6, 25], vec![3, 11]],
        "du"
    );

    generate_hill_test!(
        test_hill_crypto_2_H,
        "dy",
        'x',
        HillDirection::Horizontal,
        vec![vec![6, 25], vec![3, 11]],
        "fk"
    );

    // TODO: Test with more chars then the key matrix can handle (should split into blocks)
}