use crate::checkers::CheckerTypes;
use crate::decoders::interface::check_string_success;
use gibberish_or_not::Sensitivity;
use super::crack_results::CrackResult;
use super::interface::Crack;
use super::interface::Decoder;
use log::{info, trace};
pub struct ROT47Decoder;
impl Crack for Decoder<ROT47Decoder> {
fn new() -> Decoder<ROT47Decoder> {
Decoder {
name: "rot47",
description: "ROT47 is a derivative of ROT13 which, in addition to scrambling the basic letters, treats numbers and common symbols. Instead of using the sequence A–Z as the alphabet, ROT47 uses a larger set of characters from the common character encoding known as ASCII. Specifically, the 7-bit printable characters, excluding space, from decimal 33 '!' through 126 '~', 94 in total.",
link: "https://en.wikipedia.org/wiki/ROT13#Variants",
tags: vec!["rot47", "substitution", "decoder", "reciprocal"],
popularity: 1.0,
phantom: std::marker::PhantomData,
}
}
fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
trace!("Trying rot47 with text {:?}", text);
let mut results = CrackResult::new(self, text.to_string());
let mut decoded_strings = Vec::new();
let checker_with_sensitivity = checker.with_sensitivity(Sensitivity::Low);
for shift in 1..94 {
let decoded_text = rot47_to_alphabet(text, shift);
decoded_strings.push(decoded_text);
let borrowed_decoded_text = &decoded_strings[decoded_strings.len() - 1];
if !check_string_success(borrowed_decoded_text, text) {
info!(
"Failed to decode rot47 because check_string_success returned false on string {}. This means the string is 'funny' as it wasn't modified.",
borrowed_decoded_text
);
return results;
}
let checker_result = checker_with_sensitivity.check(borrowed_decoded_text);
if checker_result.is_identified {
trace!("Found a match with rot47 shift {}", shift);
results.unencrypted_text = Some(vec![borrowed_decoded_text.to_string()]);
results.update_checker(&checker_result);
return results;
}
}
results.unencrypted_text = Some(decoded_strings);
results
}
fn get_tags(&self) -> &Vec<&str> {
&self.tags
}
fn get_name(&self) -> &str {
self.name
}
}
fn rot47_to_alphabet(text: &str, shift: u8) -> String {
let mut result = String::new();
for c in text.chars() {
let mut c = c as u8;
if (33..=126).contains(&c) {
c = ((c - 33 + shift) % 94) + 33;
}
result.push(c as char);
}
result
}
#[cfg(test)]
mod tests {
use super::rot47_to_alphabet;
use super::ROT47Decoder;
use crate::{
checkers::{
athena::Athena,
checker_type::{Check, Checker},
english::EnglishChecker,
CheckerTypes,
},
decoders::interface::{Crack, Decoder},
};
fn get_athena_checker() -> CheckerTypes {
let athena_checker = Checker::<Athena>::new();
CheckerTypes::CheckAthena(athena_checker)
}
#[test]
#[ignore]
fn rot47_decodes_successfully() {
let rot47_decoder = Decoder::<ROT47Decoder>::new();
let input = "$A9:?I @7 3=24< BF2CEK[ ;F586 >J G@H";
let expected = "3PHINX OF BLACK QUARTZj JUDGE MY VOW";
println!("Input text: {:?}", input);
for shift in 1..94 {
let decoded = rot47_to_alphabet(input, shift);
println!("Shift: {}, Result: {:?}", shift, decoded);
}
let result = rot47_decoder.crack(input, &get_athena_checker());
if let Some(decoded_texts) = &result.unencrypted_text {
println!("Number of decoded texts: {}", decoded_texts.len());
for (i, text) in decoded_texts.iter().enumerate() {
println!("Decoded text {}: {:?}", i, text);
}
if !decoded_texts.is_empty() {
println!("First decoded text: {:?}", decoded_texts[0]);
println!("Expected text: {:?}", expected);
}
} else {
println!("No decoded texts found");
}
assert_eq!(result.unencrypted_text.unwrap()[0], expected);
}
#[test]
fn rot47_handles_panic_if_empty_string() {
let rot47_decoder = Decoder::<ROT47Decoder>::new();
let result = rot47_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn test_rot47_uses_low_sensitivity() {
let rot47_decoder = Decoder::<ROT47Decoder>::new();
let text = "Test text";
let result = rot47_decoder.crack(
text,
&CheckerTypes::CheckEnglish(Checker::<EnglishChecker>::new()),
);
assert!(
result.unencrypted_text.is_some(),
"ROT47 decoder should return some result"
);
}
}