ares/lib.rs
1//! Ares is an automatic decoding and cracking tool. https://github.com/bee-san/ares
2// Warns in case we forget to include documentation
3#![warn(
4 missing_docs,
5 clippy::missing_docs_in_private_items,
6 clippy::missing_errors_doc,
7 clippy::missing_panics_doc
8)]
9
10/// The main crate for the Ares project.
11/// This provides the library API interface for Ares.
12mod api_library_input_struct;
13/// Checkers is a module that contains the functions that check if the input is plaintext
14pub mod checkers;
15/// CLI Arg Parsing library
16pub mod cli;
17/// CLI Input Parser parses the input from the CLI and returns a struct.
18mod cli_input_parser;
19/// CLI Pretty Printing module for consistent output formatting
20///
21/// # Examples
22/// ```
23/// use ares::cli_pretty_printing::{success, warning};
24///
25/// // Print a success message
26/// let success_msg = success("Operation completed successfully");
27/// assert!(!success_msg.is_empty());
28///
29/// // Print a warning message
30/// let warning_msg = warning("Please check your input");
31/// assert!(!warning_msg.is_empty());
32/// ```
33pub mod cli_pretty_printing;
34/// The Config module enables a configuration module
35/// Like a global API to access config details
36pub mod config;
37/// Decoders are the functions that actually perform the decodings.
38pub mod decoders;
39/// The filtration system builds what decoders to use at runtime
40/// By default it will use them all.
41mod filtration_system;
42/// The searcher is the thing which searches for the plaintext
43/// It is the core of the program.
44mod searchers;
45/// Storage module for dictionaries and invisible characters
46pub mod storage;
47/// Timer for internal use
48mod timer;
49
50use checkers::{
51 athena::Athena,
52 checker_result::CheckResult,
53 checker_type::{Check, Checker},
54 wait_athena::WaitAthena,
55};
56use log::debug;
57
58use crate::{
59 config::{get_config, Config},
60 decoders::interface::Decoder,
61};
62
63use self::decoders::crack_results::CrackResult;
64
65/// The main function to call which performs the cracking.
66/// ```rust
67/// use ares::perform_cracking;
68/// use ares::config::Config;
69/// let mut config = Config::default();
70/// // You can set the config to your liking using the Config struct
71/// // Just edit the data like below if you want:
72/// config.timeout = 5;
73/// config.human_checker_on = false;
74/// config.verbose = 0;
75/// let result = perform_cracking("VGhlIG1haW4gZnVuY3Rpb24gdG8gY2FsbCB3aGljaCBwZXJmb3JtcyB0aGUgY3JhY2tpbmcu", config);
76/// assert!(true);
77/// // The result is an Option<DecoderResult> so we need to unwrap it
78/// // The DecoderResult contains the text and the path
79/// // The path is a vector of CrackResults which contains the decoder used and the keys used
80/// // The text is a vector of strings because some decoders return more than 1 text (Caesar)
81/// // Becuase the program has returned True, the first result is the plaintext (and it will only have 1 result).
82/// // This is some tech debt we need to clean up https://github.com/bee-san/Ares/issues/130
83/// assert!(result.unwrap().text[0] == "The main function to call which performs the cracking.");
84/// ```
85/// The human checker defaults to off in the config, but it returns the first thing it finds currently.
86/// We have an issue for that here https://github.com/bee-san/Ares/issues/129
87/// ```rust
88/// use ares::perform_cracking;
89/// use ares::config::Config;
90/// let mut config = Config::default();
91/// // You can set the config to your liking using the Config struct
92/// // Just edit the data like below if you want:
93/// config.timeout = 0;
94/// let result = perform_cracking("VGhlIG1haW4gZnVuY3Rpb24gdG8gY2FsbCB3aGljaCBwZXJmb3JtcyB0aGUgY3JhY2tpbmcu", config);
95/// assert!(true);
96/// // If the program times out, or it cannot decode the text it will return None.
97/// assert!(result.is_none());
98/// ```
99pub fn perform_cracking(text: &str, config: Config) -> Option<DecoderResult> {
100 // If top_results is enabled, ensure human_checker_on is disabled
101 let mut modified_config = config;
102 if modified_config.top_results {
103 modified_config.human_checker_on = false;
104 // Clear any previous results when starting a new cracking session
105 storage::wait_athena_storage::clear_plaintext_results();
106 }
107
108 config::set_global_config(modified_config);
109 let text = text.to_string();
110
111 let initial_check_for_plaintext = check_if_input_text_is_plaintext(&text);
112 if initial_check_for_plaintext.is_identified {
113 debug!(
114 "The input text provided to the program {} is the plaintext. Returning early.",
115 text
116 );
117 cli_pretty_printing::return_early_because_input_text_is_plaintext();
118
119 let mut crack_result = CrackResult::new(&Decoder::default(), text.to_string());
120 crack_result.checker_name = initial_check_for_plaintext.checker_name;
121
122 let output = DecoderResult {
123 text: vec![text],
124 path: vec![crack_result],
125 };
126
127 return Some(output);
128 }
129
130 // Build a new search tree
131 // This starts us with a node with no parents
132 // let search_tree = searchers::Tree::new(text.to_string());
133 cli_pretty_printing::success(&format!(
134 "DEBUG: lib.rs - Calling search_for_plaintext with text: {}",
135 text
136 ));
137 // Perform the search algorithm
138 // It will either return a failure or success.
139 let result = searchers::search_for_plaintext(text);
140 cli_pretty_printing::success(&format!(
141 "DEBUG: lib.rs - Result from search_for_plaintext: {:?}",
142 result.is_some()
143 ));
144 if let Some(ref res) = result {
145 cli_pretty_printing::success(&format!(
146 "DEBUG: lib.rs - Result has {} decoders in path",
147 res.path.len()
148 ));
149 }
150 result
151}
152
153/// Checks if the given input is plaintext or not
154/// Used at the start of the program to not waste CPU cycles
155fn check_if_input_text_is_plaintext(text: &str) -> CheckResult {
156 let config = get_config();
157
158 if config.top_results {
159 let wait_athena_checker = Checker::<WaitAthena>::new();
160 wait_athena_checker.check(text)
161 } else {
162 let athena_checker = Checker::<Athena>::new();
163 athena_checker.check(text)
164 }
165}
166
167/// DecoderResult is the result of decoders
168#[derive(Debug)]
169pub struct DecoderResult {
170 /// The text we have from the decoder, as a vector
171 /// because the decoder might return more than 1 text (caesar)
172 pub text: Vec<String>,
173 /// The list of decoders we have so far
174 /// The CrackResult contains more than just each decoder, such as the keys used
175 /// or the checkers used.
176 pub path: Vec<CrackResult>,
177}
178
179/// Creates a default DecoderResult with Default as the text / path
180impl Default for DecoderResult {
181 fn default() -> Self {
182 DecoderResult {
183 text: vec!["Default".to_string()],
184 path: vec![CrackResult::new(&Decoder::default(), "Default".to_string())],
185 }
186 }
187}
188
189/// Lets us create a new decoderResult with given text
190impl DecoderResult {
191 /// It's only used in tests so it thinks its dead code
192 fn _new(text: &str) -> Self {
193 DecoderResult {
194 text: vec![text.to_string()],
195 path: vec![CrackResult::new(&Decoder::default(), "Default".to_string())],
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::perform_cracking;
203 use crate::config::Config;
204
205 #[test]
206 fn test_perform_cracking_returns() {
207 let config = Config::default();
208 perform_cracking("SGVscCBJIG5lZWQgc29tZWJvZHkh", config);
209 }
210
211 #[test]
212 fn test_perform_cracking_returns_failure() {
213 let config = Config::default();
214 let result = perform_cracking("", config);
215 assert!(result.is_none());
216 }
217
218 #[test]
219 fn test_perform_cracking_returns_successful_base64_reverse() {
220 let config = Config::default();
221 let result = perform_cracking("aGVsbG8gdGhlcmUgZ2VuZXJhbA==", config);
222 assert!(result.is_some());
223 assert!(result.unwrap().text[0] == "hello there general")
224 }
225
226 #[test]
227 fn test_early_exit_if_input_is_plaintext() {
228 let config = Config::default();
229 let result = perform_cracking("192.168.0.1", config);
230 // Since we are exiting early the path should be of length 1, which is 1 check (the Athena check)
231 assert!(result.unwrap().path.len() == 1);
232 }
233
234 #[ignore]
235 #[test]
236 // Previously this would decode to `Fchohs as 13 dzoqsg!` because the English checker wasn't that good
237 // This test makes sure we can decode it okay
238 // TODO: Skipping this test because the English checker still isn't good.
239 fn test_successfully_decode_caesar() {
240 let config = Config::default();
241 let result = perform_cracking("Ebgngr zr 13 cynprf!", config);
242 // We return None since the input is the plaintext
243 assert!(result.unwrap().text[0] == "Rotate me 13 places!");
244 }
245
246 #[test]
247 fn test_successfully_inputted_plaintext() {
248 let config = Config::default();
249 let result = perform_cracking("Hello, World!", config);
250 // We return None since the input is the plaintext
251 let res_unwrapped = result.unwrap();
252 assert!(&res_unwrapped.text[0] == "Hello, World!");
253 // Since our input is the plaintext we did not decode it
254 // Therefore we return with the default decoder
255 assert!(res_unwrapped.path[0].decoder == "Default decoder");
256 }
257}