Skip to content

Repository files navigation

@gykh/enigma

Enigma Machine Simulator

A historically accurate, zero-dependency Enigma machine in Node.js: rotors I–VIII, reflectors, ring settings, plugboard and the double step. Supports Strings, Buffers, and Streams.

npm version npm downloads CI Tests node version TypeScript License: MIT Zero Dependencies

Try it live in your browser → Set the rotors, plug the cables and type, no install needed. · Watch the 49s video

More short package videos on @gykhdev. Sibling packages: @gykh/morse, @gykh/vigenere-cipher, @gykh/caesar-cipher, @gykh/cat-facts. All packages: @gykh on npm.


Contents

Features

  • 🚀 Zero Dependencies: Pure native Node.js implementation.
  • 🎯 Historically accurate: Real rotor wirings I–VIII, reflectors A/B/C, ring settings, plugboard and the middle-rotor double step. Tested against a genuine 1941 German Army message.
  • 🔁 Reciprocal: The same settings encrypt and decrypt.
  • ⌨️ Key by key: The Enigma class lets you press keys and watch the rotors turn.
  • 🔄 Multi-Format Support: Strings, Buffers, and Node.js Streams.
  • 🌊 Chunk-safe streams: The rotors keep turning across chunks, so streamed output matches encrypting all at once.
  • 📦 Dual ESM & CommonJS with full TypeScript types.

Install

pnpm add @gykh/enigma
# or
npm install @gykh/enigma
# or
yarn add @gykh/enigma

Requires Node.js 18 or newer.


Usage

Letters A-Z / a-z are enciphered and keep their case. Spaces, digits and punctuation are preserved and do not turn the rotors.

1. Strings (ESM & CommonJS)

// ESM
import { encryptString, decryptString } from "@gykh/enigma";

// CommonJS
// const { encryptString, decryptString } = require("@gykh/enigma");

const settings = {
  rotors: ["II", "IV", "V"],
  reflector: "B",
  rings: "BUL",
  positions: "BLA",
  plugboard: "AV BS CG DL FU HZ IN KM OW RX",
};

encryptString("Attack at dawn", settings); // "Evhqzv rr blri"
decryptString("Evhqzv rr blri", settings); // "Attack at dawn"

encryptString("AAAAA"); // "BDZGO" with the default settings

2. Press keys one at a time

import { Enigma } from "@gykh/enigma";

const machine = new Enigma({ positions: "ADU" });

machine.press("A"); // "E"  rotors now ADV
machine.press("A"); // "Q"  rotors now AEW
machine.press("A"); // "I"  rotors now BFX: the double step
machine.positions;  // "BFX"

machine.reset();      // back to ADU
machine.type("HELLO"); // "IBXXX": types a string, rotors keep turning

3. Decrypt a real WWII message

This is the first part of an Operation Barbarossa message from 7 July 1941:

const key = { rotors: ["II", "IV", "V"], reflector: "B", rings: [2, 21, 12], plugboard: "AV BS CG DL FU HZ IN KM OW RX" };

// The header carried the message key KCH, enciphered at WXC
decryptString("KCH", { ...key, positions: "WXC" }); // "BLA"

decryptString("EDPUD NRGYS ZRCXN UYTPO MRMBO ...", { ...key, positions: "BLA" });
// "AUFKL XABTE ILUNG XVONX KURTI NOWAX ..."  (Aufklärungsabteilung von Kurtinowa)

4. Buffers

import { encrypt, decrypt } from "@gykh/enigma";
import { readFile } from "fs/promises";

const buffer = await readFile("sample.txt");

const encryptedBuffer = encrypt(buffer, settings);
const decryptedBuffer = decrypt(encryptedBuffer, settings);

console.log(buffer.equals(decryptedBuffer)); // true

5. Streams (For Large Files)

For files or streams exceeding 1000 characters/bytes, use the streaming transform classes:

import { EncryptTransform, DecryptTransform } from "@gykh/enigma";
import fs from "fs";
import { pipeline } from "stream/promises";

await pipeline(
  fs.createReadStream("large-input.txt"),
  new EncryptTransform(settings),
  fs.createWriteStream("large-encrypted.txt")
);

await pipeline(
  fs.createReadStream("large-encrypted.txt"),
  new DecryptTransform(settings),
  fs.createWriteStream("large-decrypted.txt")
);

Settings

Every field is optional. Omitted fields use the defaults.

Field Meaning Values Default
rotors Walzenlage: rotor order, left to right three different of I–VIII ["I", "II", "III"]
reflector Umkehrwalze A, B, C "B"
rings Ringstellung: ring settings "AAA" or [1, 1, 1] (1–26) "AAA"
positions Grundstellung: starting rotor positions "AAA" or [1, 1, 1] "AAA"
plugboard Steckerbrett: letter pairs "AV BS CG" or ["AV", "BS"], each letter once none

API Reference

encryptString(str, settings?) / decryptString(str, settings?)

  • str (string, max 1000 chars): Input text.
  • settings (object): See Settings.
  • Returns: string. Both functions do the same thing, since Enigma is reciprocal.

encrypt(buffer, settings?) / decrypt(buffer, settings?)

  • buffer (Buffer, max 1000 bytes): Input buffer.
  • Returns: Buffer

new Enigma(settings?)

A stateful machine whose rotors keep turning between calls.

  • .press(letter): Press one key, returns the lit lamp.
  • .type(str): Encipher a string (max 1000 chars) from the current positions.
  • .positions: Rotor window letters, e.g. "BFX".
  • .reset(): Return to the starting positions.

new EncryptTransform(settings?) / new DecryptTransform(settings?)

Node.js stream.Transform subclasses. The rotors keep turning between chunks.

Note

The string and buffer functions enforce an input limit of 1000 characters/bytes. For larger data, pipe through EncryptTransform / DecryptTransform.


How the machine works

  1. Step. Each key press turns the right rotor one place before the current flows. When a rotor passes its notch, it turns the rotor to its left. The middle rotor also turns itself when it sits on its notch, so it moves on two presses in a row: the famous double step.
  2. Plugboard. The letter is swapped with its cable partner, if it has one.
  3. Rotors, right to left. Each rotor is a scrambled wiring, offset by its position minus its ring setting.
  4. Reflector. It sends the current back through the rotors along a different path.
  5. Rotors left to right, then the plugboard again. The lamp lights up.

Because the reflector pairs letters up, the machine is reciprocal, and a letter can never encrypt to itself. That flaw helped Allied codebreakers at Bletchley Park break Enigma. Use this package for learning, puzzles and CTFs, not for protecting data.


Developer


License

MIT © 2026 get-your-knowledge-here

About

⚡ Historically accurate, zero-dependency Enigma machine in Node.js: rotors I–VIII, reflectors, ring settings, plugboard & double-stepping. Strings, Buffers & Streams.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages