ares/decoders/
hexadecimal_decoder.rs1use 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
10pub struct HexadecimalDecoder;
12
13#[derive(Debug)]
15enum Error {
16    InvalidLength,
18    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    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    fn get_tags(&self) -> &Vec<&str> {
68        &self.tags
69    }
70    fn get_name(&self) -> &str {
72        self.name
73    }
74}
75
76fn hexadecimal_to_string(hex: &str) -> Result<String, Error> {
78    let hex = hex.replace("0x", "");
80    let hex = hex.replace(|c: char| !c.is_ascii_hexdigit(), "");
82
83    let bytes = hex.as_bytes();
85
86    if bytes.len() % 2 == 1 {
88        return Err(Error::InvalidLength);
89    }
90
91    let mut result = String::new();
93    for pair in bytes.chunks(2) {
94        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    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        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        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        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        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        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        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        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        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        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}