Skip to main content

Enigma — A World War II Enigma Machine Simulator in Python

Enigma is a zero-dependency Python package and command-line tool that faithfully simulates the German Enigma cipher machine used during World War II. It reproduces the machine's full electromechanical encoding path — plugboard, stepping rotors, ring settings, and reflector — and can trace every character's journey through the machine, making it equally useful as a working cipher tool and as an educational aid for understanding how the Enigma actually worked.

Because the Enigma cipher is reciprocal, the same machine configuration both encodes and decodes: run ciphertext through a machine with identical settings and the original plaintext comes back out.


Table of Contents


How the Simulation Works

Each keypress on a real Enigma machine sent an electrical signal through a fixed sequence of components, and this simulator models every stage of that path:

Input letter
   │
   ▼
Plugboard ──▶ Rotors (right → left, forward pass) ──▶ Reflector
                                                          │
Output letter ◀── Plugboard ◀── Rotors (left → right) ◀───┘
  1. Rotor stepping — before the signal travels anywhere, the rotors advance. The right-hand rotor steps on every keypress; when a rotor sits at its notch position it carries the next rotor with it. The simulator also reproduces the machine's famous double-stepping anomaly, where the middle rotor steps twice on consecutive keypresses. In four-rotor configurations, the fourth (leftmost) rotor never steps — exactly as on the historical naval machines.
  2. Plugboard (Steckerbrett) — if the letter is connected by a plug lead, it is swapped with its partner before entering the rotors, and again on the way out.
  3. Forward pass — the signal passes through each rotor from right to left. Each rotor applies its internal wiring, offset by its current position and ring setting.
  4. Reflector — the signal is reflected back through a fixed pairwise substitution. This is what makes the cipher reciprocal (and guarantees no letter ever encodes to itself).
  5. Backward pass — the signal returns through the rotors left to right, using each rotor's inverse wiring, then exits through the plugboard as the enciphered letter.

Features

  • Historically accurate rotor mechanics — correct notch-driven stepping, the double-stepping anomaly of the middle rotor, and ring settings (Ringstellung) that shift both the wiring offset and the effective notch position.
  • Three- and four-rotor configurations — mount any number of rotors; four-rotor naval (M4-style) setups work out of the box, with the fourth rotor correctly held stationary.
  • Full plugboard support — connect up to 13 plug leads. The plugboard validates its own state: duplicate leads and leads reusing an already-occupied letter are rejected with a clear error.
  • Custom hardware — override any rotor's notch position, or supply your own 26-letter reflector wiring as a list instead of a named reflector.
  • Strict input validation — rotor names, ring settings, positions, reflector wirings, and plug leads are all checked at construction time, so misconfigurations fail immediately with a message explaining what is wrong rather than silently producing garbage ciphertext.
  • Step-by-step encoding traces — at the highest verbosity level, the simulator prints the complete path of every character through every component (see Verbosity and Tracing), which makes it easy to follow — or teach — exactly how the machine transforms a letter.
  • Two interfaces, one engine — use the reusable Enigma class from Python, or the enigma console command from the terminal. Both are backed by the same components (Rotor, RotorAssembly, Reflector, Plugboard, PlugLead), each of which can also be used and studied independently.
  • No dependencies — pure Python standard library, Python 3.7+.

Supported Rotors and Reflectors

The simulator ships with the historical wirings for the following components:

Rotor Notch position Notes
I Q Wehrmacht/Luftwaffe rotor
II E Wehrmacht/Luftwaffe rotor
III V Wehrmacht/Luftwaffe rotor
IV J Wehrmacht/Luftwaffe rotor
V Z Wehrmacht/Luftwaffe rotor
Beta none Naval thin rotor — never drives a neighbour
Gamma none Naval thin rotor — never drives a neighbour
Reflector Description
A Reflector A wiring
B Reflector B wiring (the most widely used, default)
C Reflector C wiring
custom Any 26-letter wiring passed as a Python list

Notch positions can be overridden per rotor via the notch_positions argument, and every wiring is validated to contain each letter of the alphabet exactly once.

Installation

Install from PyPI:

pip install enigma-sim

The distribution is named enigma-sim, but the import name is simply enigma:

from enigma import Enigma

Or install from source for development:

