jgandrews.com


Golang Enigma

Yet another Enigma Machine Simulator

If you search Github you will find thousands of Enigma Machine implementations, including several in Go. I have written several versions in the past myself. Mostly very naive implementations however. In any case, why write yet another one?

Like most things on this site, I wanted to do it for my own satisfaction first, but for two other reasons:

  1. Practise more Golang and in particular focus on testing
  2. As an accompaniment to my Enigma article in order to better understand the workings of the machine in detail and run some tests and examples for use in the article

I’d like to talk about some things I learned while writing the simulator.

You can find the code for my Go Enigma on my GitHub site at: https://github.com/andrewsjg/go-enigma

Note that it is very rough and ready and will probably get polished up a bit. See the To Do section below for some comments on what would be good to polish off.

Why Go?

The first thing I will say here is that the goal of this project was not to write ‘Idiomatic Go’ (a subject that makes me shudder and we may return to!), but rather to build an Enigma Machine model by implementing the individual components and composing them into a working machine. Having done quite a bit of playing around with Go I could visualise how to do it using Go quite easily.

I am a huge fan of Go for a few reasons, but primarily I like how clean and simple the language is. It seems to gel with my way of thinking and reasoning about things so it is for me the most productive way to implement an idea.

That said, I’ll be the first to admit that I use very little ‘Go specific’ features for this project. But I was able to get a testable (see below) and working model up and running from scratch very quickly. I am no Go expert so it is quite something that Go just gives me the confidence to be able to build things like this and take things from simple ideas floating in my brain to actual implementation without too much struggle.

For later projects I will try to implement things using more of the language features but for something like this there isn’t much need to get to fancy. It’s great just to get from zero to working thing in a quick and enjoyable way.

Implementing Enigma - What I learnt

The Concept

As I mentioned above, I wanted to implement all the parts of the machine (rotors, reflector, plugboard etc) and their behaviours in code and wire them together like a real machine and it would functionally replicate the real machine.

To do this I wanted to implement the components in a way that directly mimicked the real component and not optimise in any way using any coding ‘shortcuts’.

Much like what will be discussed in the testing section, the Enigma Machine was a good model to use to build a very modular application. I knew quite well how everything should behave and how everything interfaced so it was quite easy to model in a structure way.

However, not everything worked as I had it planned out. Below I discuss what I learnt.

What I Learnt about the Enigma Machine

Some of the design decisions I made were not optimal. A key one was that I wanted to use an array of alphabet characters for the rotor wiring. I liked the fact that I could see in my code the wiring order visually. Practically it would have been better just to use integers (1-26) everywhere. This would have made a lot of the operations easier. Not using integers, meant I had to do some otherwise unnecessary gymnastics in the code to translate from letter to number and back again in certain circumstances, such as the reverse path through the rotors discussed below.

I could have changed this relatively easily, but I stuck with letters because I think it is still easier to look at the code for the model and understand what is going on.

I could also have modelled the rotors using a simple substitution cipher implementation for each rotor. But when I mapped out the model I was thinking about the mechanical operation of the rotors and not what the logical effect of them was. It would have been easier and cleaner to just implement the logical effect of the machine rather than write analogs of the mechanical components in code. But that wasn’t what I wanted to do. I wanted to model everything as I understood them in the real machine and combine them. My theory was that by doing that the ‘real’ action of the machine would be reflected in my code and my implementation.

Rotor behaviour

I used an array of 26 characters for the rotor wiring. Each index of the array represented a place in the alphabet from A to Z. 0 = A, 1 =B, 2 = C etc.

I assumed I could model the rotor and its wiring and just plug that into the machine model and it would magically work. This is kind of true, but what I failed to recognise initially was that when the signal returns back through the rotors via the reflector (see my Enigma article for context here) it goes back through the wiring in the reverse direction. Seems obvious but it didn’t occur to me straight away.

