Package: yb-enigma
EnigmaAuthor:Yurii BliusiukEmail:ura.blusuk@gmail.comLicense:MITDescriptionEmulate (replicate) functionality of the physical Enigma machine.Installationpip install yb-enigmaUsage (CLI)enigma-cli [ARGS]...Input-s, --stringString to encode-i, --input-filePath to source file-cnfg, --configurationConfiguration string (seeConfiguration string format)-k, --key-filePath to file with key (Configuration string)Output-Default: print key and encoded string to console-o, --output-filePath to destination file-sk, --save-keySave key to file. Key will be saved to /path/to/destination_file_name.keyCan only be used in FILE modeArgs-d, --debugEnable debug output-rcnfg --random-configurationSet random rotors, reflector and plugboard configuration-ks, --keep-spacesKeep spaces in input string-kx, --keep-specialKeep all special characters in input string.-kn, --keep-new-lineKeep new line charecters in input string.-g, --groupsDivide output string to groupsExample: "ENIGMA IS COOL" => "ENIGM AISCO OL"Can`t be used with '--keep-spaces', '--keep-special' or '--keep-new-line'Configuration stringExample:A II:10-I:3-III:20 AB:CDA - reflectorII:10-I:3-III:20 - rotorsII:10 - set first rotor to II at position 10I:3 - set second rotor to I at position 3III:20 - set third rotor to III at position 20AB:CD - plugboardAB - Plug pair A to BCD - Plpug pair C to DExamplesEncode string "Hello, World!" using random configuration:NOTE: special characters must be escaped by "\"$ enigma-cli -s "Hello, World\!" -rcnfgEncode string "Hello, World!" using random configuration, keeping spaces and special characters:$ enigma-cli -s "Hello, World\!" -rcnfg -ks -kxEncode string "Hello, World!" using random configuration, keeping spaces and special characters, output to file ./encoded.txt, key will be saved to ./encoded.key:$ enigma-cli -s "Hello, World\!" -rcnfg -o ./encoded.txt -sk -ks -kxEncode text from ./text.txt using "A II:10-I:3-III:20 AB:CD" configuration:$ enigma-cli -f ./text.txt -cnfg "A II:10-I:3-III:20 AB:CD"Encode text from ./text.txt using random configuration, output to file ./encoded.txt:$ enigma-cli -f ./text.txt -rcnfg -o ./encoded.txtEncode text from ./text.txt using random configuration, keeping spaces, special characters and new-line charecters, output to file ./encoded.txt, key will be saved to ./encoded.key:$ enigma-cli -f ./text.txt -rcnfg -o ./encoded.txt -sk -ks -kx -knUsage (as module)Import:fromyb_enigmaimportEnigma,Rotor,Reflector,Plugboard,parse_configurationEncode string:enigma=Enigma(random_cnfg=True)# Create enigma instanceencoded_string=enigma.encode('Hello, World!')# Encode "Hello, World!"Encode text from file:enigma=Enigma(random_cnfg=True)# Create enigma instanceencoded_string=''withopen('./text.txt','r')asfile:# Open file to readchar=file.read(1)# Read first bytewhilechar:ifchar.isalpha():# If char is [a-zA-Z]encoded_string+=enigma.encode(char)# Encode char and add to encoded_stringchar=file.read(1)# Read next byteCustom configuration (manually):reflector=Reflector.A()# Reflectorrotors_list=[# List of rotorsRotor.I(pos=2),Rotor.II(pos=10),Rotor.III(pos=0),]plugboard=Plugboard(pairs=[# Plugboard{'a','b'},{'c','d'},])enigma=Enigma(# Create enigma instance withreflector=reflector,# predefined configurationrotors_list=rotors_list,plugboard=plugboard,)Custom configuration (from Configuration string):reflector,rotors_list,plugboard=parse_configuration("A I:2-II:10-III:0 AB:CD")enigma=Enigma(# Create enigma instance withreflector=reflector,# predefined configurationrotors_list=rotors_list,plugboard=plugboard,)Get/set configuration using Configuration string:enigma=Enigma(random_cnfg=True)# Create enigma instanceconfiguration_string=enigma.get_configuration()# Get configuration stringenigma.set_configuration(configuration_string)# Set configurationHow it worksMain parts are:RotorIn real Enigma machine, rotors are discs with 26 brass, spring-loaded, electrical contact pins arranged in a circle on one face, with the other face housing 26 corresponding electrical contacts in the form of circular plates.Inside the body of the rotor, 26 wires connect each pin on one side to a contact on the other in a complex pattern.This way, we have very simple encryption algorithm (basically substitution cipher). For example, the pin corresponding to the letter E might be wired to the contact for letter T on the opposite face, and so on.Enigma's security comes from using several rotors in series (usually three or four) and the regular stepping movement of the rotors, thus implementing a polyalphabetic substitution cipher.In program this is made by each Rotor knowing it's "key": string, containing all english letter (basically it's permutation of alphabet).To find corresponding letter, you get letter's position in alphabet and then get letter in "key" at this position. For example, we need to find corresponding letter for "c" having key "ekmflgdqvzntowyhxuspaibrcj": position in alphabet is 3, third letter in "key" is "m". So, we have C => M.However, rotors are shifting, so N'th letter in alphabet corresponds to (N+Rotor.position)'th letter in "key"Also, important part of Enigma's encoding algorithm is the fact, that for each pressed letter (in our case encoded letter), first rotor shifts (rotates) by 1. Moreover, rotors are connected such a way, that when first rotor makes full rotation, it shifts second rotor by 1. The same thing second rotor does with third and so on.Example:For simplicity, lets consider we have only 5 letters in alphabet. Our Enigma has 3 rotors with following keys:"cbdae""decba""acdeb"All rotors are at starting position (let's say posiiton 0). We want to encode string "cad". The steps are going to be:press (encode) "c":first rotor shifts, so it's position is 1 nowposition of "c" in alphabet is 3(3+1)'th letter in first rotor "key" is "a"position of "a" in alphabet is 1, second rotor's position is 0(1+0)'th letter in second rotor "key" is "d"position of "d" in alphabet is 4, third rotor's position is 0(4+0)'th letter in third rotor "key" is "e""e" is the resultpress (encode) "a":first rotor shifts, so it's position is 2 nowposition of "a" in alphabet is 1(1+2)'th letter in first rotor "key" is "d"position of "d" in alphabet is 4, second rotor's position is 0(4+0)'th letter in second rotor "key" is "b"position of "b" in alphabet is 2, third rotor's position is 0(2+0)'th letter in third rotor "key" is "c""c" is the resultpress (encode) "d":first rotor shifts, so it's position is 3 nowposition of "d" in alphabet is 4(4+3)'th letter in first rotor "key" is "b"NOTE: actually, there is no 7'th letter, so it's (7 % 5)'th letterposition of "b" in alphabet is 2, second rotor's position is 0(2+0)'th letter in second rotor "key" is "e"position of "e" in alphabet is 5, third rotor's position is 0(5+0)'th letter in third rotor "key" is "b""b" is the resultResult:"cad" => "ecb"ReflectorPreviously, we have encoded string "cad" such a way, that each letter "went" from the first rotor to the second one, from the second to third and so on. However, what should actually happen, is that each letter is "going" through all rotors from the first to the last, then through thereflector, and back through all rotors, but now from the last to the first.So,reflectoris basically doing the same job as the rotor does, but it "encodes" and then redirects letter (electrical signal in real Enigma) back to the last rotor andit's not movingduring work.IMPORTANT:in reflector's "key", letters are pointing on each other in pairs, e.g. if "a" is pointing to "c", "c" must be pointing to "a"PlugboardThe plugboard permitted variable wiring that could be reconfigured by the operator.A cable placed onto the plugboard connected letters in pairs; for example, E and Q might be the pair. The effect was to swap those letters before and after the main rotor unit. For example, when an operator pressed E, the signal was diverted to Q before entering the rotors.ImplementationAll the steps are well described in the comments. Small summary:RotorTo implement rotors rotation system (each rotor rotates the next one after it's full rotation, more in "How it works -> Rotor"), rotors are organised using Doubly linked list.Also, to optimise encoding, I am creatingcoding_list- list of each key letter's num in alphabet (e.g. "ecabd" => [4, 2, 0, 1, 3]).Each rotor has it's number: I, II, ..., VIII and position (rotation step).ReflectorAs previously said in "How it works -> Reflector", reflector is doing pretty much the same work as Rotor do, butit is not moving during encoding, so it doesn't have position.it doesn't have reverse mode while getting corresponding letter.PlugboardPlugpairs are stored usingsetdata type (it just feels more ideologically correct for me, however in terms of speed, it is just the same aslistortuple, because of small amount of data stored (it's always only two strings of length 1)).Pairs are stored inlist.During encoding, it returns corresponding char or char itself, if it's not in plugged pairsEnigmaFinally an Enigma class.Can be initialised using random or default configuration.Takes care of creating doubly linked list of rotors.While encoding, shift the first rotor and passes letter through the whole chain (plugboard -> rotors -> reflector -> rotors (desc) -> plugboard).CLIThe CLI script is pretty simple script, which parses passed arguments, and works based on those argument.There are basically 2 modes: string and file. In the string mode, input is passed as string using (-s <STRING>, --string <STRING>) argument. In the file mode, input is passed using file, path is set by (-i <PATH>, --input-file <PATH>).