ares/decoders/
citrix_ctx1_decoder.rsuse 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};
pub struct CitrixCTX1Decoder;
#[derive(Debug)]
enum Error {
InvalidLength,
LhsOverflow,
RhsOverflow,
InvalidUtf8,
}
impl Crack for Decoder<CitrixCTX1Decoder> {
fn new() -> Decoder<CitrixCTX1Decoder> {
Decoder {
name: "Citrix Ctx1",
description: "Citrix CTX1 is a very old encoding that was used for encoding Citrix passwords.",
link: "https://www.remkoweijnen.nl/blog/2012/05/13/encoding-and-decoding-citrix-passwords/",
tags: vec!["citrix_ctx1", "citrix", "passwords", "decoder"],
popularity: 0.1,
phantom: std::marker::PhantomData,
}
}
fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
trace!("Trying citrix_ctx1 with text {:?}", text);
let decoded_text: Result<String, Error> = decode_citrix_ctx1(text);
let mut results = CrackResult::new(self, text.to_string());
if decoded_text.is_err() {
debug!("Failed to decode citrix_ctx1: {:?}", decoded_text);
return results;
}
trace!("Decoded text for citrix_ctx1: {:?}", decoded_text);
let decoded_text = decoded_text.unwrap();
if !check_string_success(&decoded_text, text) {
info!(
"Failed to decode citrix_ctx1 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 decode_citrix_ctx1(text: &str) -> Result<String, Error> {
if text.len() % 4 != 0 {
return Err(Error::InvalidLength);
}
let mut rev = text.as_bytes().to_vec();
rev.reverse();
let mut result = Vec::new();
let mut temp;
for i in (0..rev.len()).step_by(2) {
if i + 2 >= rev.len() {
temp = 0;
} else {
temp = ((rev[i + 2].checked_sub(0x41)).ok_or(Error::LhsOverflow)? & 0xF)
^ (((rev[i + 3].checked_sub(0x41)).ok_or(Error::RhsOverflow)? << 4) & 0xF0);
}
temp ^= (((rev[i].checked_sub(0x41)).ok_or(Error::LhsOverflow)? & 0xF)
^ (((rev[i + 1].checked_sub(0x41)).ok_or(Error::RhsOverflow)? << 4) & 0xF0))
^ 0xA5;
result.push(temp);
}
result.retain(|&x| x != 0);
result.reverse();
String::from_utf8(result).map_err(|_| Error::InvalidUtf8)
}
#[cfg(test)]
mod tests {
use super::CitrixCTX1Decoder;
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 citrix_ctx1_decodes_successfully() {
let decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = decoder.crack(
"MNGIKIANMEGBKIANMHGCOHECJADFPPFKINCIOBEEIFCA",
&get_athena_checker(),
);
assert_eq!(result.unencrypted_text.unwrap()[0], "hello world");
}
#[test]
fn citrix_ctx1_decodes_lowercase_successfully() {
let decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = decoder.crack(
"pbfejjdmpaffidcgkdagmkgpljbmjjdmpffajkdponeiiicnpkfpjjdmpifnilcoooelmoglincioeebjadfocehilcopdfgndhgjadfmegbjmdjknai",
&get_athena_checker(),
);
assert_eq!(
result.unencrypted_text.unwrap()[0],
"This is lowercase Citrix CTX1"
);
}
#[test]
fn citrix_ctx1_handles_substraction_overflow() {
let citrix_ctx1_decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = citrix_ctx1_decoder
.crack("NUWEN43XR44TLAYHSU4DVI2ISF======", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn citrix_ctx1_handles_length_not_divisible_by_4() {
let citrix_ctx1_decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = citrix_ctx1_decoder
.crack("AAA", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn citrix_ctx1_decode_handles_panics() {
let citrix_ctx1_decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = citrix_ctx1_decoder
.crack(
"hello my name is panicky mc panic face!",
&get_athena_checker(),
)
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn citrix_ctx1_handle_panic_if_empty_string() {
let citrix_ctx1_decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = citrix_ctx1_decoder
.crack("", &get_athena_checker())
.unencrypted_text;
assert!(result.is_none());
}
#[test]
fn citrix_ctx1_decodes_emoji_successfully() {
let citrix_ctx1_decoder = Decoder::<CitrixCTX1Decoder>::new();
let result = citrix_ctx1_decoder.crack("😂", &get_athena_checker());
assert_eq!(result.unencrypted_text.unwrap()[0], "[*");
}
}