I assumed I could just look at the input letter and reference the wiring array to get the enciphered letter. This is how it works going right to left through a rotor. Going left to right that logic doesn’t work. A real rotor wiring, for example, wires A to N, but N is not wired to A. This means if you simply reference the wiring array on the return path enciphering and deciphering won’t work.

What needs to happen in the model is the wiring needs to be reversed for the return path. In effect the letter represented by the array index needs to be come the array value and the value needs to become the wiring array index. This is where I paid the price for not using integers everywhere!

I solved this by generating a reverse wiring on the fly for the return path. This way I didn’t need to store a reverse wiring array in my rotor model. I didn’t like the idea of storing a reverse wiring array in my rotor model because it meant the rotor model didn’t look like the real thing. i.e. the reverse path effect is a behaviour of the rotor and not a fixed feature of the real thing.

Some implementations of Enigma machines I have seen do store the reverse wiring for this purpose. This is a valid approach and it’s probably more efficient to do it this way. But in my concept it would look out of place. So I generate it on the fly.

Another solution I had was to implement the rotor encoding as a behaviour. I have methods that do the encoding right to left and left to right through the rotor. I don’t use this in the version on GitHub, but it is something I might revisit.

All this is to say that when implementing the rotors I learnt a lot about how the rotors work practically, about the ‘best’ way to represent them in code and that what just works by default in the mechanical machine might need some special handling in code. I learnt about creating logical analogs to physical things!

The Alphabet Rings

I knew exactly how to implement the alphabet rings. They are simply an offset on the wiring array. As in the real machine the rings have no effect on the wiring, they just translate the orientation of the rotor with regard to the wiring (again refer to my Enigma article for further detail).

This all worked perfectly except that you need to add AND subtract the effect of the translation when referencing the wiring array as the signal moves across all the rotors. The ring settings change how each rotor is positioned relative to its neighbour. This took me a long time to get my head around because the analogy in code is not easily apparent on the physical machine (at least it wasn’t to me!).

The Turnover Notch

This I think was the best thing I learnt from doing the simulator. Again, this is probably obvious to some, but it really became clear when I went to implement my version: the turnover notches on the rotors effect the rotor to the right. That is to say, the notches don’t tell a rotor when to rotate, they ’tell’ the machine when to rotate the adjacent rotor when the notch rotates into play.

I think I have implemented this badly in my model because I misunderstood this conceptually and it needs revisiting. My machine works, but the implementation is not great.

Modular Arithmetic

It was not in the least a surprise to me that modular arithmetic would be required for the rotors. They cycle round 26 positions which means modular arithmetic is of course critical. What I did forget though was that there would be circumstance when translating the effect of the alphabet ring would result in negative numbers. To cancel these cases out (because a negative number makes no sense and will cause array bounds issues) I needed to ADD 26 to a result before doing the mod 26 operation to get the correct array index.

Anyway, this is a lot of rambling. What I hope is that if someone else is implementing an Enigma Machine and they hit these little snags, they find this article and can get to the answer quicker than I did!

To Do

Like a lot of my development projects I managed to implement what some might call a ‘minimum viable product’ and not much else yet. What I want to do to polish it up a bit is:

  • Fully test to ensure things like the double turnover effect work
  • Rework the turnover notch logic. I think this will probably be the cause of a double turnover not working correctly
  • Write proper tests for the double turnover
  • Support multiple turnover notches
  • Revisit the reverse path encoding. Look at using the encodeLeft and encodeRight logic
  • Move everything to a package rather than one big package main. In the current project I just use package main and main.go to do everything. It would probably be nicer to have the Enigma sim as its own package.

I will more than likely revisit this article when I make these changes and hopefully tidy up some of the detail above. For now, this article serves as a reference for later and to help any future Enigma Machine implementors with some detail.

Testing with Go

I really wanted to make sure I wrote proper tests for this project which is something I have been woefully bad at doing in the past. This section discusses what I found out trying to write and do tests in Go

Attempting TDD

