1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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());
}
}