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 ASCII85 decoding #244

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
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 @@ -35,6 +35,7 @@ regex = "1.8.3"
text_io = "0.1.12"

# Dependencies used for decoding
ascii85 = "0.2.1"
base64 = "0.21.2"
base65536 = "1.0.1"
base91 = "0.1.0"
Expand Down
176 changes: 176 additions & 0 deletions src/decoders/ascii85_decoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
///! Decode a ascii85 string
///! Performs error handling and returns a string
///! Call ascii85_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 ascii85;

use super::crack_results::CrackResult;
use super::interface::Crack;
use super::interface::Decoder;

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

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

impl Crack for Decoder<ASCII85Decoder> {
fn new() -> Decoder<ASCII85Decoder> {
Decoder {
name: "ASCII85",
description: "Ascii85, also called Base85, is a form of binary-to-text encoding that uses five ASCII characters to represent four bytes of binary data.",
gregorni marked this conversation as resolved.
Show resolved Hide resolved
link: "https://en.wikipedia.org/wiki/Ascii85",
tags: vec!["decoder", "ascii85"],
gregorni marked this conversation as resolved.
Show resolved Hide resolved
popularity: 1.0,
gregorni marked this conversation as resolved.
Show resolved Hide resolved
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 ASCII85 with text {:?}", text);
let decoded_text = decode_ascii85_no_error_handling(text);
let mut results = CrackResult::new(self, text.to_string());

if decoded_text.is_none() {
debug!("Failed to decode ascii85 because ASCII85Decoder::decode_ascii85_no_error_handling returned None");
return results;
}

let decoded_text = decoded_text.unwrap();
if !check_string_success(&decoded_text, text) {
info!(
"Failed to decode ascii85 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
}
/// Gets all tags for this decoder
fn get_tags(&self) -> &Vec<&str> {
&self.tags
}
/// Gets the name for the current decoder
fn get_name(&self) -> &str {
self.name
}
}

/// helper function
fn decode_ascii85_no_error_handling(text: &str) -> Option<String> {
// Runs the code to decode ascii85
// Doesn't perform error handling, call from_ascii85
ascii85::decode(text)
.ok()
.map(|inner| String::from_utf8(inner).ok())?
}

#[cfg(test)]
mod tests {
use super::ASCII85Decoder;
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]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add more tests for base85 vs ascii85. There should be tests to make sure this only decodes ascii85 and not base85. Also add tests with longer strings and special characters in the plaintext. See the base64 URL tests for an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you give me a base85 string that isn't ascii85? All the online decoders seems to use the same encoding mechanism for both.

fn successful_decoding() {
let ascii85_decoder = Decoder::<ASCII85Decoder>::new();

let result = ascii85_decoder.crack("BOu!rD]j7BEbo7", &get_athena_checker());
let decoded_str = &result
.unencrypted_text
.expect("No unencrypted text for ascii85");
assert_eq!(decoded_str[0], "hello world");
}

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

#[test]
fn ascii85_decode_handles_panics() {
let ascii85_decoder = Decoder::<ASCII85Decoder>::new();
let result = ascii85_decoder
.crack(
"hello my name is panicky mc panic face!",
&get_athena_checker(),
)
.unencrypted_text;
if result.is_some() {
panic!("Decode_ascii85 did not return an option with Some<t>.")
} else {
// If we get here, the test passed
// Because the ascii85_decoder.crack function returned None
// as it should do for the input
assert_eq!(true, true);
}
}

#[test]
fn ascii85_handle_panic_if_empty_string() {
let ascii85_decoder = Decoder::<ASCII85Decoder>::new();
let result = ascii85_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
gregorni marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(true, true);
}
}

#[test]
fn ascii85_handle_panic_if_emoji() {
let ascii85_decoder = Decoder::<ASCII85Decoder>::new();
let result = ascii85_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 @@ -5,6 +5,8 @@
//! mod.rs file
//! you will also need to make it a public module in this file.

/// The ascii85_decoder module decodes ascii85
pub mod ascii85_decoder;
/// The atbash_decoder module decodes atbash
pub mod atbash_decoder;
/// The base32_decoder module decodes base32
Expand Down
3 changes: 3 additions & 0 deletions src/filtration_system/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::mpsc::channel;

use crate::checkers::CheckerTypes;
use crate::decoders::ascii85_decoder::ASCII85Decoder;
use crate::decoders::atbash_decoder::AtbashDecoder;
use crate::decoders::base32_decoder::Base32Decoder;
use crate::decoders::base58_bitcoin_decoder::Base58BitcoinDecoder;
Expand Down Expand Up @@ -106,6 +107,7 @@ impl MyResults {
/// Currently takes no args as this is just a spike to get all the basic functionality working
pub fn filter_and_get_decoders(_text_struct: &DecoderResult) -> Decoders {
trace!("Filtering and getting all decoders");
let ascii85 = Decoder::<ASCII85Decoder>::new();
let binary = Decoder::<BinaryDecoder>::new();
let hexadecimal = Decoder::<HexadecimalDecoder>::new();
let base58_bitcoin = Decoder::<Base58BitcoinDecoder>::new();
Expand All @@ -126,6 +128,7 @@ pub fn filter_and_get_decoders(_text_struct: &DecoderResult) -> Decoders {
let railfencedecoder = Decoder::<RailfenceDecoder>::new();
Decoders {
components: vec![
Box::new(ascii85),
Box::new(reversedecoder),
Box::new(base64),
Box::new(base58_bitcoin),
Expand Down