git clone https://github.com/NaumanHSA/enigma.git
cd enigma
pip install -e .

Python Usage

Basic encoding

from enigma import Enigma

machine = Enigma(
    rotors="I II III",              # left-to-right rotor order
    ring_settings="01 01 01",       # Ringstellung, 1–26 per rotor
    initial_positions="A A Z",      # starting letter shown in each window
    reflector="B",
    plugleads="HL MO AJ CX BZ SR NI YW DG PK",
)

ciphertext = machine.encode("HELLOWORLD")
print(ciphertext)  # RFKTMBXVVW

Decoding — the reciprocal property

Decryption is just encryption with the same settings. Build a machine with the identical configuration and feed it the ciphertext:

receiver = Enigma(
    rotors="I II III",
    ring_settings="01 01 01",
    initial_positions="A A Z",
    reflector="B",
    plugleads="HL MO AJ CX BZ SR NI YW DG PK",
)
print(receiver.encode("RFKTMBXVVW"))  # HELLOWORLD

Note: rotor positions advance as characters are encoded, so create a fresh Enigma instance (or one with the original settings) to decode — a machine that has already processed text is no longer at its starting position.

Four-rotor naval configuration

machine = Enigma(
    rotors="IV V Beta I",
    ring_settings="18 24 03 05",
    initial_positions="E Z G P",
    reflector="A",
    plugleads="PC XZ FM QA ST NB HY OR EV IU",
)
plaintext = machine.encode("BUPXWJCDPFASXBDHLBBIBSRNWCSZXQOLBNXYAXVHOGCUUIBCVMPUZYUUKHI")

Custom reflector wiring

custom_reflector = list("YRUHQSLDPXNGOKMIEBFZCWVJAT")
machine = Enigma(rotors="I II III", reflector=custom_reflector)

Enigma constructor reference

Parameter Type Default Description
rotors str "I II III" Space-separated rotor names, ordered left to right as mounted in the machine.
ring_settings str "01 01 01" One numeric ring setting (1–26) per rotor.
initial_positions str "A A A" One starting letter per rotor — the letter visible in each rotor window.
notch_positions str None Optional per-rotor notch override (e.g. "Q E V"). When omitted, each rotor's historical notch is used.
reflector str | list "B" Named reflector ("A", "B", "C") or a custom 26-letter wiring list.
plugleads str None Space-separated letter pairs (e.g. "AB CD EF"). Omit for no plugboard connections.
verbose int 0 0 silent, 1 prints the machine configuration, 2 additionally traces every character.

encode(input_string) accepts uppercase English letters (A–Z) and returns the enciphered string. Any other character raises a ValueError.

More runnable examples, including the assertions used to verify the machine against known Enigma outputs, live in example_simulations.py.

CLI Usage

Installing the package registers an enigma console command:

enigma "HELLO" --rotors="I II III" --initial-positions="A A A" --ring-settings="01 01 01" --plugleads="HA YZ" --verbose=2
Argument Type Default Description
text string (required) The text to encode — uppercase English letters only.
--rotors string "I II III" Space-separated rotor names, left to right.
--ring-settings string "01 01 01" One ring setting (1–26) per rotor.
--initial-positions string "A A A" One starting letter per rotor.
--notch-positions string None Optional notch overrides (e.g. "Q E V"); historical notches are used by default.
--reflector string "A" Reflector to mount: A, B, or C. Note the CLI defaults to A while the Python API defaults to B.
--plugleads string None Plugboard letter pairs (e.g. "AB CD EF").
--verbose int (0–2) 1 0 silent, 1 prints the machine configuration and result, 2 adds full per-character traces.

Verbosity and Tracing

The simulator's most distinctive feature is its transparency. At verbose=1 it prints the resolved machine configuration — each rotor's wiring, ring setting, effective notch, starting position, and placement relative to its neighbours. At verbose=2 it additionally logs the complete path of every character:

Input char → Plugboard → Rotors (forward) → Reflector → Rotors (backward) → Plugboard → Encoded char

Example output for enigma "HELLO" --plugleads="HA YZ" --verbose=2:

