ares/decoders/
base64_url_decoder.rs

1///! Decode a base64_url string
2///! Performs error handling and returns a string
3///! Call base64_url_decoder.crack to use. It returns option<String> and check with
4///! `result.is_some()` to see if it returned okay.
5///
6use crate::checkers::CheckerTypes;
7use crate::decoders::interface::check_string_success;
8
9use super::crack_results::CrackResult;
10use super::interface::Crack;
11use super::interface::Decoder;
12
13use log::{debug, info, trace};
14
15/// The base64_url decoder, call:
16/// `let base64_url_decoder = Decoder::<Base64URLDecoder>::new()` to create a new instance
17/// And then call:
18/// `result = base64_url_decoder.crack(input)` to decode a base64_url string
19/// The struct generated by new() comes from interface.rs
20/// ```
21/// use ares::decoders::base64_url_decoder::{Base64URLDecoder};
22/// use ares::decoders::interface::{Crack, Decoder};
23/// use ares::checkers::{athena::Athena, CheckerTypes, checker_type::{Check, Checker}};
24///
25/// let decode_base64_url = Decoder::<Base64URLDecoder>::new();
26/// let athena_checker = Checker::<Athena>::new();
27/// let checker = CheckerTypes::CheckAthena(athena_checker);
28///
29/// let result = decode_base64_url.crack("aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8_ZXhhbXBsZT10ZXN0", &checker).unencrypted_text;
30/// assert!(result.is_some());
31/// assert_eq!(result.unwrap()[0], "https://www.google.com/?example=test");
32/// ```
33pub struct Base64URLDecoder;
34
35impl Crack for Decoder<Base64URLDecoder> {
36    fn new() -> Decoder<Base64URLDecoder> {
37        Decoder {
38            name: "Base64 URL",
39            description: "Modified Base64 for URL variants exist (such as base64url in RFC 4648), where the '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_', so that using URL encoders/decoders is no longer necessary.",
40            link: "https://en.wikipedia.org/wiki/Base64#URL_applications",
41            tags: vec!["base64_url", "base64", "url", "decoder", "base"],
42            popularity: 0.9,
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 base64_url with text {:?}", text);
52        let decoded_text = decode_base64_url_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 base64_url because Base64URLDecoder::decode_base64_url_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 base64_url 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_base64_url_no_error_handling(text: &str) -> Option<String> {
88    // Runs the code to decode base64_url
89    // Doesn't perform error handling, call from_base64_url
90    base64::decode_engine(
91        text.as_bytes(),
92        &base64::engine::fast_portable::FastPortable::from(
93            &base64::alphabet::URL_SAFE,
94            base64::engine::fast_portable::PAD,
95        ),
96    )
97    .ok()
98    .map(|inner| String::from_utf8(inner).ok())?
99}
100
101#[cfg(test)]
102mod tests {
103    use super::Base64URLDecoder;
104    use crate::{
105        checkers::{
106            athena::Athena,
107            checker_type::{Check, Checker},
108            CheckerTypes,
109        },
110        decoders::interface::{Crack, Decoder},
111    };
112
113    // helper for tests
114    fn get_athena_checker() -> CheckerTypes {
115        let athena_checker = Checker::<Athena>::new();
116        CheckerTypes::CheckAthena(athena_checker)
117    }
118
119    #[test]
120    fn base64_url_decodes_successfully() {
121        // This tests if Base64 URL can decode Base64 URL successfully
122        // Regular Base64 can't decode this string as it has "_" instead of "/"
123        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
124        let result = base64_url_decoder.crack(
125            "aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8_ZXhhbXBsZT10ZXN0",
126            &get_athena_checker(),
127        );
128        assert_eq!(
129            result.unencrypted_text.unwrap()[0],
130            "https://www.google.com/?example=test"
131        );
132    }
133
134    #[test]
135    fn base64_url_decodes_regular_base64_successfully() {
136        // This tests if Base64 URL can decode regular Base64 successfully
137        // Base64 URL can decode Base64 strings if they don't have "+" or "/" in them
138        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
139        let result = base64_url_decoder.crack(
140            "VGhpcyBpcyBkZWNvZGFibGUgYnkgYm90aCBCYXNlNjQgYW5kIEJhc2U2NCBVUkw=",
141            &get_athena_checker(),
142        );
143        assert_eq!(
144            result.unencrypted_text.unwrap()[0],
145            "This is decodable by both Base64 and Base64 URL"
146        );
147    }
148
149    #[test]
150    fn base64_url_handles_regular_base64_with_plus_signs() {
151        // This tests if Base64 URL can handle regular Base64 with plus signs
152        // Base64 URL can't decode Base64 strings that have "+" in them as it's replaced with "-"
153        // It should return None
154        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
155        let result = base64_url_decoder
156            .crack(
157                "VGhpcyBpc24ndCA+Pj4+IGRlY29kYWJsZSBieSBCYXNlNjQgVVJM",
158                &get_athena_checker(),
159            )
160            .unencrypted_text;
161        assert!(result.is_none());
162    }
163
164    #[test]
165    fn base64_url_handles_regular_base64_with_slashes() {
166        // This tests if Base64 URL can handle regular Base64 with slashes
167        // Base64 URL can't decode Base64 strings that have "/" in them as it's replaced with "_"
168        // It should return None
169        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
170        let result = base64_url_decoder
171            .crack(
172                "aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8/ZXhhbXBsZT10ZXN0",
173                &get_athena_checker(),
174            )
175            .unencrypted_text;
176        assert!(result.is_none());
177    }
178
179    #[test]
180    fn base64_url_handles_panics() {
181        // This tests if Base64 URL can handle panics
182        // It should return None
183        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
184        let result = base64_url_decoder
185            .crack(
186                "hello my name is panicky mc panic face!",
187                &get_athena_checker(),
188            )
189            .unencrypted_text;
190        assert!(result.is_none());
191    }
192
193    #[test]
194    fn base64_url_handles_panic_if_empty_string() {
195        // This tests if Base64 URL can handle an empty string
196        // It should return None
197        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
198        let result = base64_url_decoder
199            .crack("", &get_athena_checker())
200            .unencrypted_text;
201        assert!(result.is_none());
202    }
203
204    #[test]
205    fn base64_url_handles_panic_if_emoji() {
206        // This tests if Base64 URL can handle an emoji
207        // It should return None
208        let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
209        let result = base64_url_decoder
210            .crack("😂", &get_athena_checker())
211            .unencrypted_text;
212        assert!(result.is_none());
213    }
214}