203 lines
5.8 KiB
Rust
203 lines
5.8 KiB
Rust
mod enigma;
|
|
mod letter;
|
|
mod plugboard;
|
|
mod random;
|
|
mod reflector;
|
|
mod rotor;
|
|
|
|
pub use enigma::*;
|
|
pub use letter::*;
|
|
pub use plugboard::*;
|
|
pub use random::*;
|
|
pub use reflector::*;
|
|
pub use rotor::*;
|
|
|
|
use std::io;
|
|
|
|
fn main() {
|
|
#[cfg(debug_assertions)]
|
|
pretty_env_logger::init();
|
|
|
|
let rotors = choose_rotors();
|
|
let plugboard = choose_plugboard();
|
|
let reflector = choose_reflector();
|
|
println!("Plugboard configuration: {}", plugboard);
|
|
let enigma_machine = Enigma::new(rotors, plugboard, reflector);
|
|
|
|
loop {
|
|
let mut enigma_machine = enigma_machine.clone();
|
|
|
|
println!("Input some text to encrypt:");
|
|
|
|
let input = clean_input(get_input_from_stdin());
|
|
let output = enigma_machine.encode(&input);
|
|
|
|
match output {
|
|
Ok(output) => println!("Input: {}\nOutput: {}", input, output),
|
|
Err(e) => println!("Wrong input: {}", e),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn choose_rotors() -> Vec<Rotor> {
|
|
let mut rotors = vec![];
|
|
|
|
while rotors.is_empty() {
|
|
rotors.clear();
|
|
println!("Available rotors:");
|
|
for rotor in Rotor::all() {
|
|
println!("\t{}", rotor.variant);
|
|
}
|
|
println!("Choose rotors (comma-separated, right to left):");
|
|
let input = clean_input(get_input_from_stdin());
|
|
|
|
for choice in input.split(',') {
|
|
let rotor = match choice {
|
|
"I" => Rotor::variant(RotorVariant::I),
|
|
"II" => Rotor::variant(RotorVariant::II),
|
|
"III" => Rotor::variant(RotorVariant::III),
|
|
"IV" => Rotor::variant(RotorVariant::IV),
|
|
"V" => Rotor::variant(RotorVariant::V),
|
|
"VI" => Rotor::variant(RotorVariant::VI),
|
|
"VII" => Rotor::variant(RotorVariant::VII),
|
|
"VIII" => Rotor::variant(RotorVariant::VIII),
|
|
"IDENTITY" => Rotor::variant(RotorVariant::Identity),
|
|
_ => {
|
|
println!("Invalid rotor choice: {}", choice);
|
|
break;
|
|
}
|
|
};
|
|
|
|
rotors.push(rotor);
|
|
}
|
|
}
|
|
|
|
rotors
|
|
}
|
|
|
|
fn choose_plugboard() -> Plugboard {
|
|
let mut plugboard = None;
|
|
|
|
while plugboard.is_none() {
|
|
println!("Available plugboards:");
|
|
println!("\tRandom");
|
|
println!("\tIdentity");
|
|
println!("Choose plugboard (only one):");
|
|
let input = clean_input(get_input_from_stdin());
|
|
|
|
plugboard = match &input[..] {
|
|
"IDENTITY" => Some(Plugboard::identity()),
|
|
"RANDOM" => Some(Plugboard::random()),
|
|
_ => {
|
|
println!("Invalid plugboard choice: {}", input);
|
|
None
|
|
}
|
|
};
|
|
}
|
|
|
|
plugboard.unwrap()
|
|
}
|
|
|
|
fn choose_reflector() -> Reflector {
|
|
let mut reflector = None;
|
|
|
|
while reflector.is_none() {
|
|
println!("Available reflectors:");
|
|
for reflector in Reflector::all() {
|
|
println!("\t{}", reflector.variant);
|
|
}
|
|
println!("Choose reflector (only one):");
|
|
let input = clean_input(get_input_from_stdin());
|
|
|
|
reflector = match &input[..] {
|
|
"A" => Some(Reflector::variant(ReflectorVariant::A)),
|
|
"B" => Some(Reflector::variant(ReflectorVariant::B)),
|
|
"C" => Some(Reflector::variant(ReflectorVariant::C)),
|
|
"BETA" => Some(Reflector::variant(ReflectorVariant::Beta)),
|
|
"GAMMA" => Some(Reflector::variant(ReflectorVariant::Gamma)),
|
|
"BTHIN" => Some(Reflector::variant(ReflectorVariant::BThin)),
|
|
"CTHIN" => Some(Reflector::variant(ReflectorVariant::CThin)),
|
|
"IDENTITY" => Some(Reflector::variant(ReflectorVariant::Identity)),
|
|
_ => {
|
|
println!("Invalid reflector choice: {}", input);
|
|
None
|
|
}
|
|
};
|
|
}
|
|
|
|
reflector.unwrap()
|
|
}
|
|
|
|
fn get_input_from_stdin() -> String {
|
|
let mut input = String::new();
|
|
io::stdin().read_line(&mut input).unwrap();
|
|
input
|
|
}
|
|
|
|
fn clean_input(input: String) -> String {
|
|
input
|
|
.trim()
|
|
.chars()
|
|
.filter(|c| c != &' ' && c != &'\r' && c != &'\n')
|
|
.collect::<String>()
|
|
.to_uppercase()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn enigma_machine_encodes_correctly() {
|
|
let rotors = vec![
|
|
Rotor::variant(RotorVariant::I),
|
|
Rotor::variant(RotorVariant::II),
|
|
Rotor::variant(RotorVariant::III),
|
|
];
|
|
let plugboard = Plugboard::identity();
|
|
let reflector = Reflector::variant(ReflectorVariant::B);
|
|
let enigma_machine = Enigma::new(rotors, plugboard, reflector);
|
|
|
|
let mut em1 = enigma_machine.clone();
|
|
let mut em2 = enigma_machine;
|
|
|
|
println!("Encoding string");
|
|
let result = em1
|
|
.encode("MAGNUSERPISSEDAARLIGTILLEAGUEOFLEGENDS")
|
|
.unwrap();
|
|
assert_eq!(result, "HTMPRVHFYZUHSANJWQCCAOHUTDRICUYUJJTHBE");
|
|
|
|
println!("Decoding string");
|
|
let result = em2
|
|
.encode("HTMPRVHFYZUHSANJWQCCAOHUTDRICUYUJJTHBE")
|
|
.unwrap();
|
|
assert_eq!(result, "MAGNUSERPISSEDAARLIGTILLEAGUEOFLEGENDS");
|
|
}
|
|
|
|
#[test]
|
|
fn random_plugboard_is_complete() {
|
|
let plugboard = Plugboard::random();
|
|
|
|
for letter in Letter::all() {
|
|
println!("Testing if '{:?}' is mapped in plugboard", letter);
|
|
plugboard.letter(letter);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn rotor_rotates_as_expected() {
|
|
let mut rotor = Rotor::variant(RotorVariant::Identity);
|
|
|
|
println!("{:?}", rotor.mappings);
|
|
assert_eq!(rotor.forwards(Letter::A), Letter::A);
|
|
rotor.rotate();
|
|
assert_eq!(rotor.forwards(Letter::A), Letter::A);
|
|
|
|
for _ in 0..26 {
|
|
rotor.rotate();
|
|
}
|
|
|
|
println!("{:?}", rotor.mappings);
|
|
assert_eq!(rotor.forwards(Letter::A), Letter::A);
|
|
}
|
|
}
|