How to Make the Legendary Enigma Machine with Python?

julianawrites
GapsScience
Published in
8 min readFeb 18, 2023
Picture of an Enigma machine I took at the Museum of the Second World War in Gdańsk

The Enigma machine is one of the most intriguing and mysterious inventions of the 20th century. Originally developed for commercial purposes in the early 1920s, it was quickly adopted by military and intelligence agencies as a way to encrypt secret messages during World War II.

Despite its reputation, the Enigma machine was eventually cracked by a team of Allied cryptanalysts led by Alan Turing, whose work at Bletchley Park in England is now legendary. Using a combination of mathematics, logic, and sheer persistence, Turing and his colleagues managed to decipher the Enigma messages and gain a crucial advantage in the war effort.

Today, we will take a closer look at the Enigma machine and its history and create a Python implementation that allows us to encrypt and decrypt messages just like the original device.

Background and overview of the mechanism

The Enigma machine is a marvel of mechanical engineering and encryption technology. At its core, the machine relies on a series of rotors that spin and interact with one another to produce complex substitution ciphers. When a key is pressed, an electrical current flows through the rotors, creating a unique path that transforms the input letter into an encrypted output letter.

Each Enigma machine typically had three rotors, although some models had up to eight. These rotors were interchangeable and could be arranged in any order, which made the machine highly customizable and difficult to crack. Additionally, each rotor had a different internal wiring pattern, which added an extra layer of complexity to the encryption process.

The rotors themselves were marked with the letters of the alphabet, but their internal wiring was such that pressing a key on the keyboard would cause the current to flow through the rotor and emerge at a different letter. The flow of the current was controlled by the position of the rotor, which could be adjusted using a set of knobs on the front of the machine.

In addition to the rotors, the Enigma machine also had a reflector, which was essentially a fourth rotor that bounced the signal back through the rotors in reverse order. This made the encryption process even more complex, as each letter was encrypted and decrypted multiple times using different rotors and settings.

To add an extra layer of security, the Enigma machine was also designed to rotate its rotors after each keypress, which changed the internal wiring pattern and prevented any patterns from emerging in the ciphertext. This made the machine incredibly difficult to crack, as the same letter could be encrypted in multiple different ways depending on the position of the rotors and the reflector.

The four main components of the Enigma: Four rotors, a lampboard, a keyboard and a plugboard. (Photo taken by Leif Nilsen edit by Åvald Sommervoll)

Python Implementation

First, we’ll need to define the mapping for each rotor and the mapping for the reflector. In the Enigma machine, each rotor is a spinning disk with a series of contacts on each side. Each contact corresponds to a letter in the alphabet. When a letter is pressed on the keyboard, an electrical signal is sent through the rotors, causing them to spin and altering the signal’s path through the machine. The mapping of each rotor determines how each letter is encrypted as it passes through the rotors.

# Define the rotor settings for the Enigma machine
rotor1 = "EKMFLGDQVZNTOWYHXUSPAIBRCJ" # Rotor 1 mapping
rotor2 = "AJDKSIRUXBLHWTMCQGZNPYFVOE" # Rotor 2 mapping
rotor3 = "BDFHJLCPRTXVZNYEIWGAKMUSQO" # Rotor 3 mapping
reflector = "YRUHQSLDPXNGOKMIEBFZCWVJAT" # Reflector mapping

Here, we define three rotors: Rotor 1, Rotor 2, and Rotor 3. Each rotor has a different mapping, which is represented as a string of letters. For example, Rotor 1 has the mapping “EKMFLGDQVZNTOWYHXUSPAIBRCJ.” This means that when a letter passes through Rotor 1, it will be encrypted based on the mapping of each letter in the rotor.

The reflector is used to reflect the encrypted letter back through the rotors, which helps to ensure that the encryption process is reversible. The reflector mapping is represented as a string of letters, and in this code, we’ll use the mapping “YRUHQSLDPXNGOKMIEBFZCWVJAT” as an example.

It’s worth mentioning that the rotor and reflector mappings were regularly switched up during the war to make the messages more secure.

Next, we’ll set the initial position for each rotor.

# Define the starting position for each rotor
rotor1_start = 0 # Starting position for rotor 1
rotor2_start = 0 # Starting position for rotor 2
rotor3_start = 0 # Starting position for rotor 3

In the Enigma machine, each rotor has 26 positions that correspond to the 26 letters of the alphabet. The starting position determines which letter of the rotor is aligned with the corresponding letter on the machine’s keyboard when the machine is first set up.

