From 62997caf0b80bb2e8bd7e6bdbda258955eaea0f9 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 19:07:38 +0200 Subject: [PATCH 01/21] Added Enigma machine file Added Enigma machine file to 'ciphers' section --- ciphers/enigma_machine.py | 231 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 ciphers/enigma_machine.py diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py new file mode 100644 index 000000000000..aff576bc7f2b --- /dev/null +++ b/ciphers/enigma_machine.py @@ -0,0 +1,231 @@ +""" +Wikipedia: https://en.wikipedia.org/wiki/Enigma_machine +Video explanation: https://youtu.be/QwQVMqfoB2E +Also check out Numberphile's and Computerphile's videos on this topic + +This module contains function enigma(text, rotor_position, rotor_selection, plugb) which emulates +the famous Enigma machine from WWII. +Module includes: +- enigma function +- showcase of function usage +- 9 randnomly generated rotors +- reflector (aka static rotor) +- original alphabet +""" + +# used alphabet -------------------------- +# from string.ascii_uppercase +abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + +# -------------------------- default selection -------------------------- +# rotors -------------------------- +rotor1 = 'EGZWVONAHDCLFQMSIPJBYUKXTR' +rotor2 = 'FOBHMDKEXQNRAULPGSJVTYICZW' +rotor3 = 'ZJXESIUQLHAVRMDOYGTNFWPBKC' +# reflector -------------------------- +reflector = {'A': 'N', 'N': 'A', 'B': 'O', 'O': 'B', 'C': 'P', 'P': 'C', 'D': 'Q', 'Q': 'D', + 'E': 'R', 'R': 'E', 'F': 'S', 'S': 'F', 'G': 'T', 'T': 'G', 'H': 'U', 'U': 'H', + 'I': 'V', 'V': 'I', 'J': 'W', 'W': 'J', 'K': 'X', 'X': 'K', 'L': 'Y', 'Y': 'L', + 'M': 'Z', 'Z': 'M'} + +# -------------------------- extra rotors -------------------------- +rotor4 = 'RMDJXFUWGISLHVTCQNKYPBEZOA' +rotor5 = 'SGLCPQWZHKXAREONTFBVIYJUDM' +rotor6 = 'HVSICLTYKQUBXDWAJZOMFGPREN' +rotor7 = 'RZWQHFMVDBKICJLNTUXAGYPSOE' +rotor8 = 'LFKIJODBEGAMQPXVUHYSTCZRWN' +rotor9 = 'KOAEGVDHXPQZMLFTYWJNBRCIUS' + + +def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: + """ + Checks if the values can be used for the 'enigma' function + + :param rotpos: rotor_positon + :param rotsel: rotor_selection + :param pb: plugb -> validated and transformed + :return: (rotpos, rotsel, pb) + """ + # Checks if there are 3 unique rotors + if len(set(rotsel)) < 3: + raise Exception('Please use 3 unique rotors (not ' + str(len(set(rotsel))) + ')') + + # Checks if rotor positions are valid + rotorpos1, rotorpos2, rotorpos3 = rotpos + if rotorpos1 < 1 or rotorpos1 > len(abc): + raise ValueError('First rotor position is not withing range of 1..74 (' + str(rotorpos1) + ')') + if rotorpos2 < 1 or rotorpos2 > len(abc): + raise ValueError('Second rotor position is not withing range of 1..74 (' + str(rotorpos2) + ')') + if rotorpos3 < 1 or rotorpos3 > len(abc): + raise ValueError('Third rotor position is not withing range of 1..74 (' + str(rotorpos3) + ')') + + # Validates string and returns dict + pb = _plugboard(pb) + + return rotpos, rotsel, pb + + +def _plugboard(pbl: str) -> dict: + """ + https://en.wikipedia.org/wiki/Enigma_machine#Plugboard + + >>> _plugboard('PICTURES') + {'P': 'I', 'I': 'P', 'C': 'T', 'T': 'C', 'U': 'R', 'R': 'U', 'E': 'S', 'S': 'E'} + >>> _plugboard('POLAND') + {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} + + :param pbl: string containing plugboard setting for the Enigma machine + :return: dictionary of + """ + + # tests the input string if it + # a) is type string + # b) has even length (so pairs can be made) + if type(pbl) is not str: + raise TypeError('PLugboard setting isn\'t type string ('+str(type(pbl))+')') + elif len(pbl) % 2 != 0: + raise Exception('Odd number of symbols (' + str(len(pbl)) + ')') + elif pbl == '': + return {} + + # Checks if all characters are unique + tmppbl = set() + for i in pbl: + if i not in abc: + raise Exception('Not in list of symbols') + elif i in tmppbl: + raise Exception('Duplicate symbol (' + i + ')') + else: + tmppbl.add(i) + del tmppbl + + # Created the dictionary + pb = {} + for i in range(0, len(pbl) - 1, 2): + pb[pbl[i]] = pbl[i + 1] + pb[pbl[i + 1]] = pbl[i] + + return pb + + +def enigma(text: str, rotor_position: tuple, rotor_selection: tuple = (rotor1, rotor2, rotor3), plugb: str = '') -> str: + """ + The only difference with real-world enigma is that I allowed string input. + All characters are converted to uppercase. (non-letter symbol are ignored) + How it works: + (for every letter in the message) + + - Input letter goes into the plugboard. If it is connected to another one, change it. + + - Letter goes through 3 rotors. Each rotor can be represented as 2 sets of symbol, where one is shuffled. + Each symbol from the first set has corresponding symbol in the second set and vice versa. (example below) + | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F + | VKLEPDBGRNWTFCJOHQAMUZYIXS | + + - Symbol then goes through reflector (static rotor). + There it is switched with paired symbol + The reflector can be represented as2 sets, each with half of the alphanet. (example below) + There are usually 10 pairs of letters. + | ABCDEFGHIJKLM | e.g. E is paired to X + | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa + + - Letter then goes through the rotors again + + - If the letter is connected to plugboard, it is switched. + + - Return the letter + + >>> enigma('Hello World!', (1, 2, 1), plugb='pictures') + 'KORYH JUHHI!' + >>> enigma('KORYH, juhhi!', (1, 2, 1), plugb='pictures') + 'HELLO, WORLD!' + >>> enigma('hello world!', (1, 1, 1), plugb='pictures') + 'FPNCZ QWOBU!' + >>> enigma('FPNCZ QWOBU', (1, 1, 1), plugb='pictures') + 'HELLO WORLD' + + + + + :param text: input message + :param rotor_position: tuple with 3 values in range 1..74 + :param rotor_selection: tuple with 3 rotors () + :param plugb: string containing plugboard configuration (default '') + :return: en/decrypted string + """ + + text = text.upper() + rotor_position, rotor_selection, pb = _validator(rotor_position, rotor_selection, plugb.upper()) + + rotorpos1, rotorpos2, rotorpos3 = rotor_position + rotor1, rotor2, rotor3 = rotor_selection + rotorpos1 -= 1 + rotorpos2 -= 1 + rotorpos3 -= 1 + pb = pb + + result = [] + + # encryption/decryption process -------------------------- + for symbol in text: + if symbol in abc: + + # 1st plugboard -------------------------- + if symbol in pb: + symbol = pb[symbol] + + # rotor ra -------------------------- + index = abc.index(symbol) + rotorpos1 + symbol = rotor1[index % len(abc)] + + # rotor rb -------------------------- + index = abc.index(symbol) + rotorpos2 + symbol = rotor2[index % len(abc)] + + # rotor rc -------------------------- + index = abc.index(symbol) + rotorpos3 + symbol = rotor3[index % len(abc)] + + # reflector -------------------------- + # this is the reason you don't need another machine to decipher + + symbol = reflector[symbol] + + # 2nd rotors + symbol = abc[rotor3.index(symbol) - rotorpos3] + symbol = abc[rotor2.index(symbol) - rotorpos2] + symbol = abc[rotor1.index(symbol) - rotorpos1] + + # 2nd plugboard + if symbol in pb: + symbol = pb[symbol] + + # moves/resets rotor positions + rotorpos1 += 1 + if rotorpos1 >= len(abc): + rotorpos1 = 0 + rotorpos2 += 1 + if rotorpos2 >= len(abc): + rotorpos2 = 0 + rotorpos3 += 1 + if rotorpos3 >= len(abc): + rotorpos3 = 0 + + #else: + # pass + # Error could be also raised + # raise ValueError('Invalid symbol('+repr(symbol)+')') + result.append(symbol) + + return "".join(result) + + +if __name__ == '__main__': + message = 'Hello, this is my Python script that emulates the famous Enigma machine from WWII.' + rotor_pos = (1, 1, 1) + pb = 'pictures' + rotor_sel = (rotor2, rotor4, rotor8) + en = enigma(message, rotor_pos, rotor_sel, pb) + + print('Encrypted message:', en) + print('Decrypted message:', enigma(en, rotor_pos, rotor_sel, pb)) From f72ab6347672ee342d516d57dd5c5e1c720cc74e Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 19:16:08 +0200 Subject: [PATCH 02/21] Added doctest to validator --- ciphers/enigma_machine.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index aff576bc7f2b..97debf364545 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -41,6 +41,9 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: """ Checks if the values can be used for the 'enigma' function + >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') +((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) + :param rotpos: rotor_positon :param rotsel: rotor_selection :param pb: plugb -> validated and transformed From 3ef11fce9b6773a11512b3183f0d519dc6c270ac Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 19:34:04 +0200 Subject: [PATCH 03/21] Fixed typo --- ciphers/enigma_machine.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index 97debf364545..c84d6c931a09 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -56,11 +56,11 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos if rotorpos1 < 1 or rotorpos1 > len(abc): - raise ValueError('First rotor position is not withing range of 1..74 (' + str(rotorpos1) + ')') + raise ValueError('First rotor position is not within range of 1..74 (' + str(rotorpos1) + ')') if rotorpos2 < 1 or rotorpos2 > len(abc): - raise ValueError('Second rotor position is not withing range of 1..74 (' + str(rotorpos2) + ')') + raise ValueError('Second rotor position is not within range of 1..74 (' + str(rotorpos2) + ')') if rotorpos3 < 1 or rotorpos3 > len(abc): - raise ValueError('Third rotor position is not withing range of 1..74 (' + str(rotorpos3) + ')') + raise ValueError('Third rotor position is not within range of 1..74 (' + str(rotorpos3) + ')') # Validates string and returns dict pb = _plugboard(pb) From de957d78246a284dffb0412b748c12e0051d4011 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 19:38:00 +0200 Subject: [PATCH 04/21] Shortened some lines --- ciphers/enigma_machine.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index c84d6c931a09..d48cccd999ee 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -42,7 +42,8 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: Checks if the values can be used for the 'enigma' function >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') -((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) + ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), +{'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) :param rotpos: rotor_positon :param rotsel: rotor_selection @@ -111,7 +112,8 @@ def _plugboard(pbl: str) -> dict: return pb -def enigma(text: str, rotor_position: tuple, rotor_selection: tuple = (rotor1, rotor2, rotor3), plugb: str = '') -> str: +def enigma(text: str, rotor_position: tuple, + rotor_selection: tuple = (rotor1, rotor2, rotor3), plugb: str = '') -> str: """ The only difference with real-world enigma is that I allowed string input. All characters are converted to uppercase. (non-letter symbol are ignored) From 2bbdbaaabe8f575ca574ccc11821964898153bdf Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 19:54:01 +0200 Subject: [PATCH 05/21] Shortened some lines --- ciphers/enigma_machine.py | 41 +++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index d48cccd999ee..d2f51f7b6538 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -23,10 +23,10 @@ rotor2 = 'FOBHMDKEXQNRAULPGSJVTYICZW' rotor3 = 'ZJXESIUQLHAVRMDOYGTNFWPBKC' # reflector -------------------------- -reflector = {'A': 'N', 'N': 'A', 'B': 'O', 'O': 'B', 'C': 'P', 'P': 'C', 'D': 'Q', 'Q': 'D', - 'E': 'R', 'R': 'E', 'F': 'S', 'S': 'F', 'G': 'T', 'T': 'G', 'H': 'U', 'U': 'H', - 'I': 'V', 'V': 'I', 'J': 'W', 'W': 'J', 'K': 'X', 'X': 'K', 'L': 'Y', 'Y': 'L', - 'M': 'Z', 'Z': 'M'} +reflector = {'A': 'N', 'N': 'A', 'B': 'O', 'O': 'B', 'C': 'P', 'P': 'C', 'D': 'Q', + 'Q': 'D', 'E': 'R', 'R': 'E', 'F': 'S', 'S': 'F', 'G': 'T', 'T': 'G', + 'H': 'U', 'U': 'H', 'I': 'V', 'V': 'I', 'J': 'W', 'W': 'J', 'K': 'X', + 'X': 'K', 'L': 'Y', 'Y': 'L', 'M': 'Z', 'Z': 'M'} # -------------------------- extra rotors -------------------------- rotor4 = 'RMDJXFUWGISLHVTCQNKYPBEZOA' @@ -42,7 +42,8 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: Checks if the values can be used for the 'enigma' function >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') - ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), + ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', + 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) :param rotpos: rotor_positon @@ -52,16 +53,20 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: """ # Checks if there are 3 unique rotors if len(set(rotsel)) < 3: - raise Exception('Please use 3 unique rotors (not ' + str(len(set(rotsel))) + ')') + raise Exception('Please use 3 unique rotors (not ' + + str(len(set(rotsel))) + ')') # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos if rotorpos1 < 1 or rotorpos1 > len(abc): - raise ValueError('First rotor position is not within range of 1..74 (' + str(rotorpos1) + ')') + raise ValueError('First rotor position is not within range of 1..74 (' + + str(rotorpos1) + ')') if rotorpos2 < 1 or rotorpos2 > len(abc): - raise ValueError('Second rotor position is not within range of 1..74 (' + str(rotorpos2) + ')') + raise ValueError('Second rotor position is not within range of 1..74 (' + + str(rotorpos2) + ')') if rotorpos3 < 1 or rotorpos3 > len(abc): - raise ValueError('Third rotor position is not within range of 1..74 (' + str(rotorpos3) + ')') + raise ValueError('Third rotor position is not within range of 1..74 (' + + str(rotorpos3) + ')') # Validates string and returns dict pb = _plugboard(pb) @@ -86,9 +91,11 @@ def _plugboard(pbl: str) -> dict: # a) is type string # b) has even length (so pairs can be made) if type(pbl) is not str: - raise TypeError('PLugboard setting isn\'t type string ('+str(type(pbl))+')') + raise TypeError('PLugboard setting isn\'t type string (' + + str(type(pbl)) + ')') elif len(pbl) % 2 != 0: - raise Exception('Odd number of symbols (' + str(len(pbl)) + ')') + raise Exception('Odd number of symbols (' + + str(len(pbl)) + ')') elif pbl == '': return {} @@ -122,8 +129,10 @@ def enigma(text: str, rotor_position: tuple, - Input letter goes into the plugboard. If it is connected to another one, change it. - - Letter goes through 3 rotors. Each rotor can be represented as 2 sets of symbol, where one is shuffled. - Each symbol from the first set has corresponding symbol in the second set and vice versa. (example below) + - Letter goes through 3 rotors. + Each rotor can be represented as 2 sets of symbol, where one is shuffled. + Each symbol from the first set has corresponding symbol in the second set and vice versa. + example: | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F | VKLEPDBGRNWTFCJOHQAMUZYIXS | @@ -160,7 +169,8 @@ def enigma(text: str, rotor_position: tuple, """ text = text.upper() - rotor_position, rotor_selection, pb = _validator(rotor_position, rotor_selection, plugb.upper()) + rotor_position, rotor_selection, pb = _validator( + rotor_position, rotor_selection, plugb.upper()) rotorpos1, rotorpos2, rotorpos3 = rotor_position rotor1, rotor2, rotor3 = rotor_selection @@ -219,7 +229,8 @@ def enigma(text: str, rotor_position: tuple, #else: # pass # Error could be also raised - # raise ValueError('Invalid symbol('+repr(symbol)+')') + # raise ValueError( + # 'Invalid symbol('+repr(symbol)+')') result.append(symbol) return "".join(result) From 045e4777734516e1ed32dca479786a0ddf35dc1a Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 20:00:05 +0200 Subject: [PATCH 06/21] Update enigma_machine.py --- ciphers/enigma_machine.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index d2f51f7b6538..972e8fcc4778 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -3,7 +3,7 @@ Video explanation: https://youtu.be/QwQVMqfoB2E Also check out Numberphile's and Computerphile's videos on this topic -This module contains function enigma(text, rotor_position, rotor_selection, plugb) which emulates +This module contains function 'enigma' which emulates the famous Enigma machine from WWII. Module includes: - enigma function @@ -79,7 +79,7 @@ def _plugboard(pbl: str) -> dict: https://en.wikipedia.org/wiki/Enigma_machine#Plugboard >>> _plugboard('PICTURES') - {'P': 'I', 'I': 'P', 'C': 'T', 'T': 'C', 'U': 'R', 'R': 'U', 'E': 'S', 'S': 'E'} +{'P': 'I', 'I': 'P', 'C': 'T', 'T': 'C', 'U': 'R', 'R': 'U', 'E': 'S', 'S': 'E'} >>> _plugboard('POLAND') {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} @@ -226,7 +226,7 @@ def enigma(text: str, rotor_position: tuple, if rotorpos3 >= len(abc): rotorpos3 = 0 - #else: + # else: # pass # Error could be also raised # raise ValueError( @@ -237,7 +237,7 @@ def enigma(text: str, rotor_position: tuple, if __name__ == '__main__': - message = 'Hello, this is my Python script that emulates the famous Enigma machine from WWII.' + message = 'This is my Python script that emulates the famous Enigma machine from WWII.' rotor_pos = (1, 1, 1) pb = 'pictures' rotor_sel = (rotor2, rotor4, rotor8) From ee75354259c30f26a3a2a7942e5428c773861771 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 20:08:54 +0200 Subject: [PATCH 07/21] Shortened some lines --- ciphers/enigma_machine.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index 972e8fcc4778..a8a85e0db3a2 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -127,19 +127,24 @@ def enigma(text: str, rotor_position: tuple, How it works: (for every letter in the message) - - Input letter goes into the plugboard. If it is connected to another one, change it. + - Input letter goes into the plugboard. + If it is connected to another one, switch it. - Letter goes through 3 rotors. Each rotor can be represented as 2 sets of symbol, where one is shuffled. - Each symbol from the first set has corresponding symbol in the second set and vice versa. + Each symbol from the first set has corresponding symbol in + the second set and vice versa. + example: | ABCDEFGHIJKLMNOPQRSTUVWXYZ | e.g. F=D and D=F | VKLEPDBGRNWTFCJOHQAMUZYIXS | - Symbol then goes through reflector (static rotor). There it is switched with paired symbol - The reflector can be represented as2 sets, each with half of the alphanet. (example below) + The reflector can be represented as2 sets, each with half of the alphanet. There are usually 10 pairs of letters. + + Example: | ABCDEFGHIJKLM | e.g. E is paired to X | ZYXWVUTSRQPON | so when E goes in X goes out and vice versa @@ -237,7 +242,7 @@ def enigma(text: str, rotor_position: tuple, if __name__ == '__main__': - message = 'This is my Python script that emulates the famous Enigma machine from WWII.' + message = 'This is my Python script that emulates the Enigma machine from WWII.' rotor_pos = (1, 1, 1) pb = 'pictures' rotor_sel = (rotor2, rotor4, rotor8) From 6828f5ffded8170c713a95ebfa6cada2e21d2b0c Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 21:34:58 +0200 Subject: [PATCH 08/21] Update enigma_machine.py --- ciphers/enigma_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index a8a85e0db3a2..4ee33ca4590c 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -44,7 +44,7 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), -{'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) + {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) :param rotpos: rotor_positon :param rotsel: rotor_selection From baae09bb1f5e4076a26146e00a06ba78c2436b85 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 21:44:24 +0200 Subject: [PATCH 09/21] Update enigma_machine.py --- ciphers/enigma_machine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine.py index 4ee33ca4590c..36714c74c944 100644 --- a/ciphers/enigma_machine.py +++ b/ciphers/enigma_machine.py @@ -79,7 +79,7 @@ def _plugboard(pbl: str) -> dict: https://en.wikipedia.org/wiki/Enigma_machine#Plugboard >>> _plugboard('PICTURES') -{'P': 'I', 'I': 'P', 'C': 'T', 'T': 'C', 'U': 'R', 'R': 'U', 'E': 'S', 'S': 'E'} + {'P': 'I', 'I': 'P', 'C': 'T', 'T': 'C', 'U': 'R', 'R': 'U', 'E': 'S', 'S': 'E'} >>> _plugboard('POLAND') {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} From e5fc5d58b24a4a1ca502d9ded33f88943e52ca9b Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 21:54:00 +0200 Subject: [PATCH 10/21] Update enigma_machine2.py --- ciphers/{enigma_machine.py => enigma_machine2.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ciphers/{enigma_machine.py => enigma_machine2.py} (100%) diff --git a/ciphers/enigma_machine.py b/ciphers/enigma_machine2.py similarity index 100% rename from ciphers/enigma_machine.py rename to ciphers/enigma_machine2.py From 7308a8578eedae76f1f8d0f1658006a1bc0c9c60 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Fri, 21 Aug 2020 22:08:37 +0200 Subject: [PATCH 11/21] Update enigma_machine2.py --- ciphers/enigma_machine2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 36714c74c944..123307fc2faf 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -42,9 +42,9 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: Checks if the values can be used for the 'enigma' function >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') - ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', - 'ZJXESIUQLHAVRMDOYGTNFWPBKC'), - {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) + ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \ +'ZJXESIUQLHAVRMDOYGTNFWPBKC'),\ +{'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) :param rotpos: rotor_positon :param rotsel: rotor_selection From a98f1ea3cdcc9ac122b8dc9fb98f194207035eef Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 10:38:48 +0200 Subject: [PATCH 12/21] added f-strings --- ciphers/enigma_machine2.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 123307fc2faf..1259b21a9468 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -52,21 +52,19 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: :return: (rotpos, rotsel, pb) """ # Checks if there are 3 unique rotors - if len(set(rotsel)) < 3: - raise Exception('Please use 3 unique rotors (not ' - + str(len(set(rotsel))) + ')') + + unrotsel = len(set(rotsel)) + if unrotsel < 3: + raise Exception(f'Please use 3 unique rotors (not {unrotsel})') # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos - if rotorpos1 < 1 or rotorpos1 > len(abc): - raise ValueError('First rotor position is not within range of 1..74 (' - + str(rotorpos1) + ')') - if rotorpos2 < 1 or rotorpos2 > len(abc): - raise ValueError('Second rotor position is not within range of 1..74 (' - + str(rotorpos2) + ')') - if rotorpos3 < 1 or rotorpos3 > len(abc): - raise ValueError('Third rotor position is not within range of 1..74 (' - + str(rotorpos3) + ')') + if not 0 < rotorpos1 <= len(abc): + raise ValueError(f'First rotor position is not within range of 1..74 ({rotorpos1})') + if not 0 < rotorpos2 <= len(abc): + raise ValueError(f'Second rotor position is not within range of 1..74 ({rotorpos2})') + if not 0 < rotorpos3 <= len(abc): + raise ValueError(f'Third rotor position is not within range of 1..74 ({rotorpos3})') # Validates string and returns dict pb = _plugboard(pb) @@ -91,11 +89,9 @@ def _plugboard(pbl: str) -> dict: # a) is type string # b) has even length (so pairs can be made) if type(pbl) is not str: - raise TypeError('PLugboard setting isn\'t type string (' - + str(type(pbl)) + ')') + raise TypeError(f'Plugboard setting isn\'t type string ({type(pbl)})') elif len(pbl) % 2 != 0: - raise Exception('Odd number of symbols (' - + str(len(pbl)) + ')') + raise Exception(f'Odd number of symbols ({len(pbl)})') elif pbl == '': return {} @@ -105,7 +101,7 @@ def _plugboard(pbl: str) -> dict: if i not in abc: raise Exception('Not in list of symbols') elif i in tmppbl: - raise Exception('Duplicate symbol (' + i + ')') + raise Exception(f'Duplicate symbol ({i})') else: tmppbl.add(i) del tmppbl From 0dbef0863fd082188196b9fb3035bccafb5e209d Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 10:45:57 +0200 Subject: [PATCH 13/21] Update enigma_machine2.py --- ciphers/enigma_machine2.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 1259b21a9468..a7b931858615 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -43,7 +43,7 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: >>> _validator((1,1,1), (rotor1, rotor2, rotor3), 'POLAND') ((1, 1, 1), ('EGZWVONAHDCLFQMSIPJBYUKXTR', 'FOBHMDKEXQNRAULPGSJVTYICZW', \ -'ZJXESIUQLHAVRMDOYGTNFWPBKC'),\ +'ZJXESIUQLHAVRMDOYGTNFWPBKC'), \ {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'}) :param rotpos: rotor_positon @@ -60,11 +60,14 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos if not 0 < rotorpos1 <= len(abc): - raise ValueError(f'First rotor position is not within range of 1..74 ({rotorpos1})') + raise ValueError( + f'First rotor position is not within range of 1..74 ({rotorpos1})') if not 0 < rotorpos2 <= len(abc): - raise ValueError(f'Second rotor position is not within range of 1..74 ({rotorpos2})') + raise ValueError(f'Second rotor position is not within range of 1..74 (' + f'{rotorpos2})') if not 0 < rotorpos3 <= len(abc): - raise ValueError(f'Third rotor position is not within range of 1..74 ({rotorpos3})') + raise ValueError(f'Third rotor position is not within range of 1..74 (' + f'{rotorpos3})') # Validates string and returns dict pb = _plugboard(pb) @@ -160,8 +163,6 @@ def enigma(text: str, rotor_position: tuple, 'HELLO WORLD' - - :param text: input message :param rotor_position: tuple with 3 values in range 1..74 :param rotor_selection: tuple with 3 rotors () From d09dfc73537c01c0bea3a1213dd9a6018a15bca0 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 15:57:08 +0200 Subject: [PATCH 14/21] Update enigma_machine2.py --- ciphers/enigma_machine2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index a7b931858615..7314e22f975a 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -11,6 +11,8 @@ - 9 randnomly generated rotors - reflector (aka static rotor) - original alphabet + +Created by TrapinchO """ # used alphabet -------------------------- From fa137fd2ffe7a8ba3538eec9844a046486171662 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 16:10:06 +0200 Subject: [PATCH 15/21] Updated some numbers --- ciphers/enigma_machine2.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 7314e22f975a..72c351acda0e 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -62,13 +62,13 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos if not 0 < rotorpos1 <= len(abc): - raise ValueError( - f'First rotor position is not within range of 1..74 ({rotorpos1})') + raise ValueError(f'First rotor position is not within range of 1..26 (' + f'{rotorpos1}') if not 0 < rotorpos2 <= len(abc): - raise ValueError(f'Second rotor position is not within range of 1..74 (' + raise ValueError(f'Second rotor position is not within range of 1..26 (' f'{rotorpos2})') if not 0 < rotorpos3 <= len(abc): - raise ValueError(f'Third rotor position is not within range of 1..74 (' + raise ValueError(f'Third rotor position is not within range of 1..26 (' f'{rotorpos3})') # Validates string and returns dict @@ -166,7 +166,7 @@ def enigma(text: str, rotor_position: tuple, :param text: input message - :param rotor_position: tuple with 3 values in range 1..74 + :param rotor_position: tuple with 3 values in range 1..26 :param rotor_selection: tuple with 3 rotors () :param plugb: string containing plugboard configuration (default '') :return: en/decrypted string From 3d6082138835885e0aa1ba250eedf280b491d3da Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 16:35:24 +0200 Subject: [PATCH 16/21] Plugboard improvement Added option to separate pair for plugboard by spaces --- ciphers/enigma_machine2.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 72c351acda0e..9c13e5a6d660 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -86,6 +86,7 @@ def _plugboard(pbl: str) -> dict: >>> _plugboard('POLAND') {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} + Pairs can be separated by spaces :param pbl: string containing plugboard setting for the Enigma machine :return: dictionary of """ @@ -93,13 +94,15 @@ def _plugboard(pbl: str) -> dict: # tests the input string if it # a) is type string # b) has even length (so pairs can be made) - if type(pbl) is not str: + if not isinstance(pbl, str): raise TypeError(f'Plugboard setting isn\'t type string ({type(pbl)})') elif len(pbl) % 2 != 0: raise Exception(f'Odd number of symbols ({len(pbl)})') elif pbl == '': return {} + pbl.replace(' ', '') + # Checks if all characters are unique tmppbl = set() for i in pbl: From 74cf54c1c142a2578b57f423dc35c0fabacc5b7c Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 16:43:08 +0200 Subject: [PATCH 17/21] renamed variable --- ciphers/enigma_machine2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index 9c13e5a6d660..d895b7f595f1 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -55,9 +55,9 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: """ # Checks if there are 3 unique rotors - unrotsel = len(set(rotsel)) - if unrotsel < 3: - raise Exception(f'Please use 3 unique rotors (not {unrotsel})') + unique_rotsel = len(set(rotsel)) + if unique_rotsel < 3: + raise Exception(f'Please use 3 unique rotors (not {unique_rotsel})') # Checks if rotor positions are valid rotorpos1, rotorpos2, rotorpos3 = rotpos From fecaefcf7b158b10bccd502f979692b7e8596ec4 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 17:04:36 +0200 Subject: [PATCH 18/21] renamed some variables --- ciphers/enigma_machine2.py | 42 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index d895b7f595f1..e73de5a3da13 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -77,7 +77,7 @@ def _validator(rotpos: tuple, rotsel: tuple, pb: str) -> tuple: return rotpos, rotsel, pb -def _plugboard(pbl: str) -> dict: +def _plugboard(pbstring: str) -> dict: """ https://en.wikipedia.org/wiki/Enigma_machine#Plugboard @@ -87,25 +87,25 @@ def _plugboard(pbl: str) -> dict: {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} Pairs can be separated by spaces - :param pbl: string containing plugboard setting for the Enigma machine - :return: dictionary of + :param pbstring: string containing plugboard setting for the Enigma machine + :return: dictionary containing converted pairs """ # tests the input string if it # a) is type string # b) has even length (so pairs can be made) - if not isinstance(pbl, str): - raise TypeError(f'Plugboard setting isn\'t type string ({type(pbl)})') - elif len(pbl) % 2 != 0: - raise Exception(f'Odd number of symbols ({len(pbl)})') - elif pbl == '': + if not isinstance(pbstring, str): + raise TypeError(f'Plugboard setting isn\'t type string ({type(pbstring)})') + elif len(pbstring) % 2 != 0: + raise Exception(f'Odd number of symbols ({len(pbstring)})') + elif pbstring == '': return {} - pbl.replace(' ', '') + pbstring.replace(' ', '') # Checks if all characters are unique tmppbl = set() - for i in pbl: + for i in pbstring: if i not in abc: raise Exception('Not in list of symbols') elif i in tmppbl: @@ -115,12 +115,12 @@ def _plugboard(pbl: str) -> dict: del tmppbl # Created the dictionary - pb = {} - for i in range(0, len(pbl) - 1, 2): - pb[pbl[i]] = pbl[i + 1] - pb[pbl[i + 1]] = pbl[i] + plugb = {} + for i in range(0, len(pbstring) - 1, 2): + plugb[pbstring[i]] = pbstring[i + 1] + plugb[pbstring[i + 1]] = pbstring[i] - return pb + return plugb def enigma(text: str, rotor_position: tuple, @@ -176,7 +176,7 @@ def enigma(text: str, rotor_position: tuple, """ text = text.upper() - rotor_position, rotor_selection, pb = _validator( + rotor_position, rotor_selection, plugboard = _validator( rotor_position, rotor_selection, plugb.upper()) rotorpos1, rotorpos2, rotorpos3 = rotor_position @@ -184,7 +184,7 @@ def enigma(text: str, rotor_position: tuple, rotorpos1 -= 1 rotorpos2 -= 1 rotorpos3 -= 1 - pb = pb + plugboard = plugboard result = [] @@ -193,8 +193,8 @@ def enigma(text: str, rotor_position: tuple, if symbol in abc: # 1st plugboard -------------------------- - if symbol in pb: - symbol = pb[symbol] + if symbol in plugboard: + symbol = plugboard[symbol] # rotor ra -------------------------- index = abc.index(symbol) + rotorpos1 @@ -219,8 +219,8 @@ def enigma(text: str, rotor_position: tuple, symbol = abc[rotor1.index(symbol) - rotorpos1] # 2nd plugboard - if symbol in pb: - symbol = pb[symbol] + if symbol in plugboard: + symbol = plugboard[symbol] # moves/resets rotor positions rotorpos1 += 1 From c2553be8b6fd041829a4354dc585336817e6c2ed Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 17:06:42 +0200 Subject: [PATCH 19/21] improved plugboard exception --- ciphers/enigma_machine2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index e73de5a3da13..e227ce6f884c 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -107,7 +107,7 @@ def _plugboard(pbstring: str) -> dict: tmppbl = set() for i in pbstring: if i not in abc: - raise Exception('Not in list of symbols') + raise Exception(f' \'{i}\'not in list of symbols') elif i in tmppbl: raise Exception(f'Duplicate symbol ({i})') else: From 8dd72212e71aae98b63bf1daf3e118424eaaaa98 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 17:10:33 +0200 Subject: [PATCH 20/21] Update enigma_machine2.py --- ciphers/enigma_machine2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index e227ce6f884c..bf04b21a166d 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -107,7 +107,7 @@ def _plugboard(pbstring: str) -> dict: tmppbl = set() for i in pbstring: if i not in abc: - raise Exception(f' \'{i}\'not in list of symbols') + raise Exception(f'\'{i}\' not in list of symbols') elif i in tmppbl: raise Exception(f'Duplicate symbol ({i})') else: From 89a00b3b4922c9abba2d10b2833c6e5008437235 Mon Sep 17 00:00:00 2001 From: TrapinchO <67415128+TrapinchO@users.noreply.github.com> Date: Sat, 22 Aug 2020 17:13:44 +0200 Subject: [PATCH 21/21] Update enigma_machine2.py --- ciphers/enigma_machine2.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ciphers/enigma_machine2.py b/ciphers/enigma_machine2.py index bf04b21a166d..4c79e1c2fab9 100644 --- a/ciphers/enigma_machine2.py +++ b/ciphers/enigma_machine2.py @@ -86,6 +86,8 @@ def _plugboard(pbstring: str) -> dict: >>> _plugboard('POLAND') {'P': 'O', 'O': 'P', 'L': 'A', 'A': 'L', 'N': 'D', 'D': 'N'} + In the code, 'pb' stands for 'plugboard' + Pairs can be separated by spaces :param pbstring: string containing plugboard setting for the Enigma machine :return: dictionary containing converted pairs @@ -115,12 +117,12 @@ def _plugboard(pbstring: str) -> dict: del tmppbl # Created the dictionary - plugb = {} + pb = {} for i in range(0, len(pbstring) - 1, 2): - plugb[pbstring[i]] = pbstring[i + 1] - plugb[pbstring[i + 1]] = pbstring[i] + pb[pbstring[i]] = pbstring[i + 1] + pb[pbstring[i + 1]] = pbstring[i] - return plugb + return pb def enigma(text: str, rotor_position: tuple,