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 Input Parser parses the input from the CLI and returns a struct.
16mod cli_input_parser;
17/// The CLI Pretty Printing module contains the functions that print the results
18pub mod cli_pretty_printing;
19/// The Config module enables a configuration module
20/// Like a global API to access config details
21pub mod config;
22/// Decoders are the functions that actually perform the decodings.
23pub mod decoders;
24/// The filtration system builds what decoders to use at runtime
25/// By default it will use them all.
26mod filtration_system;
27/// The searcher is the thing which searches for the plaintext
28/// It is the core of the program.
29mod searchers;
30/// The storage module contains all the dictionaries and provides
31/// storage of data to our decoderrs and checkers.
32mod storage;
33/// Timer for internal use
34mod timer;
35
36use checkers::{
37 athena::Athena,
38 checker_result::CheckResult,
39 checker_type::{Check, Checker},
40};
41use log::debug;
42
43use crate::{config::Config, decoders::interface::Decoder};
44
45use self::decoders::crack_results::CrackResult;
46/// The main function to call which performs the cracking.
47/// ```rust
48/// use ares::perform_cracking;
49/// use ares::config::Config;
50/// let mut config = Config::default();
51/// // You can set the config to your liking using the Config struct
52/// // Just edit the data like below if you want:
53/// config.timeout = 5;
54/// config.human_checker_on = false;
55/// config.verbose = 0;
56/// let result = perform_cracking("VGhlIG1haW4gZnVuY3Rpb24gdG8gY2FsbCB3aGljaCBwZXJmb3JtcyB0aGUgY3JhY2tpbmcu", config);
57/// assert!(true);
58/// // The result is an Option<DecoderResult> so we need to unwrap it
59/// // The DecoderResult contains the text and the path
60/// // The path is a vector of CrackResults which contains the decoder used and the keys used
61/// // The text is a vector of strings because some decoders return more than 1 text (Caesar)
62/// // Becuase the program has returned True, the first result is the plaintext (and it will only have 1 result).
63/// // This is some tech debt we need to clean up https://github.com/bee-san/Ares/issues/130
64/// assert!(result.unwrap().text[0] == "The main function to call which performs the cracking.");
65/// ```
66/// The human checker defaults to off in the config, but it returns the first thing it finds currently.
67/// We have an issue for that here https://github.com/bee-san/Ares/issues/129
68/// ```rust
69/// use ares::perform_cracking;
70/// use ares::config::Config;
71/// let mut config = Config::default();
72/// // You can set the config to your liking using the Config struct
73/// // Just edit the data like below if you want:
74/// config.timeout = 0;
75/// let result = perform_cracking("VGhlIG1haW4gZnVuY3Rpb24gdG8gY2FsbCB3aGljaCBwZXJmb3JtcyB0aGUgY3JhY2tpbmcu", config);
76/// assert!(true);
77/// // If the program times out, or it cannot decode the text it will return None.
78/// assert!(result.is_none());
79/// ```
80pub fn perform_cracking(text: &str, config: Config) -> Option<DecoderResult> {
81 config::set_global_config(config);
82 let initial_check_for_plaintext = check_if_input_text_is_plaintext(text);
83 if initial_check_for_plaintext.is_identified {
84 debug!(
85 "The input text provided to the program {} is the plaintext. Returning early.",
86 text
87 );
88 cli_pretty_printing::return_early_because_input_text_is_plaintext();
89
90 let mut crack_result = CrackResult::new(&Decoder::default(), (&text).to_string());
91 crack_result.checker_name = initial_check_for_plaintext.checker_name;
92
93 let output = DecoderResult {
94 text: vec![(&text).to_string()],
95 path: vec![crack_result],
96 };
97
98 return Some(output);
99 }
100
101 // Build a new search tree
102 // This starts us with a node with no parents
103 // let search_tree = searchers::Tree::new(text.to_string());
104 // Perform the search algorithm
105 // It will either return a failure or success.
106 searchers::search_for_plaintext(text)
107}
108
109/// Checks if the given input is plaintext or not
110/// Used at the start of the program to not waste CPU cycles
111fn check_if_input_text_is_plaintext(text: &str) -> CheckResult {
112 let athena_checker = Checker::<Athena>::new();
113 athena_checker.check(text)
114}
115
116/// DecoderResult is the result of decoders
117#[derive(Debug)]
118pub struct DecoderResult {
119 /// The text we have from the decoder, as a vector
120 /// because the decoder might return more than 1 text (caesar)
121 pub text: Vec<String>,
122 /// The list of decoders we have so far
123 /// The CrackResult contains more than just each decoder, such as the keys used
124 /// or the checkers used.
125 pub path: Vec<CrackResult>,
126}
127
128/// Creates a default DecoderResult with Default as the text / path
129impl Default for DecoderResult {
130 fn default() -> Self {
131 DecoderResult {
132 text: vec!["Default".to_string()],
133 path: vec![CrackResult::new(&Decoder::default(), "Default".to_string())],
134 }
135 }
136}
137
138/// Lets us create a new decoderResult with given text
139impl DecoderResult {
140 /// It's only used in tests so it thinks its dead code
141 fn _new(text: &str) -> Self {
142 DecoderResult {
143 text: vec![text.to_string()],
144 path: vec![CrackResult::new(&Decoder::default(), "Default".to_string())],
145 }
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::perform_cracking;
152 use crate::config::Config;
153
154 #[test]
155 fn test_perform_cracking_returns() {
156 let config = Config::default();
157 perform_cracking("SGVscCBJIG5lZWQgc29tZWJvZHkh", config);
158 }
159
160 #[test]
161 fn test_perform_cracking_returns_successful() {
162 // this will work after english checker can identify "CANARY: hello"
163 // let result = perform_cracking("Q0FOQVJZOiBoZWxsbw==");
164 // assert!(result.is_some());
165 // assert!(result.unwrap() == "CANARY: hello")
166 let config = Config::default();
167 let result = perform_cracking("b2xsZWg=", config);
168 assert!(result.is_some());
169 assert!(result.unwrap().text[0] == "hello");
170 }
171 #[test]
172 fn test_perform_cracking_returns_failure() {
173 let config = Config::default();
174 let result = perform_cracking("", config);
175 assert!(result.is_none());
176 }
177
178 #[test]
179 fn test_perform_cracking_returns_successful_base64_reverse() {
180 let config = Config::default();
181 let result = perform_cracking("aGVsbG8gdGhlcmUgZ2VuZXJhbA==", config);
182 assert!(result.is_some());
183 assert!(result.unwrap().text[0] == "hello there general")
184 }
185
186 #[test]
187 fn test_early_exit_if_input_is_plaintext() {
188 let config = Config::default();
189 let result = perform_cracking("192.168.0.1", config);
190 // Since we are exiting early the path should be of length 1, which is 1 check (the Athena check)
191 assert!(result.unwrap().path.len() == 1);
192 }
193 #[test]
194 // Previously this would decode to `Fchohs as 13 dzoqsg!` because the English checker wasn't that good
195 // This test makes sure we can decode it okay
196 fn test_successfully_decode_caesar() {
197 let config = Config::default();
198 let result = perform_cracking("Ebgngr zr 13 cynprf!", config);
199 // We return None since the input is the plaintext
200 assert!(result.unwrap().text[0] == "Rotate me 13 places!");
201 }
202
203 #[test]
204 fn test_successfully_inputted_plaintext() {
205 let config = Config::default();
206 let result = perform_cracking("Hello, World!", config);
207 // We return None since the input is the plaintext
208 let res_unwrapped = result.unwrap();
209 assert!(&res_unwrapped.text[0] == "Hello, World!");
210 // Since our input is the plaintext we did not decode it
211 // Therefore we return with the default decoder
212 assert!(res_unwrapped.path[0].decoder == "Default decoder");
213 }
214}