Enigma Virtual Machine¶
Enigma Machine is the most powerful cipher machine used by Nazi Germany during the World War II. It has an electromechanical design which can scramble 26 Letters. When you press a button the corresponding light according to the enigma setting is lit up. It was invented by Arthur Scherbius and Richard Ritter.
If plain test is entered, it is encoded into ciphertext and if ciphertext is entered the plain text is displayed. The encryption is based on Caesar Cipher. It is one of the simplest and most widely used encryption techniques. The Alphabet is shifted by $k$ which connects different letters together. This means for a given cipher the letters always correspond to each other.
{important}
Caesar cipher offers nowadays essentially no communication security.
Terminology¶
- Steckerbrett - "Plugboard" in front of the machine to connect two different letters together. This is an extra scramble layer
- Rotors - located between the Plugboard and the Reflector, the three Rotors left, middle and right can be set to a given position to achieve a different scrambling
- Reflector - The Reflector receives the signals from the left Rotor and sends it back to him, the Reflector scrambling can not be changed. It allows the machine to encode and decode without changing any settings.
Algorithm¶
The Enigma Machine works in a similar fashion with a more complex structure.
Rotor and Reflector Types¶
During the time the enigma was used, many different versions with different rotors, reflectors existed.
Key | Type | Setup | Usage |
---|---|---|---|
etw |
Rotor ETW | ABCDEFGHIJKLMNOPQRSTUVWXYZ | Enigma I |
ic |
Rotor IC | DMTWSILRUYQNKFEJCAZBPGXOHV | 1924 Commercial Enigma A, B |
iic |
Rotor IIC | HQZGPJTMOBLNCIFDYAWVEUSRKX | 1924 Commercial Enigma A, B |
iic |
Rotor IIIC | UQNTLSZFMREHDPXKIBVYGJCWOA | 1924 Commercial Enigma A, B |
ik |
Rotor I-K | PEZUOHXSCVFMTBGLRINQJWAYDK | February 1939 Swiss K |
iik |
Rotor II-K | ZOUESYDKFWPCIQXHMVBLGNJRAT | February 1939 Swiss K |
iiik |
Rotor III-K | EHRVXGAOBQUSIMZFLYNWKTPDJC | February 1939 Swiss K |
ukwk |
Rotor UKW-K | IMETCGFRAYSQBZXWLHKDVUPOJN | February 1939 Swiss K |
etwk |
Rotor ETW-K | QWERTZUIOASDFGHJKPYXCVBNML | February 1939 Swiss K |
i |
Rotor I | EKMFLGDQVZNTOWYHXUSPAIBRCJ | 1930 Enigma I |
ii |
Rotor II | AJDKSIRUXBLHWTMCQGZNPYFVOE | 1930 Enigma I |
iii |
Rotor III | BDFHJLCPRTXVZNYEIWGAKMUSQO | 1930 Enigma I |
iv |
Rotor IV | ESOVPZJAYQUIRHXLNFTGKDCMWB | December 1938 M3 Army |
v |
Rotor V | VZBRGITYUPSDNHLXAWMJQOFECK | December 1938 M3 Army |
vi |
Rotor VI | JPGVOUMFYQBENHZRDKASXLICTW | 1939 M3 & M4 Naval (FEB 1942) |
vii |
Rotor VII | NZJHGRCXMYSWBOUFAIVLPEKQDT | 1939 M3 & M4 Naval (FEB 1942) |
viii |
Rotor VIII | FKQHTLXOCBJSPDZRAMEWNIUYGV | 1939 M3 & M4 Naval (FEB 1942) |
beta |
Rotor Beta | LEYJVCNIXWPBQMDRTAKZGFUHOS | Spring 1941 M4 R2 |
gamma |
Rotor Gamma | FSOKANUERHMBTIYCWLQPZXVGJD | Spring 1942 M4 R2 |
a |
Reflector A | EJMZALYXVBWFCRQUONTSPIKHGD | |
b |
Reflector B | YRUHQSLDPXNGOKMIEBFZCWVJAT | |
c |
Reflector C | FVPJIAOYEDRZXWGCTKUQSBNMHL | |
bt |
Reflector B Thin | ENKQAUYWJICOPBLMDXZVFTHRGS | 1940 M4 R1 (M3 + Thin) |
ct |
Reflector C Thin | RDOBJNTKVEHMLFCWZAXGYIPSUQ | 1940 M4 R1 (M3 + Thin) |
custom |
Custom | as given by the user | |
random |
Random | ??? |
Setting¶
in order to decrypt was another machine encrypted the following needs to be true:
- The same machine with the same number and types of rotors and reflector.
- Plugboard settings needs to be the same
- Same starting position of the rotors
All these informations were changed daily, the exact setup could be found in a monthly issued codesheet
.
Imports¶
import random
Algorithm¶
class Scrambler:
def __init__(self, type_key: str = None, startpos: int = 0, custom: str = None):
"""Create a alphabet shuffle, this represents one rotor or a reflector. The configuration can be choosen from the first ever Enigma, the latest WWII Enigma machine, a Random pattern or a custom setting.
The reflector and rotor settings are according to Wikipedia https://en.wikipedia.org/wiki/Enigma_rotor_details:
Args:
type_key (str): type of enigma rotor ["EI", "alpha", "beta", "gamma", "random", "custom"]
startpos (int, optional): rotor startposition. Defaults to 0.
custom (string, optional): string of chars representing the custom configuration. Defaults to None.
"""
self.alphabet = ['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']
# Possible rotor configurations
self.configs = {
"etw": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"ic" : "DMTWSILRUYQNKFEJCAZBPGXOHV",
"iic" : "HQZGPJTMOBLNCIFDYAWVEUSRKX",
"iiic": "UQNTLSZFMREHDPXKIBVYGJCWOA",
"ik" : "PEZUOHXSCVFMTBGLRINQJWAYDK",
"iik" : "ZOUESYDKFWPCIQXHMVBLGNJRAT",
"iiik": "EHRVXGAOBQUSIMZFLYNWKTPDJC",
"ikwk": "IMETCGFRAYSQBZXWLHKDVUPOJN",
"etwk": "QWERTZUIOASDFGHJKPYXCVBNML",
"i" : "EKMFLGDQVZNTOWYHXUSPAIBRCJ",
"ii": "AJDKSIRUXBLHWTMCQGZNPYFVOE",
"iii": "BDFHJLCPRTXVZNYEIWGAKMUSQO",
"iv": "ESOVPZJAYQUIRHXLNFTGKDCMWB",
"v" : "VZBRGITYUPSDNHLXAWMJQOFECK",
"vi" : "JPGVOUMFYQBENHZRDKASXLICTW",
"vii" : "NZJHGRCXMYSWBOUFAIVLPEKQDT",
"viii" : "FKQHTLXOCBJSPDZRAMEWNIUYGV",
"beta": "LEYJVCNIXWPBQMDRTAKZGFUHOS",
"gamma" : "FSOKANUERHMBTIYCWLQPZXVGJD",
"a": "EJMZALYXVBWFCRQUONTSPIKHGD",
"b" : "YRUHQSLDPXNGOKMIEBFZCWVJAT",
"c": "FVPJIAOYEDRZXWGCTKUQSBNMHL",
"bt" : "ENKQAUYWJICOPBLMDXZVFTHRGS",
"ct" : "RDOBJNTKVEHMLFCWZAXGYIPSUQ",
"random": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", # Will be scrambled later
}
self.type_key = type_key.lower().replace(" ", "").replace("_", "").replace("-", "")
self.startpos = startpos
# get the key
if self.type_key == "custom":
self.transformation = self.getConfig(custom)
else:
self.transformation = self.getConfig(self.configs[self.type_key])
# additional scramble in case of random
if self.type_key == "random":
random.shuffle(self.transformation)
# setup initial position of rotors
self.transformation = self.rol(self.transformation, self.startpos)
self.key = self.getKey()
def getConfig(self, str_config: str):
"""Transforms the string configuration into an int array
Args:
str_config (str): string of configuration. All alphabet characters need to be represented.
Returns:
list: list of int representing the alphabet positions of the config
"""
config = []
for char in str_config:
config.append(self.alphabet.index(char))
return config
def getKey(self):
"""Get the key of the current transformation config
Returns:
str: string of characters of the current config
"""
alphabet = ['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']
key = ""
for idx in self.transformation:
key += self.alphabet[idx]
return key
def rol(self, string: str, n: int):
"""Rotating shift left of a string by n characters
example: n=2
"Test" => "stTe"
Args:
string (str): input string
n (int): number of bits to shift
Returns:
str: string rotated shift left by n chars
"""
return string[n:] + string[:n]
def passthrough(self, idx: int):
"""Pass element through (index => element)
Args:
idx (int): index of character index to return
Returns:
int: new character index
"""
return self.transformation[idx]
def passthroughRev(self, elem):
"""Reverse Passthrough, enter character index and return list index
Returns:
int: index of character index
"""
return self.transformation.index(elem)
def rotate(self):
"""Rotate the rotors by one position
"""
self.transformation = self.rol(self.transformation, 1)
def setTransformation(self, transformation: list):
"""Set manually the tranformation. E.g. to reset the machine
Args:
transformation (list): transformation list to be used
"""
self.transformation = transformation
class EnigmaMachine:
def __init__(self, nb_rotors: int = 3, rotor_types: list = ["random, random, random"], rotor_startpos: list = [random.randint(0, 25), random.randint(0, 25), random.randint(0, 25)], rotor_custom_configs: list = None, reflector_type: str = "random", plugboard_config = None, print_specialchars: bool = False):
"""Enigma Virtual Machine
nb_rotors (int, optional): number of rotors in the machine. Defaults to 3.
rotor_types (list, optional): list of types rotors types ["etw"|"ic"|"iic"|"iiic"|"ik"|"iik"|"iiik"|"ikwk"|"etwk"|"i"|"ii"|"iii"|"iv"|"v"|"vi"|"vii"|"viii"|"beta"|"gamma"|"random""custom"]. Needs to be size of nb_rotors. Defaults to ["random", "random", "random"].
rotor_startpos (list, optional): list of int representing thestart positions of the rotors. Needs to be the size of nb_rotors. Defaults to three random numbers between 0 -> 25.
rotor_custom_configs (list, optional): list of int lists representing the custom rotor configuration, only needed if "custom" type is choosen. Needs to be the size of nb_rotors if used. Defaults to None
reflector_type (str, optional): type of reflector ["a"|"b"|"c"|"bt"|"ct"|"random"]. Defaults to "random".
plugboard_config (list, optional): list of character combinations. Defaults to Nonee will result in A<->Z, B<->Y, ...
print_specialchars (bool, optional): Print characters missing by enigma. Defaults to False.
"""
self.nb_rotors = nb_rotors
self.rotor_types = rotor_types
self.rotor_startpos = rotor_startpos
self.rotor_custom_configs = rotor_custom_configs
self.reflector_type = reflector_type
self.printspecialchars = print_specialchars
if plugboard_config is None:
self.plugboard_config = ["AZ", "BY", "CX", "DW", "EV", "FU", "GT", "HS", "IR", "JQ", "KP", "LO", "MN"]
else:
self.plugboard_config = plugboard_config
# create the rotors and reflector
self.rotors = []
self.original_rotors = []
self.reflector = None
self.plugboard = None
self.setupRotors()
self.setupReflector()
self.setupPlugboard()
def setupRotors(self):
"""Setup the rotors configuration
"""
for i in range(self.nb_rotors):
if self.rotor_custom_configs is None:
self.rotors.append(Scrambler(self.rotor_types[i], self.rotor_startpos[i]))
else:
self.rotors.append(Scrambler(self.rotor_types[i], self.rotor_startpos[i], self.rotor_custom_configs[i]))
self.original_rotors.append(self.rotors[i].transformation)
def setupReflector(self):
"""Setup the reflector
"""
self.reflector = Scrambler(self.reflector_type)
def setupPlugboard(self):
"""Setup the plugboard"""
# Transform into scrambler key
alphabet = ['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']
key = " " * 26
for elem in self.plugboard_config:
key = key[:alphabet.index(elem[0])] + elem[1] + key[alphabet.index(elem[0])+1:]
key = key[:alphabet.index(elem[1])] + elem[0] + key[alphabet.index(elem[1])+1:]
self.plugboard = Scrambler("custom", 0, key)
def printEnigmaSetup(self):
"""Print Enigma setup of plugboard, rotors and reflector
"""
print("Enigma Setup")
print("============\n")
for i in range(self.nb_rotors):
print("* Rotor {}".format(i))
print(" - Type : {}".format(self.rotors[i].type_key))
print(" - Key : {}".format(self.rotors[i].key))
print(" - StartPos : {}".format(self.rotors[i].startpos))
print("* Reflector")
print(" - Type : {}".format(self.reflector.type_key))
if self.reflector.type_key == "custom" or self.reflector.type_key == "random":
print(" - Key : {}".format(self.reflector.key))
print(" - StartPos : {}".format(self.reflector.startpos))
print("* Plugboard")
print(" - Key : {}".format(self.plugboard_config))
def reset(self):
"""Restart the original rotor start positions
"""
for i in range(0, self.nb_rotors):
self.rotors[i].setTransformation(self.original_rotors[i])
def encode(self, text: str):
"""Encode and decode a string
Args:
text (str): string to encode
Returns:
str : depending on the input string, the encoded or decoded output
"""
ln = 0
encrypted_text = ""
for l in text.lower():
# get char position in alphabet
num = ord(l) % 97
if (num > 25 or num < 0):
# Special character
if (self.printspecialchars):
encrypted_text += l
else:
# encodable character
ln += 1
# pass through plugboard
num = self.plugboard.passthrough(num)
# pass through rotors
for i in range(0, self.nb_rotors):
num = self.rotors[i].passthrough(num)
# reflected by the reflector
num = self.reflector.passthrough(num)
# pass through rotors from the other side
for i in range(0, self.nb_rotors):
num = self.rotors[self.nb_rotors - i - 1].passthroughRev(num)
# pass through plugboard from the other side
num = self.plugboard.passthroughRev(num)
# Encode character
encrypted_text += "" + chr(97 + num)
# rotate the rotors
for i in range(0, self.nb_rotors):
if (ln % ((i * 6) + 1) == 0):
self.rotors[i].rotate()
return encrypted_text
Test¶
plaintext = """The Enigma machines were a series of electro-mechanical rotor cipher machines developed and used in the early- to mid-20th century to protect commercial,
diplomatic and military communication. Enigma was invented by the German engineer Arthur Scherbius at the end of World War I.
Early models were used commercially from the early 1920s, and adopted by military and government services of several countries,
most notably Nazi Germany before and during World War II. Several different Enigma models were produced,
but the German military models, having a plugboard, were the most complex. Japanese and Italian models were also in use."""
enigma = EnigmaMachine(nb_rotors=3, rotor_types=["i","beta","gamma"], rotor_startpos=[17,23,12], reflector_type="a", print_specialchars=True)
enigma.printEnigmaSetup()
print("\nPlaintext:\n{}\n".format(plaintext))
# Encrypt
enigma.reset()
encrypted_text = enigma.encode(plaintext)
print("Encrypted text:\n{}\n".format(encrypted_text))
# Decrypt
enigma.reset()
plaintext = enigma.encode(encrypted_text)
print("Plaintext:\n{}".format(plaintext))
Enigma Setup ============ * Rotor 0 - Type : i - Key : USPAIBRCJEKMFLGDQVZNTOWYHX - StartPos : 17 * Rotor 1 - Type : beta - Key : HOSLEYJVCNIXWPBQMDRTAKZGFU - StartPos : 23 * Rotor 2 - Type : gamma - Key : TIYCWLQPZXVGJDFSOKANUERHMB - StartPos : 12 * Reflector - Type : a * Plugboard - Key : ['AZ', 'BY', 'CX', 'DW', 'EV', 'FU', 'GT', 'HS', 'IR', 'JQ', 'KP', 'LO', 'MN'] Plaintext: The Enigma machines were a series of electro-mechanical rotor cipher machines developed and used in the early- to mid-20th century to protect commercial, diplomatic and military communication. Enigma was invented by the German engineer Arthur Scherbius at the end of World War I. Early models were used commercially from the early 1920s, and adopted by military and government services of several countries, most notably Nazi Germany before and during World War II. Several different Enigma models were produced, but the German military models, having a plugboard, were the most complex. Japanese and Italian models were also in use. Encrypted text: naw vhodvj ntgdxvku fvjo x oniygn zc atrwgbz-curbboxesa gwdpo fbfeco rlwjkfqv rsyqnmnsj mbw gqsa mj rrw aeksn- fi aws-20ex nhzwxal pc dlpntqn fmgyzylqqt, fwwqfxboiqe nhe wuxodrli wffwpuczdrrgy. skqxtf nrz uzzmfpxk yx rom iiehul felxiqcv zhmykn odnfujnpb ge pik mrt cv rmosi cpa d. hzffus sxghgk znlu znxm lfzjrqnxmzsr qgdg acy jgeki 1920w, ktf wkijbov xe qvbnwqbq wvh tpuqfdxtxb ummxkgjn th pocruin frzmkxpvj, ttbtc mnnddnl pnxo edjlsoh upqwqk hrt yqvuew jwvfu ikz zv. anrjcwu kjpwincbn aovlhb zxjkfy xkwd vujexemb, yifi awr qpxcmk yxbqolmj fpyzwn, ycqvqe c sdreqwfzi, kjxw kec ixvw fuyeshr. zkdpdodk ory guzixhg pysvmv dxea cooc lw hna. Plaintext: the enigma machines were a series of electro-mechanical rotor cipher machines developed and used in the early- to mid-20th century to protect commercial, kdiplomatic and military communication. enigma was invented by the german engineer arthur scherbius at the end of world war i. kearly models were used commercially from the early 1920s, and adopted by military and government services of several countries, kmost notably nazi germany before and during world war ii. several different enigma models were produced, kbut the german military models, having a plugboard, were the most complex. japanese and italian models were also in use.