use crate::checkers::CheckerTypes;
use crate::decoders::interface::check_string_success;
use super::crack_results::CrackResult;
use super::interface::Crack;
use super::interface::Decoder;
use log::{debug, info, trace};
pub struct HexadecimalDecoder;
#[derive(Debug)]
enum Error {
InvalidLength,
InvalidUtf8,
}
impl Crack for Decoder<HexadecimalDecoder> {
fn new() -> Decoder<HexadecimalDecoder> {
Decoder {
name: "Hexadecimal",
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.",
link: "https://en.wikipedia.org/wiki/Hexadecimal#Base16_(transfer_encoding)",
tags: vec!["hexadecimal", "hex", "base", "decoder"],
popularity: 1.0,
phantom: std::marker::PhantomData,
}
}
fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
trace!("Trying hexadecimal with text {:?}", text);
let decoded_text: Result<String, Error> = hexadecimal_to_string(text);
let mut results = CrackResult::new(self, text.to_string());
if decoded_text.is_err() {
debug!("Failed to decode hexadecimal: {:?}", decoded_text);
return results;
}
trace!("Decoded text for hexadecimal: {:?}", decoded_text);
let decoded_text = decoded_text.unwrap();
if !check_string_success(&decoded_text, text) {
info!(
"Failed to decode hexadecimal because check_string_success returned false on string {}",
decoded_text
);
return results;
}
let checker_result = checker.check(&decoded_text);
results.unencrypted_text = Some(vec![decoded_text]);
results.update_checker(&checker_result);
results
}
fn get_tags(&self) -> &Vec<&str> {
&self.tags
}
fn get_name(&self) -> &str {
self.name
}
}
fn hexadecimal_to_string(hex: &str) -> Result<String, Error> {
let hex = hex.replace("0x", "");
let hex = hex.replace(|c: char| !c.is_ascii_hexdigit(), "");
let bytes = hex.as_bytes();
if bytes.len() % 2 == 1 {
return Err(Error::InvalidLength);
}
let mut result = String::new();
for pair in bytes.chunks(2) {
result.push(u8::from_str_radix(std::str::from_utf8(pair).unwrap(), 16).unwrap() as char);
}
String::from_utf8(result.into()).map_err(|_| Error::InvalidUtf8)
}
#[cfg(test)]
mod tests {
use super::HexadecimalDecoder;
use crate::{
checkers::{
athena::Athena,
checker_type::{Check, Checker},
CheckerTypes,
},
decoders::interface::{Crack, Decoder},
};
fn get_athena_checker() -> CheckerTypes {
let athena_checker = Checker::<Athena>::new();
CheckerTypes::CheckAthena(athena_checker)
}
#[test]
fn hexadecimal_with_no_spaces_decodes_successfully() {
let decoder = Decoder::<HexadecimalDecoder>::new();
let result = decoder.crack(
"537068696e78206f6620626c61636b2071756172747a2c206a75646765206d7920766f772e",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"Sphinx of black quartz, judge my vow."
);
}
#[test]
fn hexadecimal_with_spaces_decodes_successfully() {
let decoder = Decoder::<HexadecimalDecoder>::new();
let result = decoder.crack(
"68 65 78 61 64 65 63 69 6d 61 6c 20 6f 72 20 62 61 73 65 31 36 3f",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"hexadecimal or base16?"
);
}
#[test]
fn hexadecimal_with_delimiters_decodes_successfully() {
let decoder = Decoder::<HexadecimalDecoder>::new();
let result = decoder.crack(
"68;74;74;70;73;3a;2f;2f;77;77;77;2e;67;6f;6f;67;6c;65;2e;63;6f;6d",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"https://www.google.com"
);
}
#[test]
fn uppercase_hexadecimal_decodes_successfully() {
let decoder = Decoder::<HexadecimalDecoder>::new();
let result = decoder.crack(
"5570706572636173652068657861646563696D616C",
&get_athena_checker(),
);
assert_eq!(result.unencrypted_text.unwrap()[0], "Uppercase hexadecimal");
}
#[test]
fn hexadecimal_with_0x_delimiters_decodes_successfully() {
let decoder = Decoder::<HexadecimalDecoder>::new();
let result = decoder.crack(
"0x540x680x690x730x200x750x730x650x730x200x300x780x200x610x730x200x740x680x650x200x700x720x650x660x690x780x200x620x650x740x770x650x650x6e0x200x650x760x650x720x790x200x630x680x750x6e0x6b",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"This uses 0x as the prefix between every chunk"
);
}
#[test]
fn hexadecimal_with_0x_and_comma_delimiters_decodes_successfully() {
let decoder = Decoder::<HexadecimalDecoder>::new();
let result = decoder.crack(
"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",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"Hexadecimal with 0x + commas"
);
}
#[test]
fn hexadecimal_handles_panics() {
let hexadecimal_decoder = Decoder::<HexadecimalDecoder>::new();
let result = hexadecimal_decoder
.crack(
"hello my name is panicky mc panic face!",
&get_athena_checker(),
)
.unencrypted_text;
assert!(result.is_some());
}
#[test]
fn hexadecimal_handles_panic_if_empty_string() {
let citrix_ctx1_decoder = Decoder::<HexadecimalDecoder>::new();
let result = citrix_ctx1_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn hexadecimal_handles_panic_if_emoji() {
let base64_url_decoder = Decoder::<HexadecimalDecoder>::new();
let result = base64_url_decoder
.crack("😂", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
}