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::{info, trace};
pub struct AtbashDecoder;
impl Crack for Decoder<AtbashDecoder> {
fn new() -> Decoder<AtbashDecoder> {
Decoder {
name: "Atbash",
description: "Atbash is a monoalphabetic substitution cipher originally used to encrypt the Hebrew alphabet. It can be modified for use with any known writing system with a standard collating order.",
link: "https://en.wikipedia.org/wiki/Atbash",
tags: vec!["atbash", "substitution", "decoder", "reciprocal"],
popularity: 1.0,
phantom: std::marker::PhantomData,
}
}
fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
trace!("Trying atbash with text {:?}", text);
let decoded_text = atbash_to_alphabet(text);
trace!("Decoded text for atbash: {:?}", decoded_text);
let mut results = CrackResult::new(self, text.to_string());
if !check_string_success(&decoded_text, text) {
info!(
"Failed to decode atbash 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 atbash_to_alphabet(text: &str) -> String {
text.chars()
.map(|char| match char {
letter @ 'a'..='z' => (b'a' + b'z' - letter as u8) as char,
letter @ 'A'..='Z' => (b'A' + b'Z' - letter as u8) as char,
other => other,
})
.collect()
}
#[cfg(test)]
mod tests {
use super::AtbashDecoder;
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 test_atbash() {
let decoder = Decoder::<AtbashDecoder>::new();
let result = decoder.crack("svool dliow", &get_athena_checker());
assert_eq!(result.unencrypted_text.unwrap()[0], "hello world");
}
#[test]
fn test_atbash_capitalization() {
let decoder = Decoder::<AtbashDecoder>::new();
let result = decoder.crack(
"Zgyzhs Hslfow Pvvk Xzkrgzorazgrlm orpv GSRH",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"Atbash Should Keep Capitalization like THIS"
);
}
#[test]
fn test_atbash_non_alphabetic_characters() {
let decoder = Decoder::<AtbashDecoder>::new();
let result = decoder.crack(
"Zgyzhs hslfow ovzev xszizxgvih orpv gsvhv: ',.39=_#%^ rmgzxg zugvi wvxlwrmt!",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"Atbash should leave characters like these: ',.39=_#%^ intact after decoding!"
);
}
#[test]
fn atbash_decode_empty_string() {
let atbash_decoder = Decoder::<AtbashDecoder>::new();
let result = atbash_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn atbash_decode_handles_panics() {
let atbash_decoder = Decoder::<AtbashDecoder>::new();
let result = atbash_decoder
.crack("583920482058430191", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
panic!("Decode_atbash did not return an option with Some<t>.")
} else {
assert_eq!(true, true);
}
}
#[test]
fn atbash_handle_panic_if_empty_string() {
let atbash_decoder = Decoder::<AtbashDecoder>::new();
let result = atbash_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
assert_eq!(true, true);
}
}
#[test]
fn atbash_work_if_string_not_atbash() {
let atbash_decoder = Decoder::<AtbashDecoder>::new();
let result = atbash_decoder
.crack("hello good day!", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
assert_eq!(true, true);
}
}
#[test]
fn atbash_handle_panic_if_emoji() {
let atbash_decoder = Decoder::<AtbashDecoder>::new();
let result = atbash_decoder
.crack("😂", &get_athena_checker())
.unencrypted_text;
if result.is_some() {
assert_eq!(true, true);
}
}
}