This commit is contained in:
Frederik Palmø 2021-04-21 20:47:47 +02:00
commit 2dab896a09
11 changed files with 1116 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

45
.vscode/launch.json vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,3 @@
pub trait Random {
fn random() -> Self;
}

96
src/reflector.rs Normal file
View 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
View 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
}