ferric_crypto_lib 0.2.7

A library for Ferric Crypto
Documentation
use crate::crypto_systems::ceasar::Ceasar;
use crate::error::CeasarError;
use crate::prelude::*;
use crate::Traits::{BruteForce, Decrypt};

use core::sync::atomic::{AtomicBool, Ordering};
use rayon::prelude::*;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

type CeaesarResultMap = HashMap<usize, String>;

impl BruteForce<CeaesarResultMap, CeasarError, CeaesarResultMap, Option<usize>> for Ceasar {
    /// The `brute_force` function attempts to decrypt a given input string by trying all possible keys of the Ceasar cipher.
    /// It takes two arguments: the input string to be decrypted and an optional clear text string.
    /// If the clear text string is provided, the function will return as soon as it finds a match.
    /// If no clear text string is provided, the function will return all possible decrypted strings.
    ///
    /// # Arguments
    ///
    /// * `input` - A string that holds the text to be decrypted.
    /// * `clear_text` - An optional string that, if provided, the function will stop and return as soon as it finds a match.
    ///
    /// # Returns
    ///
    /// * `Result<CeaesarResultMap, CeasarError>` - A Result type that holds either a HashMap of all possible decrypted strings (with the key used for decryption as the key in the map), or a CeasarError.
    ///
    /// # Errors
    ///
    /// This function will return an error if:
    /// * The clear text string is provided and its length does not match the length of the input string.
    /// * The decryption process fails.
    fn brute_force(
        &mut self,
        input: String,
        clear_text: Option<String>,
        key_info: Option<usize>,
    ) -> Result<CeaesarResultMap, CeasarError> {
        if let Some(clear_text) = &clear_text {
            if clear_text.chars().count() != input.chars().count() {
                return Err(CeasarError::InvalidClearText(clear_text.clone()));
            }
        }

        // check input for invalid characters
        if let Err(e) = encode_string(&input) {
            return Err(e.into());
        }

        // create hashmap to store all possible permutations
        let mut permutations: CeaesarResultMap = match self.gen_permutations(key_info) {
            Ok(permutations) => permutations,
            Err(e) => return Err(e),
        };

        let mutex_cipher = Arc::new(Mutex::new(self.clone()));
        let found = Arc::new(AtomicBool::new(false));
        let input_ref = &input; // Use a reference to avoid cloning

        permutations.par_iter_mut().for_each(|(key, value)| {
            if found.load(Ordering::Relaxed) {
                return;
            }

            let mut cipher = self.clone();
            cipher.set_key(*key);
            let decrypted = match cipher.decrypt(input_ref.clone().into()) {
                Ok(decrypted) => decrypted,
                Err(e) => {
                    // Handle error appropriately
                    return;
                }
            };

            *value = decrypted.clone().into();

            if let Some(clear_text) = &clear_text {
                if decrypted.data == *clear_text {
                    found.store(true, Ordering::Relaxed);
                }
            }
        });

        if found.load(Ordering::Relaxed) {
            // If clear text was found, filter results
            permutations.retain(|_, v| v == clear_text.as_ref().unwrap());
        }

        // sequential version might be faster, currently testing
        /*
        for (key, value) in permutations.iter_mut() {
            self.set_key(*key);
            let decrypted = match self.decrypt(input.clone()) {
                Ok(decrypted) => decrypted,
                Err(e) => {
                    #[cfg(feature = "python-integration")]
                    {
                        return Err(e.into())
                    }
                    #[cfg(not(feature = "python-integration"))]
                    {
                        return Err(e)
                    }
                }
            };

            *value = decrypted.clone();

            if let Some(clear_text) = &clear_text {
                if decrypted == *clear_text {
                    return Ok(permutations);
                }
            }
        }
        */
        Ok(permutations)
    }

    /// The `gen_permutations` function generates a HashMap of all possible keys for the Ceasar cipher.
    /// The keys in the map are the keys used for decryption, and the values are empty strings that will hold the decrypted strings.
    ///
    /// # Returns
    ///
    /// * `Result<CeaesarResultMap, CeasarError>` - A Result type that holds either a HashMap of all possible keys for the Ceasar cipher, or a CeasarError.
    fn gen_permutations(
        &mut self,
        key_info: Option<usize>,
    ) -> Result<CeaesarResultMap, CeasarError> {
        // Ceasar cipher only has ALPHABET_LEN - 1 possible keys
        let permutations: Arc<Mutex<CeaesarResultMap>> = Arc::new(Mutex::new(HashMap::new()));

        (0..*ALPHABET_LEN)
            .into_par_iter()
            .map(|key| {
                let mut local_permutations = permutations.lock().unwrap();
                local_permutations.insert(key, String::new());
            })
            .collect::<Vec<_>>();

        Ok(Arc::try_unwrap(permutations).unwrap().into_inner().unwrap())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::HashMap;

    #[test]
    fn brute_force_returns_all_permutations_when_no_clear_text() {
        let mut ceasar = Ceasar::default();
        let input = String::from("KHOOR");
        let result = ceasar.brute_force(input, None, None);

        assert!(result.is_ok());
        let permutations = result.unwrap();
        assert!(permutations
            .iter()
            .any(|(k, v)| k == &3usize && v == "hello"));
    }

    #[test]
    fn brute_force_returns_matching_permutation_when_clear_text_matches() {
        let mut ceasar = Ceasar::default();
        let input = String::from("KHOORZZRUOG");
        let clear_text = String::from("hellovvorld");
        let result = ceasar.brute_force(input, Some(clear_text.clone()), None);

        assert!(result.is_ok());
        let permutations = result.unwrap();
        let result = permutations.get(&3);
        assert_eq!(result, Some(&clear_text));
    }

    #[test]
    fn brute_force_returns_error_when_decryption_fails() {
        let mut ceasar = Ceasar::default();
        let input = String::from("encrypted text with inwalid characters");
        let result = ceasar.brute_force(input, None, None);

        assert!(result.is_err());
    }
}