Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Base64 URL #98

Merged
merged 2 commits into from
Nov 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions src/decoders/base64_url_decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
///! Decode a base64_url string
///! Performs error handling and returns a string
///! Call base64_url_decoder.crack to use. It returns option<String> and check with
///! `result.is_some()` to see if it returned okay.
///
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};

/// The base64_url decoder, call:
/// `let base64_url_decoder = Decoder::<Base64URLDecoder>::new()` to create a new instance
/// And then call:
/// `result = base64_url_decoder.crack(input)` to decode a base64_url string
/// The struct generated by new() comes from interface.rs
/// ```
/// use ares::decoders::base64_url_decoder::{Base64URLDecoder};
/// use ares::decoders::interface::{Crack, Decoder};
/// use ares::checkers::{athena::Athena, CheckerTypes, checker_type::{Check, Checker}};
///
/// let decode_base64_url = Decoder::<Base64URLDecoder>::new();
/// let athena_checker = Checker::<Athena>::new();
/// let checker = CheckerTypes::CheckAthena(athena_checker);
///
/// let result = decode_base64_url.crack("aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8_ZXhhbXBsZT10ZXN0", &checker).unencrypted_text;
/// assert!(result.is_some());
/// assert_eq!(result.unwrap(), "https://www.google.com/?example=test");
/// ```
pub struct Base64URLDecoder;

impl Crack for Decoder<Base64URLDecoder> {
fn new() -> Decoder<Base64URLDecoder> {
Decoder {
name: "base64_url",
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.",
link: "https://en.wikipedia.org/wiki/Base64#URL_applications",
tags: vec!["base64_url", "base64", "url", "decoder", "base"],
expected_runtime: 0.01,
expected_success: 1.0,
failure_runtime: 0.01,
normalised_entropy: vec![1.0, 10.0],
popularity: 0.9,
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 base64_url with text {:?}", text);
let decoded_text = decode_base64_url_no_error_handling(text);
let mut results = CrackResult::new(self, text.to_string());

if decoded_text.is_none() {
debug!("Failed to decode base64_url because Base64URLDecoder::decode_base64_url_no_error_handling returned None");
return results;
}

let decoded_text = decoded_text.unwrap();
if !check_string_success(&decoded_text, text) {
info!(
"Failed to decode base64_url because check_string_success returned false on string {}",
decoded_text
);
return results;
}

let checker_result = checker.check(&decoded_text);
results.unencrypted_text = Some(decoded_text);

results.update_checker(&checker_result);

results
}
}

/// helper function
fn decode_base64_url_no_error_handling(text: &str) -> Option<String> {
// Runs the code to decode base64_url
// Doesn't perform error handling, call from_base64_url
base64::decode_config(text.as_bytes(), base64::URL_SAFE)
.ok()
.map(|inner| String::from_utf8(inner).ok())?
}

#[cfg(test)]
mod tests {
use super::Base64URLDecoder;
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 base64_url_decodes_successfully() {
// This tests if Base64 URL can decode Base64 URL successfully
// Regular Base64 can't decode this string as it has "_" instead of "/"
SkeletalDemise marked this conversation as resolved.
Show resolved Hide resolved
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder.crack(
"aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8_ZXhhbXBsZT10ZXN0",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap(),
"https://www.google.com/?example=test"
);
}

#[test]
fn base64_url_decodes_regular_base64_successfully() {
// This tests if Base64 URL can decode regular Base64 successfully
// Base64 URL can decode Base64 strings if they don't have "+" or "/" in them
SkeletalDemise marked this conversation as resolved.
Show resolved Hide resolved
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder.crack(
"VGhpcyBpcyBkZWNvZGFibGUgYnkgYm90aCBCYXNlNjQgYW5kIEJhc2U2NCBVUkw=",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap(),
"This is decodable by both Base64 and Base64 URL"
);
}

#[test]
fn base64_url_handles_regular_base64_with_plus_signs() {
// This tests if Base64 URL can handle regular Base64 with plus signs
// Base64 URL can't decode Base64 strings that have "+" in them as it's replaced with "-"
// It should return None
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder
.crack(
"VGhpcyBpc24ndCA+Pj4+IGRlY29kYWJsZSBieSBCYXNlNjQgVVJM",
&get_athena_checker(),
)
.unencrypted_text;
assert!(result.is_none());
}

#[test]
fn base64_url_handles_regular_base64_with_slashes() {
// This tests if Base64 URL can handle regular Base64 with slashes
// Base64 URL can't decode Base64 strings that have "/" in them as it's replaced with "_"
// It should return None
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder
.crack(
"aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS8/ZXhhbXBsZT10ZXN0",
&get_athena_checker(),
)
.unencrypted_text;
assert!(result.is_none());
}

#[test]
fn base64_url_handles_panics() {
// This tests if Base64 URL can handle panics
// It should return None
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder
.crack(
"hello my name is panicky mc panic face!",
&get_athena_checker(),
)
.unencrypted_text;
assert!(result.is_none());
}

#[test]
fn base64_url_handles_panic_if_empty_string() {
// This tests if Base64 URL can handle an empty string
// It should return None
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}

#[test]
fn base64_url_handles_panic_if_emoji() {
// This tests if Base64 URL can handle an emoji
// It should return None
let base64_url_decoder = Decoder::<Base64URLDecoder>::new();
let result = base64_url_decoder
.crack("😂", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
}
2 changes: 2 additions & 0 deletions src/decoders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub mod base58_flickr_decoder;
/// The base64_decoder module decodes base64
/// It is public as we use it in some tests.
pub mod base64_decoder;
/// The base64_url_decoder module decodes base64 url
pub mod base64_url_decoder;
/// The citrix_ctx1_decoder module decodes citrix ctx1
pub mod citrix_ctx1_decoder;
/// The crack_results module defines the CrackResult
Expand Down
3 changes: 3 additions & 0 deletions src/filtration_system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use crate::decoders::base58_ripple_decoder::Base58RippleDecoder;
///! Given a filter object, return an array of decoders/crackers which have been filtered
///
use crate::decoders::base64_decoder::Base64Decoder;
use crate::decoders::base64_url_decoder::Base64URLDecoder;
use crate::decoders::caesar_decoder::CaesarDecoder;
use crate::decoders::citrix_ctx1_decoder::CitrixCTX1Decoder;
use crate::decoders::crack_results::CrackResult;
Expand Down Expand Up @@ -103,6 +104,7 @@ pub fn filter_and_get_decoders() -> Decoders {
let base58_ripple = Decoder::<Base58RippleDecoder>::new();
let base58_flickr = Decoder::<Base58FlickrDecoder>::new();
let base64 = Decoder::<Base64Decoder>::new();
let base64_url = Decoder::<Base64URLDecoder>::new();
let citrix_ctx1 = Decoder::<CitrixCTX1Decoder>::new();
let base32 = Decoder::<Base32Decoder>::new();
let reversedecoder = Decoder::<ReverseDecoder>::new();
Expand All @@ -116,6 +118,7 @@ pub fn filter_and_get_decoders() -> Decoders {
Box::new(base58_ripple),
Box::new(base58_flickr),
Box::new(base64),
Box::new(base64_url),
Box::new(citrix_ctx1),
Box::new(base32),
Box::new(reversedecoder),
Expand Down