The starting positions for each rotor are set to 0 initially. This means that the first letter of each rotor is aligned with the corresponding letter on the keyboard. As the machine is used to encrypt text, the starting positions of the rotors will change, resulting in different substitutions being made for each letter of the alphabet.

Then, we define the plugboard for the Enigma machine.

# Define the plugboard settings for the Enigma machine
plugboard = {"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"} # Plugboard mappings

In the Enigma machine, the plugboard was an optional component that allowed the operator to swap pairs of letters before and after the rotor encryption process. This effectively increased the number of possible encryption combinations and made the machine more secure.

After that, we have to define the actual enigma function that will encrypt the text messages.

# Define the Enigma machine's encryption function
def enigma(text):
global rotor1_start, rotor2_start, rotor3_start # Access global variables inside function
# Convert the input text to uppercase and remove any spaces
text = text.upper().replace(" ", "")

# Initialize an empty list to store the encrypted text
encrypted_text = []

# Loop over each character in the input text
for char in text:
# Apply the plugboard settings to the current character
char = plugboard.get(char, char)

# Increment the starting position of the first rotor
rotor1_start = (rotor1_start + 1) % 26

# Check if the first rotor has completed a full revolution and increment the second rotor if so
if rotor1_start == 0:
rotor2_start = (rotor2_start + 1) % 26

# Check if the second rotor has completed a full revolution and increment the third rotor if so
if rotor2_start == 0:
rotor3_start = (rotor3_start + 1) % 26

# Map the current character through the first rotor
char = rotor1[(ord(char) - 65 + rotor1_start) % 26]

# Map the current character through the second rotor
char = rotor2[(ord(char) - 65 + rotor2_start) % 26]

# Map the current character through the third rotor
char = rotor3[(ord(char) - 65 + rotor3_start) % 26]

# Map the current character through the reflector
char = reflector[(ord(char) - 65) % 26]

# Map the current character back through the third rotor in reverse
char = chr((rotor3.index(char) - rotor3_start + 26) % 26 + 65)

# Map the current character back through the second rotor in reverse
char = chr((rotor2.index(char) - rotor2_start + 26) % 26 + 65)

# Map the current character back through the first rotor in reverse
char = chr((rotor1.index(char) - rotor1_start + 26) % 26 + 65)

# Apply the plugboard settings to the final encrypted character
char = plugboard.get(char, char)

# Add the final encrypted character to the encrypted text list
encrypted_text.append(char)

# Join the encrypted text list into a single string
encrypted_text_str = "".join(encrypted_text)

# Return the encrypted text
return encrypted_text_str

The input text is first converted to uppercase, and any spaces are removed. Then, an empty list encrypted_text is initialized to store the encrypted characters.

Next, the function loops over each character in the input text. For each character, the function first applies the plugboard settings to the character using the get method of the plugboard dictionary.

The starting position of the first rotor is then incremented by 1, and if the starting position reaches 26 (i.e., completes a full revolution), the starting position of the second rotor is incremented by 1. If the starting position of the second rotor also completes a full revolution, the starting position of the third rotor is incremented by 1.

The current character is then mapped through each of the three rotors, the reflector, and then back through the rotors in reverse order. The mapping is done using the index method of the rotor lists, which returns the index of a character in the rotor list. The ord function is used to convert a character to its ASCII code, and chr to convert an ASCII code back to a character.

Finally, the function applies the plugboard settings to the final encrypted character and appends it to the encrypted_text list.

After all characters in the input text have been processed, the encrypted_text list is joined into a single string using the join method, and the encrypted text string is returned.

Lastly, to see if the code actually works (hehe), we will apply it to a message and print out the encrypted text.

# Apply the Enigma machine's encryption function
encrypted = enigma("life is a simulation")
# Print the generated encrypted text
print("Encrypted Text:", encrypted)

For this particular configuration of the Enigma machine, we get the following result:

Encrypted Text: VNPDFTLXHTZRXRLLR

Hey there, congrats on writing this Enigma machine code! You must be feeling like a real spy now, huh? I mean, who needs fancy encryption software when you can just use a code from the 1940s? Just don’t forget to adjust your rotor settings every day, or else your messages might end up in the hands of the enemy. But hey, who needs security anyway? It’s not like we’re living in a world filled with cyber threats and identity theft.

--

--

julianawrites
GapsScience

i do everything from professional sports to entrepreneurship and language learning. currently taking a gap year with baret scholars and building talpact.com ❤️