1.0
This commit is contained in:
commit
2dab896a09
11 changed files with 1116 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'enigma'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=enigma",
|
||||
"--package=enigma"
|
||||
],
|
||||
"filter": {
|
||||
"name": "enigma",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'enigma'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=enigma",
|
||||
"--package=enigma"
|
||||
],
|
||||
"filter": {
|
||||
"name": "enigma",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
224
Cargo.lock
generated
Normal file
224
Cargo.lock
generated
Normal file
|
@ -0,0 +1,224 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "enigma"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_env_logger"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "enigma"
|
||||
version = "0.1.0"
|
||||
authors = ["vodofrede <frederik@palmoe.dk>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
rand = "0.8.3"
|
67
src/enigma.rs
Normal file
67
src/enigma.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Enigma {
|
||||
rotors: Vec<Rotor>,
|
||||
plugboard: Plugboard,
|
||||
reflector: Reflector,
|
||||
}
|
||||
|
||||
impl Enigma {
|
||||
pub fn new(rotors: Vec<Rotor>, plugboard: Plugboard, reflector: Reflector) -> Self {
|
||||
Enigma {
|
||||
rotors,
|
||||
plugboard,
|
||||
reflector,
|
||||
}
|
||||
}
|
||||
pub fn encode(&mut self, input: &str) -> Result<String, &'static str> {
|
||||
let output: Result<String, &'static str> = input
|
||||
.chars()
|
||||
.map(|c| {
|
||||
self.rotate_rotors();
|
||||
|
||||
let mut letter = Letter::from_str(&c.to_string())?;
|
||||
letter = self.plugboard.letter(letter);
|
||||
|
||||
for rotor in self.rotors.iter() {
|
||||
letter = rotor.forwards(letter);
|
||||
}
|
||||
|
||||
letter = self.reflector.letter(letter);
|
||||
|
||||
for rotor in self.rotors.iter().rev() {
|
||||
letter = rotor.backwards(letter);
|
||||
}
|
||||
|
||||
letter = self.plugboard.letter(letter);
|
||||
|
||||
Ok(char::from(letter))
|
||||
})
|
||||
.collect();
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn rotate_rotors(&mut self) {
|
||||
let mut rotate = true;
|
||||
|
||||
for rotor in self.rotors.iter_mut() {
|
||||
let rotate_next = rotor.at_notch();
|
||||
|
||||
if rotate {
|
||||
rotor.rotate();
|
||||
}
|
||||
|
||||
rotate = rotate_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Random for Enigma {
|
||||
fn random() -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
212
src/letter.rs
Normal file
212
src/letter.rs
Normal file
|
@ -0,0 +1,212 @@
|
|||
use crate::*;
|
||||
use rand::prelude::*;
|
||||
use std::{
|
||||
ops::{Add, Sub},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
pub const AMOUNT_OF_LETTERS: usize = 26;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord)]
|
||||
pub enum Letter {
|
||||
A = 0,
|
||||
B = 1,
|
||||
C = 2,
|
||||
D = 3,
|
||||
E = 4,
|
||||
F = 5,
|
||||
G = 6,
|
||||
H = 7,
|
||||
I = 8,
|
||||
J = 9,
|
||||
K = 10,
|
||||
L = 11,
|
||||
M = 12,
|
||||
N = 13,
|
||||
O = 14,
|
||||
P = 15,
|
||||
Q = 16,
|
||||
R = 17,
|
||||
S = 18,
|
||||
T = 19,
|
||||
U = 20,
|
||||
V = 21,
|
||||
W = 22,
|
||||
X = 23,
|
||||
Y = 24,
|
||||
Z = 25,
|
||||
}
|
||||
|
||||
impl Letter {
|
||||
pub fn all() -> Vec<Letter> {
|
||||
use Letter::*;
|
||||
|
||||
vec![
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Letter {
|
||||
type Output = Letter;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Letter::from((self as usize + rhs as usize) % 26)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Letter {
|
||||
type Output = Letter;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
let sum = (self as isize - rhs as isize).rem_euclid(26);
|
||||
Letter::from(sum as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Random for Letter {
|
||||
fn random() -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
let num = rng.gen_range(0..AMOUNT_OF_LETTERS);
|
||||
Letter::from(num)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<usize> for Letter {
|
||||
fn from(n: usize) -> Self {
|
||||
let index = n % 26;
|
||||
|
||||
match index {
|
||||
0 => Letter::A,
|
||||
1 => Letter::B,
|
||||
2 => Letter::C,
|
||||
3 => Letter::D,
|
||||
4 => Letter::E,
|
||||
5 => Letter::F,
|
||||
6 => Letter::G,
|
||||
7 => Letter::H,
|
||||
8 => Letter::I,
|
||||
9 => Letter::J,
|
||||
10 => Letter::K,
|
||||
11 => Letter::L,
|
||||
12 => Letter::M,
|
||||
13 => Letter::N,
|
||||
14 => Letter::O,
|
||||
15 => Letter::P,
|
||||
16 => Letter::Q,
|
||||
17 => Letter::R,
|
||||
18 => Letter::S,
|
||||
19 => Letter::T,
|
||||
20 => Letter::U,
|
||||
21 => Letter::V,
|
||||
22 => Letter::W,
|
||||
23 => Letter::X,
|
||||
24 => Letter::Y,
|
||||
25 => Letter::Z,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Letter {
|
||||
fn from(c: char) -> Self {
|
||||
match c {
|
||||
'A' => Letter::A,
|
||||
'B' => Letter::B,
|
||||
'C' => Letter::C,
|
||||
'D' => Letter::D,
|
||||
'E' => Letter::E,
|
||||
'F' => Letter::F,
|
||||
'G' => Letter::G,
|
||||
'H' => Letter::H,
|
||||
'I' => Letter::I,
|
||||
'J' => Letter::J,
|
||||
'K' => Letter::K,
|
||||
'L' => Letter::L,
|
||||
'M' => Letter::M,
|
||||
'N' => Letter::N,
|
||||
'O' => Letter::O,
|
||||
'P' => Letter::P,
|
||||
'Q' => Letter::Q,
|
||||
'R' => Letter::R,
|
||||
'S' => Letter::S,
|
||||
'T' => Letter::T,
|
||||
'U' => Letter::U,
|
||||
'V' => Letter::V,
|
||||
'W' => Letter::W,
|
||||
'X' => Letter::X,
|
||||
'Y' => Letter::Y,
|
||||
'Z' => Letter::Z,
|
||||
_ => panic!("Tried to parse unknown letter: '{}'", c),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Letter {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"A" => Ok(Letter::A),
|
||||
"B" => Ok(Letter::B),
|
||||
"C" => Ok(Letter::C),
|
||||
"D" => Ok(Letter::D),
|
||||
"E" => Ok(Letter::E),
|
||||
"F" => Ok(Letter::F),
|
||||
"G" => Ok(Letter::G),
|
||||
"H" => Ok(Letter::H),
|
||||
"I" => Ok(Letter::I),
|
||||
"J" => Ok(Letter::J),
|
||||
"K" => Ok(Letter::K),
|
||||
"L" => Ok(Letter::L),
|
||||
"M" => Ok(Letter::M),
|
||||
"N" => Ok(Letter::N),
|
||||
"O" => Ok(Letter::O),
|
||||
"P" => Ok(Letter::P),
|
||||
"Q" => Ok(Letter::Q),
|
||||
"R" => Ok(Letter::R),
|
||||
"S" => Ok(Letter::S),
|
||||
"T" => Ok(Letter::T),
|
||||
"U" => Ok(Letter::U),
|
||||
"V" => Ok(Letter::V),
|
||||
"W" => Ok(Letter::W),
|
||||
"X" => Ok(Letter::X),
|
||||
"Y" => Ok(Letter::Y),
|
||||
"Z" => Ok(Letter::Z),
|
||||
_ => Err("Failed to parse string as letter"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Letter> for char {
|
||||
fn from(letter: Letter) -> Self {
|
||||
match letter {
|
||||
Letter::A => 'A',
|
||||
Letter::B => 'B',
|
||||
Letter::C => 'C',
|
||||
Letter::D => 'D',
|
||||
Letter::E => 'E',
|
||||
Letter::F => 'F',
|
||||
Letter::G => 'G',
|
||||
Letter::H => 'H',
|
||||
Letter::I => 'I',
|
||||
Letter::J => 'J',
|
||||
Letter::K => 'K',
|
||||
Letter::L => 'L',
|
||||
Letter::M => 'M',
|
||||
Letter::N => 'N',
|
||||
Letter::O => 'O',
|
||||
Letter::P => 'P',
|
||||
Letter::Q => 'Q',
|
||||
Letter::R => 'R',
|
||||
Letter::S => 'S',
|
||||
Letter::T => 'T',
|
||||
Letter::U => 'U',
|
||||
Letter::V => 'V',
|
||||
Letter::W => 'W',
|
||||
Letter::X => 'X',
|
||||
Letter::Y => 'Y',
|
||||
Letter::Z => 'Z',
|
||||
}
|
||||
}
|
||||
}
|
203
src/main.rs
Normal file
203
src/main.rs
Normal file
|
@ -0,0 +1,203 @@
|
|||
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.clone();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
122
src/plugboard.rs
Normal file
122
src/plugboard.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use crate::*;
|
||||
use rand::prelude::*;
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
// MUST be less than total amount of letters divided by two
|
||||
const CABLES: usize = 10;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Plugboard {
|
||||
mappings: Vec<Letter>,
|
||||
}
|
||||
|
||||
impl Plugboard {
|
||||
pub fn new(mappings: Vec<Letter>) -> Self {
|
||||
Plugboard { mappings }
|
||||
}
|
||||
|
||||
pub fn identity() -> Self {
|
||||
let mappings = vec![
|
||||
Letter::A,
|
||||
Letter::B,
|
||||
Letter::C,
|
||||
Letter::D,
|
||||
Letter::E,
|
||||
Letter::F,
|
||||
Letter::G,
|
||||
Letter::H,
|
||||
Letter::I,
|
||||
Letter::J,
|
||||
Letter::K,
|
||||
Letter::L,
|
||||
Letter::M,
|
||||
Letter::N,
|
||||
Letter::O,
|
||||
Letter::P,
|
||||
Letter::Q,
|
||||
Letter::R,
|
||||
Letter::S,
|
||||
Letter::T,
|
||||
Letter::U,
|
||||
Letter::V,
|
||||
Letter::W,
|
||||
Letter::X,
|
||||
Letter::Y,
|
||||
Letter::Z,
|
||||
];
|
||||
|
||||
Plugboard::new(mappings)
|
||||
}
|
||||
|
||||
pub fn index(&self, index: usize) -> usize {
|
||||
*self.mappings.get(index).expect(
|
||||
&format!(
|
||||
"Switchboard is missing mapping for a letter: {:?}",
|
||||
Letter::from(index)
|
||||
)
|
||||
.to_string(),
|
||||
) as usize
|
||||
}
|
||||
|
||||
pub fn letter(&self, letter: Letter) -> Letter {
|
||||
Letter::from(self.index(letter as usize))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Plugboard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut pairs = HashMap::new();
|
||||
|
||||
for (i, mapping) in self.mappings.iter().enumerate() {
|
||||
let left_letter = Letter::from(i);
|
||||
let right_letter = *mapping;
|
||||
|
||||
if left_letter != right_letter
|
||||
&& !pairs.get(&left_letter).is_some()
|
||||
&& !pairs.get(&right_letter).is_some()
|
||||
{
|
||||
pairs.insert(left_letter, right_letter);
|
||||
}
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
if pairs.is_empty() {
|
||||
s = "Identity".to_string();
|
||||
}
|
||||
|
||||
for (left_letter, right_letter) in pairs.iter() {
|
||||
s = format!("{:?}{:?} {}", left_letter, right_letter, s);
|
||||
}
|
||||
|
||||
f.write_str(&s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Random for Plugboard {
|
||||
fn random() -> Self {
|
||||
let mut mappings = vec![Letter::A; AMOUNT_OF_LETTERS];
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
let mut input = Letter::all();
|
||||
input.shuffle(&mut rng);
|
||||
input.truncate(CABLES);
|
||||
|
||||
let mut output: Vec<Letter> = Letter::all()
|
||||
.iter()
|
||||
.filter(|l| !input.contains(l))
|
||||
.cloned()
|
||||
.collect();
|
||||
let remaining = output.split_off(CABLES);
|
||||
|
||||
for (input, output) in input.iter().zip(output.iter()) {
|
||||
mappings.insert(*input as usize, *output);
|
||||
mappings.insert(*output as usize, *input);
|
||||
}
|
||||
|
||||
for letter in remaining {
|
||||
mappings.insert(letter as usize, letter);
|
||||
}
|
||||
|
||||
Plugboard::new(mappings)
|
||||
}
|
||||
}
|
3
src/random.rs
Normal file
3
src/random.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub trait Random {
|
||||
fn random() -> Self;
|
||||
}
|
96
src/reflector.rs
Normal file
96
src/reflector.rs
Normal file
|
@ -0,0 +1,96 @@
|
|||
use crate::*;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Reflector {
|
||||
pub variant: ReflectorVariant,
|
||||
mappings: Vec<Letter>,
|
||||
}
|
||||
|
||||
impl Reflector {
|
||||
pub fn variant(variant: ReflectorVariant) -> Self {
|
||||
let mappings = match variant {
|
||||
ReflectorVariant::Identity => mappings(VARIANT_IDENTITY),
|
||||
ReflectorVariant::Beta => mappings(VARIANT_BETA),
|
||||
ReflectorVariant::Gamma => mappings(VARIANT_GAMMA),
|
||||
ReflectorVariant::A => mappings(VARIANT_A),
|
||||
ReflectorVariant::B => mappings(VARIANT_B),
|
||||
ReflectorVariant::C => mappings(VARIANT_C),
|
||||
ReflectorVariant::BThin => mappings(VARIANT_B_THIN),
|
||||
ReflectorVariant::CThin => mappings(VARIANT_C_THIN),
|
||||
};
|
||||
|
||||
Reflector { variant, mappings }
|
||||
}
|
||||
|
||||
pub fn all() -> Vec<Reflector> {
|
||||
vec![
|
||||
Reflector::variant(ReflectorVariant::A),
|
||||
Reflector::variant(ReflectorVariant::B),
|
||||
Reflector::variant(ReflectorVariant::C),
|
||||
Reflector::variant(ReflectorVariant::Beta),
|
||||
Reflector::variant(ReflectorVariant::Gamma),
|
||||
Reflector::variant(ReflectorVariant::BThin),
|
||||
Reflector::variant(ReflectorVariant::CThin),
|
||||
Reflector::variant(ReflectorVariant::Identity),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn index(&self, letter: usize) -> usize {
|
||||
*self
|
||||
.mappings
|
||||
.get(letter)
|
||||
.expect(&format!("Reflector is missing mapping for a letter: {:?}", letter).to_string())
|
||||
as usize
|
||||
}
|
||||
|
||||
pub fn letter(&self, letter: Letter) -> Letter {
|
||||
Letter::from(self.index(letter as usize))
|
||||
}
|
||||
}
|
||||
|
||||
const VARIANT_IDENTITY: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const VARIANT_BETA: &'static str = "LEYJVCNIXWPBQMDRTAKZGFUHOS";
|
||||
const VARIANT_GAMMA: &'static str = "FSOKANUERHMBTIYCWLQPZXVGJD";
|
||||
const VARIANT_A: &'static str = "EJMZALYXVBWFCRQUONTSPIKHGD";
|
||||
const VARIANT_B: &'static str = "YRUHQSLDPXNGOKMIEBFZCWVJAT";
|
||||
const VARIANT_C: &'static str = "FVPJIAOYEDRZXWGCTKUQSBNMHL";
|
||||
const VARIANT_B_THIN: &'static str = "ENKQAUYWJICOPBLMDXZVFTHRGS";
|
||||
const VARIANT_C_THIN: &'static str = "RDOBJNTKVEHMLFCWZAXGYIPSUQ";
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum ReflectorVariant {
|
||||
Identity,
|
||||
Beta,
|
||||
Gamma,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
BThin,
|
||||
CThin,
|
||||
}
|
||||
|
||||
impl fmt::Display for ReflectorVariant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ReflectorVariant::Identity => f.write_str("Identity"),
|
||||
ReflectorVariant::Beta => f.write_str("Beta"),
|
||||
ReflectorVariant::Gamma => f.write_str("Gamma"),
|
||||
ReflectorVariant::A => f.write_str("A"),
|
||||
ReflectorVariant::B => f.write_str("B"),
|
||||
ReflectorVariant::C => f.write_str("C"),
|
||||
ReflectorVariant::BThin => f.write_str("B Thin"),
|
||||
ReflectorVariant::CThin => f.write_str("C Thin"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mappings(input: &'static str) -> Vec<Letter> {
|
||||
let mut mappings = vec![];
|
||||
|
||||
for c in input.chars() {
|
||||
mappings.push(Letter::from(c));
|
||||
}
|
||||
|
||||
mappings
|
||||
}
|
131
src/rotor.rs
Normal file
131
src/rotor.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use crate::*;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Rotor {
|
||||
pub variant: RotorVariant,
|
||||
pub mappings: Vec<Letter>,
|
||||
pub position: Letter,
|
||||
pub notches: Vec<Letter>,
|
||||
}
|
||||
|
||||
impl Rotor {
|
||||
pub fn variant(variant: RotorVariant) -> Self {
|
||||
let (mappings, notches) = match variant {
|
||||
RotorVariant::Identity => (mappings(IDENTITY), vec![Letter::Z]),
|
||||
RotorVariant::I => (mappings(VARIANT_I), vec![Letter::Q]),
|
||||
RotorVariant::II => (mappings(VARIANT_II), vec![Letter::E]),
|
||||
RotorVariant::III => (mappings(VARIANT_III), vec![Letter::V]),
|
||||
RotorVariant::IV => (mappings(VARIANT_IV), vec![Letter::J]),
|
||||
RotorVariant::V => (mappings(VARIANT_V), vec![Letter::Z]),
|
||||
RotorVariant::VI => (mappings(VARIANT_VI), vec![Letter::Z, Letter::M]),
|
||||
RotorVariant::VII => (mappings(VARIANT_VII), vec![Letter::Z, Letter::M]),
|
||||
RotorVariant::VIII => (mappings(VARIANT_VIII), vec![Letter::Z, Letter::M]),
|
||||
};
|
||||
|
||||
let position = Letter::A;
|
||||
|
||||
Rotor {
|
||||
variant,
|
||||
mappings,
|
||||
position,
|
||||
notches,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all() -> Vec<Rotor> {
|
||||
vec![
|
||||
Rotor::variant(RotorVariant::I),
|
||||
Rotor::variant(RotorVariant::II),
|
||||
Rotor::variant(RotorVariant::III),
|
||||
Rotor::variant(RotorVariant::IV),
|
||||
Rotor::variant(RotorVariant::V),
|
||||
Rotor::variant(RotorVariant::VI),
|
||||
Rotor::variant(RotorVariant::VII),
|
||||
Rotor::variant(RotorVariant::VIII),
|
||||
Rotor::variant(RotorVariant::Identity),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn set_position(&mut self, position: Letter) {
|
||||
self.position = position;
|
||||
}
|
||||
|
||||
pub fn forwards(&self, letter: Letter) -> Letter {
|
||||
*self
|
||||
.mappings
|
||||
.get((letter + self.position) as usize)
|
||||
.expect(&format!("Rotor is missing mapping for a letter: {:?}", letter).to_string())
|
||||
- self.position
|
||||
}
|
||||
|
||||
pub fn backwards(&self, letter: Letter) -> Letter {
|
||||
Letter::from(
|
||||
self.mappings
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, l)| (**l - self.position) == letter)
|
||||
.next()
|
||||
.unwrap()
|
||||
.0,
|
||||
) - self.position
|
||||
}
|
||||
|
||||
pub fn at_notch(&self) -> bool {
|
||||
self.notches.contains(&self.position)
|
||||
}
|
||||
|
||||
pub fn rotate(&mut self) {
|
||||
self.position = Letter::from((self.position as usize + 1) % AMOUNT_OF_LETTERS);
|
||||
}
|
||||
}
|
||||
|
||||
const IDENTITY: &'static str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
const VARIANT_I: &'static str = "EKMFLGDQVZNTOWYHXUSPAIBRCJ";
|
||||
const VARIANT_II: &'static str = "AJDKSIRUXBLHWTMCQGZNPYFVOE";
|
||||
const VARIANT_III: &'static str = "BDFHJLCPRTXVZNYEIWGAKMUSQO";
|
||||
const VARIANT_IV: &'static str = "ESOVPZJAYQUIRHXLNFTGKDCMWB";
|
||||
const VARIANT_V: &'static str = "VZBRGITYUPSDNHLXAWMJQOFECK";
|
||||
const VARIANT_VI: &'static str = "JPGVOUMFYQBENHZRDKASXLICTW";
|
||||
const VARIANT_VII: &'static str = "NZJHGRCXMYSWBOUFAIVLPEKQDT";
|
||||
const VARIANT_VIII: &'static str = "FKQHTLXOCBJSPDZRAMEWNIUYGV";
|
||||
|
||||
#[non_exhaustive]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum RotorVariant {
|
||||
Identity,
|
||||
I,
|
||||
II,
|
||||
III,
|
||||
IV,
|
||||
V,
|
||||
VI,
|
||||
VII,
|
||||
VIII,
|
||||
}
|
||||
|
||||
impl fmt::Display for RotorVariant {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RotorVariant::Identity => f.write_str("Identity"),
|
||||
RotorVariant::I => f.write_str("I"),
|
||||
RotorVariant::II => f.write_str("II"),
|
||||
RotorVariant::III => f.write_str("III"),
|
||||
RotorVariant::IV => f.write_str("IV"),
|
||||
RotorVariant::V => f.write_str("V"),
|
||||
RotorVariant::VI => f.write_str("VI"),
|
||||
RotorVariant::VII => f.write_str("VII"),
|
||||
RotorVariant::VIII => f.write_str("VIII"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mappings(input: &'static str) -> Vec<Letter> {
|
||||
let mut mappings = vec![];
|
||||
|
||||
for c in input.chars() {
|
||||
mappings.push(Letter::from(c));
|
||||
}
|
||||
|
||||
mappings
|
||||
}
|
Loading…
Reference in a new issue