cipher_crypt/caesar.rs
1//! The Caesar cipher is named after Julius Caesar, who used it (allegedly) with a shift of three
2//! to protect messages of military significance.
3//!
4//! As with all single-alphabet substitution ciphers, the Caesar cipher is easily broken
5//! and in modern practice offers essentially no communication security.
6//!
7use crate::common::alphabet::Alphabet;
8use crate::common::cipher::Cipher;
9use crate::common::{alphabet, substitute};
10
11/// A Caesar cipher.
12///
13/// This struct is created by the `new()` method. See its documentation for more.
14pub struct Caesar {
15 shift: usize,
16}
17
18impl Cipher for Caesar {
19 type Key = usize;
20 type Algorithm = Caesar;
21
22 /// Initialise a Caesar cipher given a specific shift value.
23 ///
24 /// # Panics
25 /// * `shift` is not in the inclusive range `1 - 26`.
26 ///
27 fn new(shift: usize) -> Caesar {
28 if shift < 1 || shift > 26 {
29 panic!("The shift factor must be within the range 1 <= n <= 26.");
30 }
31
32 Caesar { shift }
33 }
34
35 /// Encrypt a message using a Caesar cipher.
36 ///
37 /// # Examples
38 /// Basic usage:
39 ///
40 /// ```
41 /// use cipher_crypt::{Cipher, Caesar};
42 ///
43 /// let c = Caesar::new(3);
44 /// assert_eq!("Dwwdfn dw gdzq!", c.encrypt("Attack at dawn!").unwrap());
45 /// ```
46 ///
47 fn encrypt(&self, message: &str) -> Result<String, &'static str> {
48 // Encryption of a letter:
49 // E(x) = (x + n) mod 26
50 // Where; x = position of letter in alphabet
51 // n = shift factor (or key)
52
53 Ok(substitute::shift_substitution(message, |idx| {
54 alphabet::STANDARD.modulo((idx + self.shift) as isize)
55 }))
56 }
57
58 /// Decrypt a message using a Caesar cipher.
59 ///
60 /// # Examples
61 /// Basic usage:
62 ///
63 /// ```
64 /// use cipher_crypt::{Cipher, Caesar};
65 ///
66 /// let c = Caesar::new(3);
67 /// assert_eq!("Attack at dawn!", c.decrypt("Dwwdfn dw gdzq!").unwrap());
68 /// ```
69 ///
70 fn decrypt(&self, ciphertext: &str) -> Result<String, &'static str> {
71 // Decryption of a letter:
72 // D(x) = (x - n) mod 26
73 // Where; x = position of letter in alphabet
74 // n = shift factor (or key)
75
76 Ok(substitute::shift_substitution(ciphertext, |idx| {
77 alphabet::STANDARD.modulo(idx as isize - self.shift as isize)
78 }))
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85
86 #[test]
87 fn encrypt_message() {
88 let c = Caesar::new(2);
89 assert_eq!("Cvvcem cv fcyp!", c.encrypt("Attack at dawn!").unwrap());
90 }
91
92 #[test]
93 fn decrypt_message() {
94 let c = Caesar::new(2);
95 assert_eq!("Attack at dawn!", c.decrypt("Cvvcem cv fcyp!").unwrap());
96 }
97
98 #[test]
99 fn with_utf8() {
100 let c = Caesar::new(3);
101 let message = "Peace, Freedom and Liberty! 🗡️";
102 let encrypted = c.encrypt(message).unwrap();
103 let decrypted = c.decrypt(&encrypted).unwrap();
104
105 assert_eq!(decrypted, message);
106 }
107
108 #[test]
109 fn exhaustive_encrypt() {
110 //Test with every possible shift combination
111 let message = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
112
113 for i in 1..27 {
114 let c = Caesar::new(i);
115 let encrypted = c.encrypt(message).unwrap();
116 let decrypted = c.decrypt(&encrypted).unwrap();
117 assert_eq!(decrypted, message);
118 }
119 }
120
121 #[test]
122 #[should_panic]
123 fn key_to_small() {
124 Caesar::new(0);
125 }
126
127 #[test]
128 #[should_panic]
129 fn key_to_big() {
130 Caesar::new(27);
131 }
132}