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}