ares/decoders/
hexadecimal_decoder.rs

1use crate::checkers::CheckerTypes;
2use crate::decoders::interface::check_string_success;
3
4use super::crack_results::CrackResult;
5use super::interface::Crack;
6use super::interface::Decoder;
7
8use log::{debug, info, trace};
9
10///! Hexadecimal Decoder
11pub struct HexadecimalDecoder;
12
13///! Error enum
14#[derive(Debug)]
15enum Error {
16    ///! Error when the input is not divisible by 2
17    InvalidLength,
18    ///! Error if the result isn't UTF-8
19    InvalidUtf8,
20}
21
22impl Crack for Decoder<HexadecimalDecoder> {
23    fn new() -> Decoder<HexadecimalDecoder> {
24        Decoder {
25            name: "Hexadecimal",
26            description: "Data is broken into 4-bit sequences, and each value (between 0 and 15 inclusively) is encoded using one of 16 symbols from the ASCII character set. Although any 16 symbols from the ASCII character set can be used, in practice the ASCII digits '0'–'9' and the letters 'A'–'F' (or the lowercase 'a'–'f') are always chosen in order to align with standard written notation for hexadecimal numbers.",
27            link: "https://en.wikipedia.org/wiki/Hexadecimal#Base16_(transfer_encoding)",
28            tags: vec!["hexadecimal", "hex", "base", "decoder"],
29            popularity: 1.0,
30            phantom: std::marker::PhantomData,
31        }
32    }
33
34    /// This function does the actual decoding
35    /// It returns an Option<string> if it was successful
36    /// Else the Option returns nothing and the error is logged in Trace
37    fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
38        trace!("Trying hexadecimal with text {:?}", text);
39        let decoded_text: Result<String, Error> = hexadecimal_to_string(text);
40        let mut results = CrackResult::new(self, text.to_string());
41
42        if decoded_text.is_err() {
43            debug!("Failed to decode hexadecimal: {:?}", decoded_text);
44            return results;
45        }
46
47        trace!("Decoded text for hexadecimal: {:?}", decoded_text);
48
49        let decoded_text = decoded_text.unwrap();
50
51        if !check_string_success(&decoded_text, text) {
52            info!(
53                "Failed to decode hexadecimal because check_string_success returned false on string {}",
54                decoded_text
55            );
56            return results;
57        }
58
59        let checker_result = checker.check(&decoded_text);
60        results.unencrypted_text = Some(vec![decoded_text]);
61
62        results.update_checker(&checker_result);
63
64        results
65    }
66    /// Gets all tags for this decoder
67    fn get_tags(&self) -> &Vec<&str> {
68        &self.tags
69    }
70    /// Gets the name for the current decoder
71    fn get_name(&self) -> &str {
72        self.name
73    }
74}
75
76/// Decodes hexadecimal to string
77fn hexadecimal_to_string(hex: &str) -> Result<String, Error> {
78    // Remove "0x" delimiters
79    let hex = hex.replace("0x", "");
80    // Remove all non-hexadecimal characters from the string
81    let hex = hex.replace(|c: char| !c.is_ascii_hexdigit(), "");
82
83    // Convert the hexadecimal string to a vector of bytes
84    let bytes = hex.as_bytes();
85
86    // Ensure that the vector of bytes has an even length, so it can be processed in pairs
87    if bytes.len() % 2 == 1 {
88        return Err(Error::InvalidLength);
89    }
90
91    // Iterate over the vector of bytes in pairs
92    let mut result = String::new();
93    for pair in bytes.chunks(2) {
94        // Parse the pair of bytes as a hexadecimal number and push the corresponding
95        // ASCII character to the result string
96        result.push(u8::from_str_radix(std::str::from_utf8(pair).unwrap(), 16).unwrap() as char);
97    }
98
99    String::from_utf8(result.into()).map_err(|_| Error::InvalidUtf8)
100}
101
102#[cfg(test)]
103mod tests {
104    use super::HexadecimalDecoder;
105    use crate::{
106        checkers::{
107            athena::Athena,
108            checker_type::{Check, Checker},
109            CheckerTypes,
110        },
111        decoders::interface::{Crack, Decoder},
112    };
113
114    // helper for tests
115    fn get_athena_checker() -> CheckerTypes {
116        let athena_checker = Checker::<Athena>::new();
117        CheckerTypes::CheckAthena(athena_checker)
118    }
119
120    #[test]
121    fn hexadecimal_with_no_spaces_decodes_successfully() {
122        // This tests if Hexadecimal can decode Hexadecimal with no spaces successfully
123        let decoder = Decoder::<HexadecimalDecoder>::new();
124        let result = decoder.crack(
125            "537068696e78206f6620626c61636b2071756172747a2c206a75646765206d7920766f772e",
126            &get_athena_checker(),
127        );
128        assert_eq!(
129            result.unencrypted_text.unwrap()[0],
130            "Sphinx of black quartz, judge my vow."
131        );
132    }
133
134    #[test]
135    fn hexadecimal_with_spaces_decodes_successfully() {
136        // This tests if Hexadecimal can decode Hexadecimal with spaces successfully
137        // We use the hex string from the "c4ptur3-th3-fl4g" THM room
138        let decoder = Decoder::<HexadecimalDecoder>::new();
139        let result = decoder.crack(
140            "68 65 78 61 64 65 63 69 6d 61 6c 20 6f 72 20 62 61 73 65 31 36 3f",
141            &get_athena_checker(),
142        );
143        assert_eq!(
144            result.unencrypted_text.unwrap()[0],
145            "hexadecimal or base16?"
146        );
147    }
148
149    #[test]
150    fn hexadecimal_with_delimiters_decodes_successfully() {
151        // This tests if Hexadecimal can decode Hexadecimal with delimiters successfully
152        let decoder = Decoder::<HexadecimalDecoder>::new();
153        let result = decoder.crack(
154            "68;74;74;70;73;3a;2f;2f;77;77;77;2e;67;6f;6f;67;6c;65;2e;63;6f;6d",
155            &get_athena_checker(),
156        );
157        assert_eq!(
158            result.unencrypted_text.unwrap()[0],
159            "https://www.google.com"
160        );
161    }
162
163    #[test]
164    fn uppercase_hexadecimal_decodes_successfully() {
165        // This tests if Hexadecimal can decode uppercase Hexadecimal successfully
166        let decoder = Decoder::<HexadecimalDecoder>::new();
167        let result = decoder.crack(
168            "5570706572636173652068657861646563696D616C",
169            &get_athena_checker(),
170        );
171        assert_eq!(result.unencrypted_text.unwrap()[0], "Uppercase hexadecimal");
172    }
173
174    #[test]
175    fn hexadecimal_with_0x_delimiters_decodes_successfully() {
176        // This tests if Hexadecimal can decode Hexadecimal with 0x delimiters successfully
177        let decoder = Decoder::<HexadecimalDecoder>::new();
178        let result = decoder.crack(
179            "0x540x680x690x730x200x750x730x650x730x200x300x780x200x610x730x200x740x680x650x200x700x720x650x660x690x780x200x620x650x740x770x650x650x6e0x200x650x760x650x720x790x200x630x680x750x6e0x6b",
180            &get_athena_checker(),
181        );
182        assert_eq!(
183            result.unencrypted_text.unwrap()[0],
184            "This uses 0x as the prefix between every chunk"
185        );
186    }
187
188    #[test]
189    fn hexadecimal_with_0x_and_comma_delimiters_decodes_successfully() {
190        // This tests if Hexadecimal can decode Hexadecimal with 0x and comma delimiters successfully
191        let decoder = Decoder::<HexadecimalDecoder>::new();
192        let result = decoder.crack(
193            "0x48,0x65,0x78,0x61,0x64,0x65,0x63,0x69,0x6d,0x61,0x6c,0x20,0x77,0x69,0x74,0x68,0x20,0x30,0x78,0x20,0x2b,0x20,0x63,0x6f,0x6d,0x6d,0x61,0x73",
194            &get_athena_checker(),
195        );
196        assert_eq!(
197            result.unencrypted_text.unwrap()[0],
198            "Hexadecimal with 0x + commas"
199        );
200    }
201
202    #[test]
203    fn hexadecimal_handles_panics() {
204        // This tests if Hexadecimal can handle panics
205        // It should return Some
206        // This is because Hexadecimal can technically decode it, but it will be gibberish
207        let hexadecimal_decoder = Decoder::<HexadecimalDecoder>::new();
208        let result = hexadecimal_decoder
209            .crack(
210                "hello my name is panicky mc panic face!",
211                &get_athena_checker(),
212            )
213            .unencrypted_text;
214        assert!(result.is_some());
215    }
216
217    #[test]
218    fn hexadecimal_handles_panic_if_empty_string() {
219        // This tests if Hexadecimal can handle an empty string
220        // It should return None
221        let citrix_ctx1_decoder = Decoder::<HexadecimalDecoder>::new();
222        let result = citrix_ctx1_decoder
223            .crack("", &get_athena_checker())
224            .unencrypted_text;
225        assert!(result.is_none());
226    }
227
228    #[test]
229    fn hexadecimal_handles_panic_if_emoji() {
230        // This tests if Hexadecimal can handle an emoji
231        // It should return None
232        let base64_url_decoder = Decoder::<HexadecimalDecoder>::new();
233        let result = base64_url_decoder
234            .crack("😂", &get_athena_checker())
235            .unencrypted_text;
236        assert!(result.is_none());
237    }
238}