Saturday, February 6, 2021

Enigma in Software

 

My Software Enigma Machine Simulator
My Software Enigma Machine Simulator

In a previous post, I shared my long-held fascination with the Enigma cipher machine used by the German military during World War II.  Once I learned in detail how the machine functions, I decided to test my understanding by coding a working model of it.  I settled on HTML 5 and JavaScript for my platform of choice.  This way, I could run the entire simulator on the client, the JavaScript runtime of the browser.  This should make it accessible to anyone who wants to fool around with it.  It can be accessed from this link.  If you want to review the source code, you can find it here on GitHub.

The main entry point for the simulator is a file called index.html.  It contains the UI, as well as the JavaScript that drives the interaction with the machine.  The UI is a basic bootstrap styled simple form-based GUI.  I'll not go into much if any detail on this.  Instead I'll focus on the parts of the solution that are specific to the simulation of the Enigma.  The workings of the machine itself are abstracted inside a set of JavaScript classes that represent the major components of the machine.  In the HTML head section you find these classes being imported via script tags and the objects being instantiated.

<script src="enigma.js"></script>
<script src="machine.js"></script>
<script src="rotor.js"></script>
<script src="plugboard.js"></script>
<script type="application/javascript">
var enigma = new Enigma();
var machine = new Machine('M3',
new Rotor('B', enigma.arrReflector["B"], 1, 1, [0,0]),
new Rotor('III', enigma.arrRotors[3], 1, 1, enigma.arrKnockpoints[3]),
new Rotor('II', enigma.arrRotors[2], 1, 1, enigma.arrKnockpoints[2]),
new Rotor('I', enigma.arrRotors[1], 1, 1, enigma.arrKnockpoints[1]),
null,
new Plugboard([]));

The Enigma object contains constants which define things like the configuration of the standard internal components of the M3 and M4 German Naval Enigma Machines.  I chose these two models because they can be configured to encipher / decipher any message from any variant of the Enigma machine.  These components are primarily the rotors and their "knock points" or notches which control when they rotate, and the reflectors.  If you need a primer on Enigma Machine parts or how the machine is operated, see this site.

The bulk of the work is done by the Machine object.  When you construct it, you pass in the type of machine, either 'M3' for a three-rotor Naval Enigma or 'M4' for the four-rotor variant introduced late in the war, as the first parameter.  The next 5 parameters must be of type Rotor.  These are the rotor stack of the Enigma, which is made up of three (or four in the case of the M4) rotors from the collection of eight possible rotors, and one of two reflectors.  The final parameter is the set of plugboard mappings.

Rotors themselves are objects because they have specialized state and configuration.  First, the internal wiring varies by rotor.  These are fixed for each numbered rotor, so the wiring can be found in the Enigma.arrRotors array.  Second, each rotor can have notches that allow the rotor to rotate when in a particular position.  These positions are referred to as knock points.  They are fixed for each numbered rotor and their positions are stored in an array of positions in the Enigma.arrKnockpoints array.  The rotor also has an outer ring of numbers which identifies the current position of the rotor.  When configuring the machine for use, the operator can rotate the outer ring, thus shifting the position of the outer ring, altering the mapping, shifting it actually.  This is known as the ring setting.  Lastly, the initial position of the rotor is required.  It can be changed by the machine operator without removing the rotor stack, but it is still part of the initial configuration.

To construct a Rotor object for each rotor or reflector, you need to pass the rotor's name, used for display purposes, the wiring map for the rotor, the ring setting, the initial position, and lastly the array of notches.  An empty array is a valid option, and it needs to be.  The reflector is essentially a rotor with a fixed position, a fixed ring setting, and no knock points.  The first rotor passed into the Machine constructor is actually this reflector.  The reflector mappings are stored in the Enigma.arrReflector array.  The next three (or four in the case of the M4) Rotor objects represent the right, middle, and left rotors in the stack.  If you are constructing an M4 Machine, you must also pass a 4th rotor, often called the greek rotor.  It is smaller, doesn't rotate, but has mapping and ring settings, and initial position like any other rotor.  Only two rotors are allowed in this position, 'b' (beta) and 'g' (gamma).  I don't enforce this in the code in case you want to make your machine even more difficult to crack than the original Enigma.

The final parameter defines the pairs of letters swapped by the plugboard.  In the example above, the plugboard has no letters swapped, thus an empty array is passed to the plugboard constructor.

Once you have created a Machine, the "magic" of encoding is done with the Machine.evaluate() method.  It is pretty short and simple, much like the Enigma itself, but makes for a very challenging cipher to crack.  Just ask Alan Turning!