########################################## Rotors Settings ##########################################
----------------------------------------------------------------------------------------------------
Rotor    | Ring  | Initial Pos  | Notch        | Forward Mapping
----------------------------------------------------------------------------------------------------
I        | 1     | A (index 0 ) | Q (index 16) | EKMFLGDQVZNTOWYHXUSPAIBRCJ
II       | 1     | A (index 0 ) | E (index 4 ) | AJDKSIRUXBLHWTMCQGZNPYFVOE
III      | 1     | A (index 0 ) | V (index 21) | BDFHJLCPRTXVZNYEIWGAKMUSQO
----------------------------------------------------------------------------------------------------

######################################### Rotors Placement #########################################
----------------------------------------------------------------------------------------------------
Left Rotor | Current Rotor | Right Rotor
----------------------------------------------------------------------------------------------------
N/A        | I             | II
I          | II            | III
II         | III           | N/A
----------------------------------------------------------------------------------------------------

##################################### Characters Encoding Paths #####################################
----------------------------------------------------------------------------------------------------
INPUT: H
Plugboard (HA)  : H => A
Forward   (III) : A -> +1 => B -> wiring => C -> -1 => C
Forward   (II)  : C -> +0 => C -> wiring => D -> -0 => D
Forward   (I)   : D -> +0 => D -> wiring => F -> -0 => F
Reflector (A)   : F => L
Backward  (I)   : L -> +0 => L -> inverse wiring => E -> -0 => E
Backward  (II)  : E -> +0 => E -> inverse wiring => Z -> -0 => Z
Backward  (III) : Z -> +1 => A -> inverse wiring => T -> -1 => S
Plugboard (N/A) : S => S
ENCODED: S
----------------------------------------------------------------------------------------------------
... (one block per input character)

######################################### Encoding Complete #########################################
Input String:   HELLO
Encoded String: SCUBR
####################################################################################################

Each trace line reads left to right: the incoming letter, the shift applied for the rotor's current position, the substitution through the rotor's wiring (or inverse wiring on the way back), and the shift removed on exit.

Project Structure

enigma/
├── enigma/
│   ├── enigma.py               # Enigma class — input validation, machine assembly, encode loop
│   ├── cli.py                  # argparse-based console entry point (`enigma` command)
│   ├── common/
│   │   └── base.py             # Baseclass: rotor/reflector wirings, notch table, logging helpers
│   └── components/
│       ├── rotor.py            # Single rotor: wiring, ring setting, stepping, notch logic
│       ├── rotors_assembly.py  # Rotor bank + reflector: stepping rules and signal routing
│       ├── reflector.py        # Named and custom reflectors
│       ├── plug_board.py       # Plugboard managing the set of connected leads
│       └── plug_lead.py        # A single two-letter plug lead
├── example_simulations.py      # Verified examples, including a full decoding demo
├── setup.py / pyproject.toml   # Packaging configuration
└── README.md

License

This project is licensed under the MIT License.

Author

Nauman Ahsan — Machine Learning Engineer & Software Developer LinkedIn · naumanhsa965@gmail.com

Acknowledgements

Inspired by historical research on the German Enigma machine and its cryptographic mechanisms. The rotor and reflector wirings reproduce the documented wirings of the historical machines. This simulator is intended for educational and illustrative purposes.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

enigma_sim-0.1.0.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

enigma_sim-0.1.0-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

File details

Details for the file enigma_sim-0.1.0.tar.gz.

File metadata

  • Download URL: enigma_sim-0.1.0.tar.gz
  • Upload date:
  • Size: 23.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for enigma_sim-0.1.0.tar.gz
Algorithm Hash digest
SHA256 484879315d0b9fc8ee8ea41b7c7e5915d849fafdba15085b57e3bfc1d64ab081
MD5 95662ba9750d220810d3be1673e2e666
BLAKE2b-256 aa03177f5445c069ce601f1f6721db64cea9ed94602d0a4148ee1b931454e482

See more details on using hashes here.

File details

Details for the file enigma_sim-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: enigma_sim-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.13

File hashes

Hashes for enigma_sim-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 78fe7780f53022018d7bf8e7dcbccd1dc387556904f9573cc72af1d2200041dc
MD5 2079e22276c2baa32fa159d0168fe141
BLAKE2b-256 1ffc5db3b35c4e0ccb5c721259957502d77a019aa55799a8c314d845d5881324

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page