Package: enigma-cipher-machine
EnigmaThe enigma machine in python.The MachineBuilt by the German Scherbius & Ritter company in the early 20th century, the enigma machine is a cipher machine. The machine performs substitution : it encodes a letter from the alphabet to another one.The machine is composed of the following elements:a keyboard for user inputmultiple rotors which are rotating parts that perform the cipher operation using a scrambled electrical wiringa front panel plugboard to allow additionnal substitution making the deciphering operation insanely more difficult (over 150,738,274,937,250 additionnal substitutions using 10 pairs)a light panel with 26 lamps labeled from A to Z.The cipher process can be described as follow :Keyboard -> Plugboard -> Entry Wheel -> Rotors (from 1 to x) -> Reflector -> Rotors (from x to 1) -> Entry Wheel -> Plugboard -> Light PanelThe user press a letter on the keyboard making the first rotor to rotate. The keyboard press close an electrical circuit, allowing the current to enter the plugboard, the entry wheel, each rotor and the reflector (a non rotating wheel). From there the electrical current makes it way back to the light panel, lighting up the alphabet letter corresponding to the cipher letter.RotorsRotors are the rotating part of the enigma machine. Most machines contains three of them but an additionnal one can be found (e.g. M4 variant for the Kriegsmarine with thegreekwheel).Rotors' positions are numbered from A to Z and possess turnovers positions. When a rotor reach a turnover position, it rotates its closest neighbour.Rotors' internal wiring can be shifted prior any operation by rotating a ring to a certain position. This setting is often called theRingstellung.The wiring pattern is usely represented with an alphabeter string. Each alphabet chacracter is replaced by the corresponding letter in the string. For instance the string:EKMFLGDQVZNTOWYHXUSPAIBRCJmeans that aAsubstitute to anE, aBto anKand so on. The same logic applies for the remaining character as shown below.ABCDEFGHIJKLMNOPQRSTUVWXYZ      keyboard    reflector  
||||||||||||||||||||||||||         ↓            ↑
EKMFLGDQVZNTOWYHXUSPAIBRCJ      reflector   keyboardOn each rotor increment, we rotate the alphabet by one (please pay attention to the position of the letter A):BCDEFGHIJKLMNOPQRSTUVWXYZA
||||||||||||||||||||||||||
EKMFLGDQVZNTOWYHXUSPAIBRCJMoving the ring to a B position is equivalent to the inverse of a rotation.ZABCDEFGHIJKLMNOPQRSTUVWXY
||||||||||||||||||||||||||
EKMFLGDQVZNTOWYHXUSPAIBRCJWe can represent the shift with the following equations:From the keyboard to the reflector (alphabet to rotor's wiring)wiring_letter = letter + position - ringstellung
cipher_letter = wire.at(wiring_letter) - position + ringstellungFrom the reflector to the light panel (rotor's wiring to alphabet)wiring_letter = letter + position - rinsgtellung
cipher_letter = wire.index(wiring_letter) - position + ringstellungThe reflector is considered as a one-way (keyboard to reflector), non rotating "rotor".Enigma machines that use a lever suffers an abnormal rotation when the center rotor reaches its turnover position, all the rotors are increment one step further.
Let's illustrate this anomaly.Rotor I turnover position is Q : if reached Rotor II increment,Rotor II turnover position  is E : if reached Rotor III increment.We setup the machine to the A-D-Q position and we increment the Rotor I.Expected rotation:IIIIIIADQAERAESDouble stepping rotation:IIIIIIADQAERBFSRotor III, II and I increment. This software enables double stepping rotation by default as it was present on the M3 and M4 machine. You can desactivate this behaviour by settingdouble_stepping=Falsein theMachine constructor.PlugboardThe plugboard is a front panel with 26 electrical sockets. A male-male cable is used to connect letters between them. 10 connections (20 pairs) is a common setup.An AB pair will substitute every A to B and every B to an A.We use this notation :AE BF CM DQ HU JN LX PR SZ VW. Every pairs are space separated.ExampleWe use the M3 machine and the following setup:ObjectValueReflectorCRotorsIII-II-IPositionsA-B-CRingstellungD-E-FPlugboardqd fe rw jn il ps cm ax kg yuTextHello worldThe enigma produces:fsqsj fustaGetting startedGet the library:pipinstallenigma-machineWe use pure python, no dependency required and tested on python 3.10.6.To run tests please installpytest.UsageWe want to decode the following message:xgytk npnkq ssnxw kyfwith the settings extracted from the above example.Start by initialise a plugboard, A plugboard object contains from 0 to 10 pairs of letters. A letter can only appears one time in the plugboard.fromenigma.plugboardimportPlugboardplugboard=Plugboard("qd fe rw jn il ps cm ax kg yu")Then make the machine using the predefined rotors (see below if you want to create yours).fromenigma.rotorimportRotorsfromenigma.machineimportMachineM3=Machine("M3",[Rotors.M3.ETW,Rotors.M3.I,Rotors.M3.II,Rotors.M3.III,Rotors.M3.UKWC],plugboard)Now set the positions and the ringstellung:M3.set_rotor_position("I","A")M3.set_rotor_position("II","B")M3.set_rotor_position("III","C")M3.set_rotor_ringstellung("I","D")M3.set_rotor_ringstellung("II","E")M3.set_rotor_ringstellung("III","F")And then provide the input text (split is optionnal it adds an additionnal space every n character):plaintext=M3.encode("xgytk npnkq ssnxw kyf",split=0)print(plaintext)>>alanmathisonturingHere is a full example using a M4 enigma machine with double stepping. We are decoding a real world message sent by GrossAdmiral Donitz on the 1st May 1945.fromenigma.machineimportMachinefromenigma.plugboardimportPlugboardfromenigma.rotorimportRotorsplugboard=Plugboard("AE BF CM DQ HU JN LX PR SZ VW")M4=Machine("M4",[Rotors.M4.ETW,Rotors.M4.VIII,Rotors.M4.VI,Rotors.M4.V,Rotors.M4.Beta,Rotors.M4.UKWC],plugboard)M4.set_rotor_position("Beta","C")M4.set_rotor_position("V","D")M4.set_rotor_position("VI","S")M4.set_rotor_position("VIII","Z")M4.set_rotor_ringstellung("Beta","E")M4.set_rotor_ringstellung("V","P")M4.set_rotor_ringstellung("VI","E")M4.set_rotor_ringstellung("VIII","L")print(M4)ciphertext="""LANO TCTO UARB BFPM HPHG CZXT DYGA HGUF XGEW KBLK GJWL QXXTGPJJ AVTO CKZF SLPP QIHZ FXOE BWII EKFZ LCLO AQJU LJOY HSSM BBGW HZANVOII PYRB RTDJ QDJJ OQKC XWDN BBTY VXLY TAPG VEAT XSON PNYN QFUD BBHHVWEP YEYD OHNL XKZD NWRH DUWU JUMW WVII WZXI VIUQ DRHY MNCY EFUA PNHOTKHK GDNP SAKN UAGH JZSM JBMH VTRE QEDG XHLZ WIFU SKDQ VELN MIMI THBHDBWV HDFY HJOQ IHOR TDJD BWXE MEAY XGYQ XOHF DMYU XXNO JAZR SGHP LWMLRECW WUTL RTTV LBHY OORG LGOW UXNX HMHY FAAC QEKT HSJW"""plaintext=M4.encode(ciphertext,split=0,debug=False)print(plaintext)The machine returns :krkrallexxfolgendesistsofortbekanntzugebenxxichhabefolgelnbebefehlerhaltenxxjansterledesbisherigxnreichsmarschallsjgoeringjsetztderfuehrersieyhvrrgrzssadmiralyalsseinennachfolgereinxschriftlschevollmachtunterwegsxabsofortsollensiesaemtlichemassnahmenverfuegenydiesichausdergegenwaertigenlageergebenxgezxreichsleiteikktulpekkjbormannjxxobxdxmmmdurnhfkstxkomxadmxuuubooiexkpAfter formatingkrkr alle xx 
folgendes ist sofort bekanntzugeben xx 
ich habe folgelnbe befehl erhalten xx 
j ansterle des bisherigxn reichsmarschalls j goering j setzt der fuehrer sie y hvrr grzssadmiral y als seinen nachfolger ein x schriftlsche vollmacht unterwegs x absofort sollen sie saemtliche massnahmen verfuegen y die sich aus der gegenwaertigen lage ergeben x gez x reichsleitei kk tulpe kk j bormann j xx
ob x d x mmm durnh fkst x kom x adm x uuu booie x kpPlease seecryptomuseum.comfor more.Going furtherYou can create additionnal rotors by using theRotorclass from theenigma.rotorpackage.A plugboard object contains from 0 to 10 pairs of letters. A letter can only appears one time in the plugboard.fromenigma.plugboardimportPlugboardplugboard=Plugboard("AE BF CM DQ HU JN LX PR SZ VW")You can also randomise the plugboard:plugboard.randomise()A Rotor object requires an unique name, the wiring sequence containing every letter of the alphabet (len(wiring) == 26) and the turnover positions. Seerotor.pyfor the full api description.fromenigma.rotorimportRotor# Commercial Enigma A27 wiringKI=Rotor("KI",#name"LPGSZMHAEOQKVXRFYBUTNICJDW",# wiring"Y"# turnover)KII=Rotor("KII","SLVGBTFXJQOHEWIRZYAMKPCNDU","E")KIII=Rotor("KII","CJGDPSHKTURAWZXFMYNQOBVLIE","N")ETWK=Rotor("ETWK","QWERTZUIOASDFGHJKPYXCVBNML","",rotate=False# no rotation for an entry wheel)UKWK=Rotor("UKWK","IMETCGFRAYSQBZXWLHKDVUPOJN","",reflector=True# this rotor is a reflector)A machine object requires a name, a list of rotors (starting from the entry wheel to the reflector) and a plugboard.fromenigma.machineimportMachineEK=Machine("K-A27",[ETWK,KI,KII,KIII,UKWK],plugboard)TODOset_rotor_position and set_rotor_ringstellung even if the rotor shares the same nameLicenceMIT of course.