ares/decoders/
caesar_decoder.rs1use crate::checkers::CheckerTypes;
7use crate::decoders::interface::check_string_success;
8
9use super::crack_results::CrackResult;
10use super::interface::Crack;
11use super::interface::Decoder;
12
13use log::{info, trace};
14
15pub struct CaesarDecoder;
36
37impl Crack for Decoder<CaesarDecoder> {
38 fn new() -> Decoder<CaesarDecoder> {
39 Decoder {
40 name: "Caesar Cipher",
41 description: "Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet.",
42 link: "https://en.wikipedia.org/wiki/Caesar_cipher",
43 tags: vec!["caesar", "decryption", "classic", "reciprocal"],
44 popularity: 1.0,
45 phantom: std::marker::PhantomData,
46 }
47 }
48
49 fn crack(&self, text: &str, checker: &CheckerTypes) -> CrackResult {
53 trace!("Trying Caesar Cipher with text {:?}", text);
54 let mut results = CrackResult::new(self, text.to_string());
55 let mut decoded_strings = Vec::new();
56 for shift in 1..25 {
57 let decoded_text = caesar(text, shift);
58 decoded_strings.push(decoded_text);
59 let borrowed_decoded_text = &decoded_strings[decoded_strings.len() - 1];
60 if !check_string_success(borrowed_decoded_text, text) {
61 info!(
62 "Failed to decode caesar because check_string_success returned false on string {}. This means the string is 'funny' as it wasn't modified.",
63 borrowed_decoded_text
64 );
65 return results;
66 }
67 let checker_result = checker.check(borrowed_decoded_text);
68 if checker_result.is_identified {
70 trace!("Found a match with caesar shift {}", shift);
71 results.unencrypted_text = Some(vec![borrowed_decoded_text.to_string()]);
72 results.update_checker(&checker_result);
73 return results;
74 }
75 }
76 results.unencrypted_text = Some(decoded_strings);
77 results
78 }
79 fn get_tags(&self) -> &Vec<&str> {
81 &self.tags
82 }
83 fn get_name(&self) -> &str {
85 self.name
86 }
87}
88
89fn caesar(cipher: &str, shift: u8) -> String {
91 cipher
92 .chars()
93 .map(|c| {
94 if c.is_ascii_alphabetic() {
95 let first = if c.is_ascii_lowercase() { b'a' } else { b'A' };
96 (first + (c as u8 + shift - first) % 26) as char
98 } else {
99 c
100 }
101 })
102 .collect()
103}
104
105#[cfg(test)]
106mod tests {
107 use super::CaesarDecoder;
108 use super::*;
109 use crate::{
110 checkers::{
111 athena::Athena,
112 checker_type::{Check, Checker},
113 CheckerTypes,
114 },
115 decoders::interface::{Crack, Decoder},
116 };
117
118 fn get_athena_checker() -> CheckerTypes {
120 let athena_checker = Checker::<Athena>::new();
121 CheckerTypes::CheckAthena(athena_checker)
122 }
123
124 #[test]
125 fn empty() {
126 assert_eq!(caesar("", 13), "");
127 }
128
129 #[test]
130 fn caesar_rot_13() {
131 assert_eq!(caesar("rust", 13), "ehfg");
132 }
133
134 #[test]
135 fn caesar_unicode() {
136 assert_eq!(caesar("attack at dawn 攻", 5), "fyyfhp fy ifbs 攻");
137 }
138
139 #[test]
140 fn successful_decoding() {
141 let caesar_decoder = Decoder::<CaesarDecoder>::new();
142
143 let result = caesar_decoder.crack("fyyfhp", &get_athena_checker());
144 let decoded_str = &result
145 .unencrypted_text
146 .expect("No unencrypted text for caesar");
147 assert_eq!(decoded_str[0], "attack");
148 }
149
150 #[test]
151 fn successful_decoding_longer_text() {
152 let caesar_decoder = Decoder::<CaesarDecoder>::new();
153
154 let result = caesar_decoder.crack("uryyb guvf vf ybat grkg", &get_athena_checker());
155 let decoded_str = &result
156 .unencrypted_text
157 .expect("No unencrypted text for caesar");
158 assert_eq!(decoded_str[0], "hello this is long text");
159 }
160
161 #[test]
162 fn successful_decoding_longer_text_with_puncuation() {
163 let caesar_decoder = Decoder::<CaesarDecoder>::new();
164
165 let result = caesar_decoder.crack("Uryyb! guvf vf ybat grkg?", &get_athena_checker());
166 let decoded_str = &result
167 .unencrypted_text
168 .expect("No unencrypted text for caesar");
169 assert_eq!(decoded_str[0], "Hello! this is long text?");
170 }
171
172 #[test]
173 fn caesar_decode_empty_string() {
174 let caesar_decoder = Decoder::<CaesarDecoder>::new();
177 let result = caesar_decoder
178 .crack("", &get_athena_checker())
179 .unencrypted_text;
180 assert!(result.is_none());
181 }
182
183 #[test]
184 fn caesar_decode_fails() {
185 let caesar_decoder = Decoder::<CaesarDecoder>::new();
186 let result = caesar_decoder
187 .crack("#", &get_athena_checker())
188 .unencrypted_text;
189 if result.is_some() {
190 panic!("Decode_caesar did not return an option with Some<t>.")
191 } else {
192 assert!(true);
196 }
197 }
198}