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!
Are you kidding me? You’ve implemented an enigma machine in 109 lines of python code?
Amazing. Certainly beats carrying an enigma typewriter around.
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…. 😉
@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.
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.
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?
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.
Absolutely !! A work of a genius!!
How can I run this code? Sorry for the ultra newbie question, but I have no idea.
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.
@Michael Great point, that makes this more like the British version, the Typex machine. Which is actually harder to crack than the enigma.
Do you just start programming ? I see noob errors everywhere, I just can’t correct them all ! There are too much.
Fuck your mum. Oops, I already did it
how do i use this machine?
seems amazing i’m just starting to learn python