I have always been a fan of writing tests and building testable code. I have never been great at the discipline of test driven development (TDD) however I wanted to try to do a flavour of TDD for this project to see if I could get into the habit.

I started out ok, but quickly drifted into implementation before writing the tests. But I thought I did ok and it really helped. I’d like to try to do proper TDD for a full project at some point and I will write more about it for sure.

How I ended up testing

By way of explaining how I approached testing I What follows is the simplest introduction to testing with Go you will find, and its all you need to start writing and executing tests for software written in Go. Trust me, it couldn’t be simpler and it will help you write better software without doubt.

Note: I am going to assume some familiarity with the structure of a Go program here, but not much else. It should be easy to follow along!

There are many fancy ways to automatically generate tests with Go, but for me that requires learning another thing before getting to grips with the basics. So we are going to ignore the fancy ways and the fancy testing methodologies for now and cut straight to building a very simple test suite.

Writing Tests - The Basics

Go has testing baked into the language via the testing package which makes it super easy to get started building tests for everything.

All you need to do is create a source code file that ends in _test.go it can be in its own package or part of the current package. In your _test.go file import the testing package and create a test function to perform a test.

A Test function is simply a function in your test file called (by convention) Test. The descriptive name is usually something like the name of the function in the source code that is being tested. I strayed a little bit from this convention for my Enigma code because I wanted to show I was testing a function of the machine.

The Test function takes as a function parameter a point to testing.T. Go will provide this when the tests are run (see below).

The first test I wrote was to test Encryption using my machine model. Because the details of the real Enigma Machine are well known, I could of course generate a test case based on a known config that would produce a known result.

I wrote a helper function to generate a Military Enigma Machine with the known config. Then called the Encrypt function.

The TestEncryption function is shown below.

package main

import (
	"testing"

	log "github.com/sirupsen/logrus"
)

func TestEncryption(t *testing.T) {
	em := createMilitaryMachine()

	em.SetRotorPosition("left", 'A')
	em.SetRotorPosition("middle", 'A')
	em.SetRotorPosition("right", 'A')

	enc := em.Encrypt("AAAAA") // BDZGO

	if enc != "BDZGO" {
		t.Errorf("Encryption Failed. Expected BDZGO, got %s ", enc)
	}
}

If we don’t see the expected result, we use the testing.T object to indicate a test failure with a reason.

And that is the simplest test you could write! Yes there is a lot unsaid here and a lot of ‘other stuff’ we could do. But for me, this was what go me started and made me realise it doesn’t have to be hard or cumbersome to write tests. In fact I quite enjoyed it.

Running Tests

Running the tests could not be simpler. Simply issue the go test command from within your project and Go will compile a test binary and execute all of the test functions in your _test.go source code.

It will then report any failed tests or report pass:

go-enigma:master> go test
PASS
ok  	github.com/andrewsjg/go-enigma	1.109s

What does it all mean?

Once the tests are validated for a particular function you can make changes to your implementation and be sure that any changes made still produce the expected results when the tests are run. Good tests, run regularly during development liberate you from the ‘Uh Oh! What did I just do to break that?’ problem.

The Enigma Machine was a great way to learn the practical benefit of testing for me because the outputs are so well defined and easy to build tests for. I could mess around with my implementation and quickly see if anything broke because my tests would fail. This literally saved me hours in one case where I made a simple error I wouldn’t have noticed for hours if I didn’t have the tests at hand.

This won’t be a revelation to any professional developer, but to a novice like me, and I suspect others it was. Because practically what this meant was that I could checkout a new branch of my code in Git, make a bunch of changes to an implementation, run the tests and be certain that the new implementation worked or didn’t.

Using Git I could always trace what I had done or revert to a working copy if an experiment went off the rails. In short I could experiment a lot more with the implementation without fear of losing a working model. In the past I would have had several copies of the code with different versions of the model and have to manually backtrack through mistakes.

My tests for the Enigma project aren’t spectacular but they really did save me hours of debugging time.

References