Blog

Recreating the enigma in python

Whilst on holiday I was challenged by a friend (mikemeat) to create an enigma in python. Here is what I wrote:

# -*- coding: utf-8 -*-  
from random import shuffle,randint,choice
from copy import copy
alphabet=range(0,26)

def shift(l, n): # Method to rotate arrays/cogs
	return l[n:] + l[:n]
	
class cog: # Simple substitution cipher for each cog
	def create(self):
		self.transformation=copy(alphabet)
		shuffle(self.transformation)
		return 
	def passthrough(self,i):
		return self.transformation[i]
	def passthroughrev(self,i):
		return self.transformation.index(i)
	def rotate(self):
		self.transformation=shift(self.transformation, 1)
	def setcog(self,a):
		self.transformation=a

class enigma: # Enigma class	
	def __init__(self, nocogs,printspecialchars):
		self.printspecialchars=printspecialchars
		self.nocogs=nocogs
		self.cogs=[]
		self.oCogs=[] # Create backup of original cog positions for reset
		
		for i in range(0,self.nocogs): # Create cogs
			self.cogs.append(cog())
			self.cogs[i].create()
			self.oCogs.append(self.cogs[i].transformation)
		
		# Create reflector
		refabet=copy(alphabet)
		self.reflector=copy(alphabet)
		while len(refabet)>0:
			a=choice(refabet)
			refabet.remove(a)
			b=choice(refabet)
			refabet.remove(b)
			self.reflector[a]=b
			self.reflector[b]=a

	def print_setup(self): # To print the enigma setup for debugging/replication
		print "Enigma Setup:\nCogs: ",self.nocogs,"\nCog arrangement:"
		for i in range(0,self.nocogs):
			print self.cogs[i].transformation
		print "Reflector arrangement:\n",self.reflector,"\n"
		
	def reset(self):
		for i in range(0,self.nocogs):
			self.cogs[i].setcog(self.oCogs[i])
			
	def encode(self,text):
		ln=0
		ciphertext=""
		for l in text.lower():
			num=ord(l)%97
			if (num>25 or num<0):
				if (self.printspecialchars): # readability
					ciphertext+=l 
				else:
					pass # security
			else:
				ln+=1
				for i in range(0,self.nocogs): # Move thru cogs forward...
					num=self.cogs[i].passthrough(num)
					
				num=self.reflector[num] # Pass thru reflector
				
				for i in range(0,self.nocogs): # Move back thru cogs...
					num=self.cogs[self.nocogs-i-1].passthroughrev(num)
				ciphertext+=""+chr(97+num) # add encrypted letter to ciphertext
				
				for i in range(0,self.nocogs): # Rotate cogs...
					if ( ln % ((i*6)+1) == 0 ): # in a ticker clock style
						self.cogs[i].rotate()
		return ciphertext

plaintext="""The most common arrangement used a ratchet and pawl mechanism. 
Each rotor had a ratchet with 26 teeth and, every time a key was pressed, each 
of the pawls corresponding to a particular rotor would move forward in unison, 
trying to engage with a ratchet, thus stepping the attached rotor once. A thin 
metal ring attached to each rotor upon which the pawl rode normally prevented 
this. As this ring rotated with its rotor, a notch machined into it would 
eventually align itself with the pawl, allowing it to drop into position, engage 
with the ratchet, and advance the rotor. The first rotor, having no previous 
rotor (and therefore no notched ring controlling a pawl), stepped with every 
key press. The five basic rotors (I–V) had one notch each, while the additional 
naval rotors VI, VII and VIII had two notches. The position of the notch on each 
rotor was determined by the letter ring which could be adjusted in relation to 
the core containing the interconnections. The points on the rings at which they 
caused the next wheel to move were as follows"""

x=enigma(4,True)
#x.print_setup()

print "Plaintext:\n"+plaintext+"\n"
ciphertext=x.encode(plaintext)
print "Ciphertext:\n"+ciphertext+"\n"

# To proove that encoding and decoding are symmetrical
# we reset the enigma to starting conditions and enter
# the ciphertext, and get out the plaintext
x.reset()
plaintext=x.encode(ciphertext)
print "Plaintext:\n"+plaintext+"\n"

Feel free to tear it apart and show me how much better/easier it could have been!

13 Comments

  1. Brendan

    Are you kidding me? You’ve implemented an enigma machine in 109 lines of python code?
    Amazing. Certainly beats carrying an enigma typewriter around.

  2. Brian

    Thats cool,
    The line feeds from the multi-line string “plaintext” (what do people have against camel case) are messing up the translations a bit though. They are being printed as letters in the encrypted and decrypted text.

    Doubt they cared too much about that though when the machine was being invented though…. 😉

  3. mat

    @Brendan Thanks, I’m sure it could be coded far better by an expert though.

    @Brian I don’t believe the original enigma supported capitalisation, but you could modifiy the above code to support it.

  4. Paul

    Hmm, interesting. I’m about to embark on a slightly more ambitious version of the same thing. Most of the scripts I’ve found on the internet are not historically accurate simulations, which is what I’m going to try and achieve. Yours has no plugboard, for example.

  5. CubeX

    Hey man, great neat code! But I have a question, maybe you know how to change it so instead of reading plaintext it would read binary file?

  6. rogerh

    Thanks very much. Just been trying this out and adding more comments (I am a bit dim witted). I noticed around line 81 the ‘advance ticker style’ routine seems a cunning wheeze to avoid a pre-settable rotor notch – or have I missed something? Only my second python prog so easily could.

  7. VenomLORD

    Absolutely !! A work of a genius!!

  8. Tony1951

    How can I run this code? Sorry for the ultra newbie question, but I have no idea.

    Regards

    Tony1951

  9. Michael Ragan

    One problem I noticed: The original Enigma would never encode a letter as itself. After I modified the code above so I could enter some text to be encoded, I found that it does encode letters as themselves occasionally.

  10. Christopher

    @Michael Great point, that makes this more like the British version, the Typex machine. Which is actually harder to crack than the enigma.

  11. Jean-Luc

    Do you just start programming ? I see noob errors everywhere, I just can’t correct them all ! There are too much.
    Noob. Bye.
    Fuck your mum. Oops, I already did it

  12. Connor

    Savage

  13. adam fleisher

    how do i use this machine?
    seems amazing i’m just starting to learn python

Leave a Comment

Your email address will not be published. Required fields are marked *