ares/decoders/
base91_decoder.rs

1use crate::checkers::CheckerTypes;
2use crate::decoders::interface::check_string_success;
3
4use super::crack_results::CrackResult;
5///! Decodes a base91 string
6///! Performs error handling and returns a string
7///! Call base91_decoder.crack to use. It returns option<String> and check with
8///! `result.is_some()` to see if it returned okay.
9///
10use super::interface::Crack;
11use super::interface::Decoder;
12
13use log::{debug, info, trace};
14
15/// The Base91 decoder, call:
16/// `let base91_decoder = Decoder::<Base91Decoder>::new()` to create a new instance
17/// And then call:
18/// `result = base91_decoder.crack(input)` to decode a base91 string
19/// The struct generated by new() comes from interface.rs
20/// ```
21/// use ares::decoders::base91_decoder::{Base91Decoder};
22/// use ares::decoders::interface::{Crack, Decoder};
23/// use ares::checkers::{athena::Athena, CheckerTypes, checker_type::{Check, Checker}};
24///
25/// let decode_base91 = Decoder::<Base91Decoder>::new();
26/// let athena_checker = Checker::<Athena>::new();
27/// let checker = CheckerTypes::CheckAthena(athena_checker);
28///
29/// let result = decode_base91.crack("TPwJh>Io2Tv!lE", &checker).unencrypted_text;
30/// assert!(result.is_some());
31/// assert_eq!(result.unwrap()[0], "hello world");
32/// ```
33pub struct Base91Decoder;
34
35impl Crack for Decoder<Base91Decoder> {
36    fn new() -> Decoder<Base91Decoder> {
37        Decoder {
38            name: "Base91",
39            description: "basE91 is an advanced method for encoding binary data as ASCII characters. It is similar to UUencode or base64, but is more efficient.",
40            link: "https://base91.sourceforge.net/",
41            tags: vec!["base91", "decoder", "base"],
42            popularity: 0.3,
43            phantom: std::marker::PhantomData,
44        }
45    }
46
47    /// This function does the actual decoding
48    /// It returns an Option<string> if it was successful
49    /// Else the Option returns nothing and the error is logged in Trace
50    fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
51        trace!("Trying Base91 with text {:?}", text);
52        let decoded_text = decode_base91_no_error_handling(text);
53        let mut results = CrackResult::new(self, text.to_string());
54
55        if decoded_text.is_none() {
56            debug!("Failed to decode base91 because Base91Decoder::decode_base91_no_error_handling returned None");
57            return results;
58        }
59
60        let decoded_text = decoded_text.unwrap();
61        if !check_string_success(&decoded_text, text) {
62            info!(
63                "Failed to decode base91 because check_string_success returned false on string {}",
64                decoded_text
65            );
66            return results;
67        }
68
69        let checker_result = checker.check(&decoded_text);
70        results.unencrypted_text = Some(vec![decoded_text]);
71
72        results.update_checker(&checker_result);
73
74        results
75    }
76    /// Gets all tags for this decoder
77    fn get_tags(&self) -> &Vec<&str> {
78        &self.tags
79    }
80    /// Gets the name for the current decoder
81    fn get_name(&self) -> &str {
82        self.name
83    }
84}
85
86/// helper function
87fn decode_base91_no_error_handling(text: &str) -> Option<String> {
88    // Runs the code to decode base91
89    // Doesn't perform error handling, call from_base91
90    let decoded_text = base91::slice_decode(text.as_bytes());
91    return Some(String::from_utf8_lossy(&decoded_text).to_string());
92}
93
94#[cfg(test)]
95mod tests {
96    use super::Base91Decoder;
97    use crate::{
98        checkers::{
99            athena::Athena,
100            checker_type::{Check, Checker},
101            CheckerTypes,
102        },
103        decoders::interface::{Crack, Decoder},
104    };
105
106    // helper for tests
107    fn get_athena_checker() -> CheckerTypes {
108        let athena_checker = Checker::<Athena>::new();
109        CheckerTypes::CheckAthena(athena_checker)
110    }
111
112    #[test]
113    fn successful_decoding() {
114        let base91_decoder = Decoder::<Base91Decoder>::new();
115
116        let result = base91_decoder.crack("TPwJh>Io2Tv!lE", &get_athena_checker());
117        let decoded_str = &result
118            .unencrypted_text
119            .expect("No unencrypted text for base91");
120        assert_eq!(decoded_str[0], "hello world");
121    }
122
123    #[test]
124    fn base91_decode_empty_string() {
125        // Base91 returns an empty string, this is a valid base91 string
126        // but returns False on check_string_success
127        let base91_decoder = Decoder::<Base91Decoder>::new();
128        let result = base91_decoder
129            .crack("", &get_athena_checker())
130            .unencrypted_text;
131        assert!(result.is_none());
132    }
133
134    #[test]
135    fn base91_decode_handles_panics() {
136        let base91_decoder = Decoder::<Base91Decoder>::new();
137        let result = base91_decoder
138            .crack("😈", &get_athena_checker())
139            .unencrypted_text;
140        if result.is_some() {
141            panic!("Decode_base91 did not return an option with Some<t>.")
142        } else {
143            // If we get here, the test passed
144            // Because the base91_decoder.crack function returned None
145            // as it should do for the input
146            assert_eq!(true, true);
147        }
148    }
149
150    #[test]
151    fn base91_handle_panic_if_empty_string() {
152        let base91_decoder = Decoder::<Base91Decoder>::new();
153        let result = base91_decoder
154            .crack("", &get_athena_checker())
155            .unencrypted_text;
156        if result.is_some() {
157            assert_eq!(true, true);
158        }
159    }
160
161    #[test]
162    fn base91_work_if_string_not_base91() {
163        // You can base91 decode a string that is not base91
164        // This string decodes to:
165        // ```.ée¢
166        // (uÖ²```
167        // https://gchq.github.io/CyberChef/#recipe=From_Base91('A-Za-z0-9%2B/%3D',true)&input=aGVsbG8gZ29vZCBkYXkh
168        let base91_decoder = Decoder::<Base91Decoder>::new();
169        let result = base91_decoder
170            .crack("hello good day!", &get_athena_checker())
171            .unencrypted_text;
172        if result.is_some() {
173            assert_eq!(true, true);
174        }
175    }
176
177    #[test]
178    fn base91_handle_panic_if_emoji() {
179        let base91_decoder = Decoder::<Base91Decoder>::new();
180        let result = base91_decoder
181            .crack("😂", &get_athena_checker())
182            .unencrypted_text;
183        if result.is_some() {
184            assert_eq!(true, true);
185        }
186    }
187}