evaluate(inputLetter) {
// rotate the rotors
this.rotateRotors();

// map the input letter through the plugboard
var l = this.plugboard.mapLetter(inputLetter);

// map the resulting letter into the rotors
l = this.rRotor.mapLetter(l);
l = this.mRotor.mapLetter(l);
l = this.lRotor.mapLetter(l);
if (this.zRotor) {
l = this.zRotor.mapLetter(l);
}
l = this.reflector.mapLetterReflector(l);
if (this.zRotor) {
l = this.zRotor.mapLetter(l, true);
}
l = this.lRotor.mapLetter(l, true);
l = this.mRotor.mapLetter(l, true);
l = this.rRotor.mapLetter(l, true);

// map back through the plugboard
var l = this.plugboard.mapLetter(l);

// return the resulting encoded letter
return l;
}

When you press a key on the keyboard, that key is passed into the evaluate() method as its inputLetter parameter.  Before evaluating, the machine rotates its rotor stack.  Why before encoding?  The mechanical process of pressing the key down pushes on a bar that actuates prongs that actually rotate the rotors, if they are on a notch (knockpoint), so they have to be rotated first.  The rest of the evaluate() method follows the same path through the virtual machine that current flows through the actual machine.  First you map the letter through the plugboard.  The Plugboard object's mapLetter() method knows how to swap any mapped letters.  Next you map the resulting letter through the right, middle, and left rotors.  If there is a greek rotor in this machine, you map through it as well.  Then, you map the letter through the configured reflector, and you reverse the process back out.  Simple, right?  Well, the complexity is hidden mainly in the Rotor.mapLetter() method.  Lets take a look at that next.

mapLetter(inputLetter, reverse) {
// given an input letter, return the output letter for
// the current rotor position

var mappedChar = "";
var outputPos;

var inputPos = this.plaintext.indexOf(inputLetter);
// adjust for position
var adjPos = inputPos + (this.position - 1);
adjPos = this.safetyPos(adjPos);

// adjust for Ring Setting
adjPos = adjPos - (this.ringSetting - 1);
adjPos = this.safetyPos(adjPos);
if (! reverse ) {
mappedChar = this.wireing.charAt(adjPos);
outputPos = this.plaintext.indexOf(mappedChar);
} else {
mappedChar = this.plaintext.charAt(adjPos);
outputPos = this.wireing.indexOf(mappedChar);
};

// readjust for Ring Setting
outputPos = outputPos + (this.ringSetting - 1);
outputPos = this.safetyPos(outputPos);

// readjust for position
outputPos = outputPos - (this.position - 1);
outputPos = this.safetyPos(outputPos);
return this.plaintext.charAt(outputPos);
}

The comments in this method explain what is going on with each block of code.  Basically, you convert the character passed in to its position in the alphabet.  Then you adjust the current position taking into consideration the current position of the rotor.  As the rotor turns, its relative position is increased.  Next you adjust for the ring setting.  When you rotate the ring setting, you are subtracting from the relative position.  The internal wiring of the rotors is handled next.  You have to take into account which side of the rotor is getting the current.  Is the letter coming in from the keyboard or on its way back out of the rotor stack on its way to the lampboard?  The reverse parameter lets the mapLetter() method know.  Finally, you readjust ring setting and position, and return the newly mapped letter.

In addition, let me call your attention to the safetyPos() method of the Rotor class.  The mapLetter() function calls this method every time it adjusts the position.  Why?  Because the rotors are round, and thus they loop back on themselves.  There is no letter 27, so what happens when you adjust position for a letter and the new position is >26 or <=0?  That is right, you have to start back over at the top or the bottom of the list respectively.  safetyPos() does this for the class.

Lastly, you may have noticed that on reflectors, mapLetter() isn't called by Machine.evaluate(), instead mapLetterReflector() is called.  Reflectors don't have ring settings or positions to adjust for so their mapping function is much more simplified.  I could have implemented this better.  Future revisions will refactor this for better code reuse.

Phew!  This took quite some time to work out.  Without the help of the models I built, it would have been very hard to work this out.  I have much respect for the folks at Bletchley Park who figured all this out with paper and pencil!

The next article in this series will explore the next phase of my obsession with the Engima as I port my code to Python so it can run on the Raspberry Pi, and begin to allow the machine to take shape in the real world.  Stay tuned.

No comments:

Post a Comment

Enigma in Software

  My Software Enigma Machine Simulator In a previous post, I shared my long-held fascination with the Enigma cipher machine used by the Germ...