Skip to content

Recreating the enigma in python

by mat on May 19th, 2011

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!

7 Comments
  1. Brendan permalink

    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 permalink

    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. @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 permalink

    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 permalink

    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 permalink

    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 permalink

    Absolutely !! A work of a genius!!

Leave a Reply

Note: I am currently writing my thesis so probably wont have time to reply to your comment
Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS