-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Hexadecimal Adds a hexadecimal decoder. * Clean up hexadecimal encoder * Added one last test * Add error propagation and 0x support * Adds error propagation * Adds support for 0x delimiters * Merges redundant tests * Adds 2 more tests Co-authored-by: autumn skerritt <github@skerritt.blog>
- Loading branch information
1 parent
e92f1b1
commit d28b2b6
Showing
3 changed files
with
239 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
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}; | ||
|
||
///! Hexadecimal Decoder | ||
pub struct HexadecimalDecoder; | ||
|
||
///! Error enum | ||
#[derive(Debug)] | ||
enum Error { | ||
///! Error when the input is not divisible by 2 | ||
InvalidLength, | ||
///! Error if the result isn't UTF-8 | ||
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"], | ||
expected_runtime: 0.01, | ||
expected_success: 1.0, | ||
failure_runtime: 0.01, | ||
normalised_entropy: vec![1.0, 10.0], | ||
popularity: 1.0, | ||
phantom: std::marker::PhantomData, | ||
} | ||
} | ||
|
||
/// This function does the actual decoding | ||
/// It returns an Option<string> if it was successful | ||
/// Else the Option returns nothing and the error is logged in Trace | ||
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 | ||
} | ||
} | ||
|
||
/// Decodes hexadecimal to string | ||
fn hexadecimal_to_string(hex: &str) -> Result<String, Error> { | ||
// Remove "0x" delimiters | ||
let hex = hex.replace("0x", ""); | ||
// Remove all non-hexadecimal characters from the string | ||
let hex = hex.replace(|c: char| !c.is_ascii_hexdigit(), ""); | ||
|
||
// Convert the hexadecimal string to a vector of bytes | ||
let bytes = hex.as_bytes(); | ||
|
||
// Ensure that the vector of bytes has an even length, so it can be processed in pairs | ||
if bytes.len() % 2 == 1 { | ||
return Err(Error::InvalidLength); | ||
} | ||
|
||
// Iterate over the vector of bytes in pairs | ||
let mut result = String::new(); | ||
for pair in bytes.chunks(2) { | ||
// Parse the pair of bytes as a hexadecimal number and push the corresponding | ||
// ASCII character to the result string | ||
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}, | ||
}; | ||
|
||
// helper for tests | ||
fn get_athena_checker() -> CheckerTypes { | ||
let athena_checker = Checker::<Athena>::new(); | ||
CheckerTypes::CheckAthena(athena_checker) | ||
} | ||
|
||
#[test] | ||
fn hexadecimal_with_no_spaces_decodes_successfully() { | ||
// This tests if Hexadecimal can decode Hexadecimal with no spaces 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() { | ||
// This tests if Hexadecimal can decode Hexadecimal with spaces successfully | ||
// We use the hex string from the "c4ptur3-th3-fl4g" THM room | ||
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() { | ||
// This tests if Hexadecimal can decode Hexadecimal with delimiters 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() { | ||
// This tests if Hexadecimal can decode uppercase Hexadecimal 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() { | ||
// This tests if Hexadecimal can decode Hexadecimal with 0x delimiters 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() { | ||
// This tests if Hexadecimal can decode Hexadecimal with 0x and comma delimiters 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() { | ||
// This tests if Hexadecimal can handle panics | ||
// It should return Some | ||
// This is because Hexadecimal can technically decode it, but it will be gibberish | ||
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() { | ||
// This tests if Hexadecimal can handle an empty string | ||
// It should return None | ||
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() { | ||
// This tests if Hexadecimal can handle an emoji | ||
// It should return None | ||
let base64_url_decoder = Decoder::<HexadecimalDecoder>::new(); | ||
let result = base64_url_decoder | ||
.crack("😂", &get_athena_checker()) | ||
.unencrypted_text; | ||
assert!(result.is_none()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters