Skip to content

Commit

Permalink
Add Base91 (#69)
Browse files Browse the repository at this point in the history
* Add Base91

Adds a Base91 decoder.

* fix base91

* cargo fmt

* merging once more

Co-authored-by: autumn skerritt <github@skerritt.blog>
  • Loading branch information
SkeletalDemise and bee-san authored Dec 12, 2022
1 parent d28b2b6 commit b809c2c
Show file tree
Hide file tree
Showing 7 changed files with 466,111 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ once_cell = "1.16.0"
text_io = "0.1.12"
data-encoding = "2.3.3"
bs58 = "0.4.0"
base91 = "0.1.0"
num = "0.4"
crossbeam = "0.8"
base65536 = "1.0.1"
Expand Down
183 changes: 183 additions & 0 deletions src/decoders/base91_decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
use crate::checkers::CheckerTypes;
use crate::decoders::interface::check_string_success;

use super::crack_results::CrackResult;
///! Decodes a base91 string
///! Performs error handling and returns a string
///! Call base91_decoder.crack to use. It returns option<String> and check with
///! `result.is_some()` to see if it returned okay.
///
use super::interface::Crack;
use super::interface::Decoder;

use log::{debug, info, trace};

/// The Base91 decoder, call:
/// `let base91_decoder = Decoder::<Base91Decoder>::new()` to create a new instance
/// And then call:
/// `result = base91_decoder.crack(input)` to decode a base91 string
/// The struct generated by new() comes from interface.rs
/// ```
/// use ares::decoders::base91_decoder::{Base91Decoder};
/// use ares::decoders::interface::{Crack, Decoder};
/// use ares::checkers::{athena::Athena, CheckerTypes, checker_type::{Check, Checker}};
///
/// let decode_base91 = Decoder::<Base91Decoder>::new();
/// let athena_checker = Checker::<Athena>::new();
/// let checker = CheckerTypes::CheckAthena(athena_checker);
///
/// let result = decode_base91.crack("TPwJh>Io2Tv!lE", &checker).unencrypted_text;
/// assert!(result.is_some());
/// assert_eq!(result.unwrap()[0], "hello world");
/// ```
pub struct Base91Decoder;

impl Crack for Decoder<Base91Decoder> {
fn new() -> Decoder<Base91Decoder> {
Decoder {
name: "base91",
description: "basE91 is an advanced method for encoding binary data as ASCII characters. It is similar to UUencode or base64, but is more efficient.",
link: "https://base91.sourceforge.net/",
tags: vec!["base91", "decoder", "base"],
expected_runtime: 0.01,
expected_success: 0.7,
failure_runtime: 0.01,
normalised_entropy: vec![1.0, 10.0],
popularity: 0.3,
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 Base91 with text {:?}", text);
let decoded_text = decode_base91_no_error_handling(text);
let mut results = CrackResult::new(self, text.to_string());

if decoded_text.is_none() {
debug!("Failed to decode base91 because Base91Decoder::decode_base91_no_error_handling returned None");
return results;
}

let decoded_text = decoded_text.unwrap();
if !check_string_success(&decoded_text, text) {
info!(
"Failed to decode base91 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
}
}

/// helper function
fn decode_base91_no_error_handling(text: &str) -> Option<String> {
// Runs the code to decode base91
// Doesn't perform error handling, call from_base91
let decoded_text = base91::slice_decode(text.as_bytes());
return Some(String::from_utf8_lossy(&decoded_text).to_string());
}

#[cfg(test)]
mod tests {
use super::Base91Decoder;
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 successful_decoding() {
let base91_decoder = Decoder::<Base91Decoder>::new();

let result = base91_decoder.crack("TPwJh>Io2Tv!lE", &get_athena_checker());
let decoded_str = &result
.unencrypted_text
.expect("No unencrypted text for base91");
assert_eq!(decoded_str[0], "hello world");
}

#[test]
fn base91_decode_empty_string() {
// Base91 returns an empty string, this is a valid base91 string
// but returns False on check_string_success
let base91_decoder = Decoder::<Base91Decoder>::new();
let result = base91_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}

#[test]
fn base91_decode_handles_panics() {
let base91_decoder = Decoder::<Base91Decoder>::new();
let result = base91_decoder
.crack("😈", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
panic!("Decode_base91 did not return an option with Some<t>.")
} else {
// If we get here, the test passed
// Because the base91_decoder.crack function returned None
// as it should do for the input
assert_eq!(true, true);
}
}

#[test]
fn base91_handle_panic_if_empty_string() {
let base91_decoder = Decoder::<Base91Decoder>::new();
let result = base91_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
assert_eq!(true, true);
}
}

#[test]
fn base91_work_if_string_not_base91() {
// You can base91 decode a string that is not base91
// This string decodes to:
// ```.ée¢
// (uÖ²```
// https://gchq.github.io/CyberChef/#recipe=From_Base91('A-Za-z0-9%2B/%3D',true)&input=aGVsbG8gZ29vZCBkYXkh
let base91_decoder = Decoder::<Base91Decoder>::new();
let result = base91_decoder
.crack("hello good day!", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
assert_eq!(true, true);
}
}

#[test]
fn base91_handle_panic_if_emoji() {
let base91_decoder = Decoder::<Base91Decoder>::new();
let result = base91_decoder
.crack("😂", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
assert_eq!(true, true);
}
}
}
2 changes: 2 additions & 0 deletions src/decoders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub mod base64_decoder;
pub mod base64_url_decoder;
/// The base65536 module decodes base65536
pub mod base65536_decoder;
/// The base91_decoder module decodes base91
pub mod base91_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 @@ -17,6 +17,7 @@ use crate::decoders::base58_ripple_decoder::Base58RippleDecoder;
use crate::decoders::base64_decoder::Base64Decoder;
use crate::decoders::base64_url_decoder::Base64URLDecoder;
use crate::decoders::base65536_decoder::Base65536Decoder;
use crate::decoders::base91_decoder::Base91Decoder;
use crate::decoders::caesar_decoder::CaesarDecoder;
use crate::decoders::citrix_ctx1_decoder::CitrixCTX1Decoder;
use crate::decoders::crack_results::CrackResult;
Expand Down Expand Up @@ -109,6 +110,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 base91 = Decoder::<Base91Decoder>::new();
let base64_url = Decoder::<Base64URLDecoder>::new();
let base65536 = Decoder::<Base65536Decoder>::new();
let citrix_ctx1 = Decoder::<CitrixCTX1Decoder>::new();
Expand All @@ -126,6 +128,7 @@ pub fn filter_and_get_decoders() -> Decoders {
Box::new(base58_ripple),
Box::new(base58_flickr),
Box::new(base64),
Box::new(base91),
Box::new(base64_url),
Box::new(base65536),
Box::new(citrix_ctx1),
Expand Down
3 changes: 2 additions & 1 deletion src/searchers/bfs.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::filtration_system::MyResults;
use crate::{cli_pretty_printing::decoded_how_many_times, config::get_config};
use crossbeam::{channel::bounded, select};
use log::{debug, trace};
use std::collections::HashSet;

use crate::{filtration_system::MyResults, timer, DecoderResult};
use crate::{timer, DecoderResult};

/// Breadth first search is our search algorithm
/// https://en.wikipedia.org/wiki/Breadth-first_search
Expand Down
Loading

0 comments on commit b809c2c

Please sign in to comment.