Programmierung
Contents
Programmierung¶
Lest sorgfältig die Aufgabenstellung und ergänzt den Code.
Important
Mit dem # TODO #
werden stellen markiert die ihr Bearbeiten müsst.
Cäsar Chiffre¶
Aber als erstes wird ein Alphabet benötigt.
# Führe diese Zelle aus
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
TODO 1¶
# TODO 1
TODO 2¶
def isLetter(character: str):
"""Checks if the character is a letter in the alphabet
Args:
character (str): character to verify
Returns:
bool: if character is a letter
"""
# TODO 2
# Test the function
print("H is a letter: {}".format(isLetter("H")))
print("Ä is a letter: {}".format(isLetter("Ä")))
TODO 3¶
def idxOfLetter(letter: str):
""" Function returns the index of the letter within the alphabet
Args:
letter (str): letter to search
Returns:
int: index of letter in the alphabet
"""
# TODO 3
# Test
print(idxOfLetter("A"))
print(idxOfLetter("H"))
print(idxOfLetter("Z"))
TODO 4¶
def incrementIndex(index:int, k:int):
"""increments the index according to the k value, the output value stays within the range [0-25]
Args:
index (int): index of letter
k (int): index shift
Returns:
int: new index of letter
"""
# TODO 4
# Test the function
print("The index {} has shifted by {} = {}".format(7, 1, incrementIndex(7, 1)))
print("The index {} has shifted by {} = {}".format(24, 1, incrementIndex(24, 1)))
print("The index {} has shifted by {} = {}".format(25, 1, incrementIndex(25, 1)))
print("The index {} has shifted by {} = {}".format(0, 1, incrementIndex(0, 1)))
print("The index {} has shifted by {} = {}".format(7, -1, incrementIndex(7, -1)))
print("The index {} has shifted by {} = {}".format(24, -1, incrementIndex(24, -1)))
print("The index {} has shifted by {} = {}".format(25, -1, incrementIndex(25, -1)))
print("The index {} has shifted by {} = {}".format(0, -1, incrementIndex(0, -1)))
TODO 5¶
def cesarEncoding(text: str, k: int):
"""Takes a text and encodes it.
Args:
text (str): text to be encoded
k (int): character shift (positive and negative)
Returns:
str: encoded text
"""
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
text_encoded = ""
# Loop though every character in the input text
for char in text:
# TODO 5
# 1. Kontrollieren ob der Character ein Buchstabe ist
# 2. Suchen des Index des Buchstaben
# 3. Inkrementieren oder dekrementieren des Indexes
# 4. Suchen des neuen Buchstaben
pass
return text_encoded
# Test of the function
plain_text = "Mit ihren Bachelor-Studiengaengen stellt die HES-SO Valais-Wallis in Sitten ein echtes Kompetenz und Innovationszentrum dar"
cesarEncoding(text=plain_text, k=3)
Entwickeln der Enigma¶
Das Klassendiagram der Applikation:
Scrambler¶
Aufgabe 6-8
class Scrambler:
def __init__(self, type_key: str = None, startpos: int = 0, custom: str = None):
"""Create a alphabet shuffle, this represents one rotor or a reflector. The configuration can be choosen from the first ever Enigma, the latest WWII Enigma machine, a Random pattern or a custom setting.
The reflector and rotor settings are according to Wikipedia https://en.wikipedia.org/wiki/Enigma_rotor_details:
Args:
type_key (str): type of enigma rotor ["etw", "i", "ii", "iii", ..., "custom"]
startpos (int, optional): rotor startposition. Defaults to 0.
custom (string, optional): string of chars representing the custom configuration. Defaults to None.
"""
self.alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
# Possible rotor configurations
self.configs = {
"etw" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"i" : "EKMFLGDQVZNTOWYHXUSPAIBRCJ",
"ii" : "AJDKSIRUXBLHWTMCQGZNPYFVOE",
"iii" : "BDFHJLCPRTXVZNYEIWGAKMUSQO",
"iv" : "ESOVPZJAYQUIRHXLNFTGKDCMWB",
"v" : "VZBRGITYUPSDNHLXAWMJQOFECK",
"vi" : "JPGVOUMFYQBENHZRDKASXLICTW",
"vii" : "NZJHGRCXMYSWBOUFAIVLPEKQDT",
"viii" : "FKQHTLXOCBJSPDZRAMEWNIUYGV",
"a" : "EJMZALYXVBWFCRQUONTSPIKHGD",
"b" : "YRUHQSLDPXNGOKMIEBFZCWVJAT",
"c" : "FVPJIAOYEDRZXWGCTKUQSBNMHL",
}
# TODO 6
self.type_key = type_key
self.startpos = startpos
# get the key
if self.type_key == "custom":
self.transformation = self.getConfig(custom)
else:
self.transformation = self.getConfig(self.configs[self.type_key])
# setup initial position of rotors
self.transformation = self.rol(self.transformation, self.startpos)
self.key = self.getKey()
def getConfig(self, str_config: str):
"""Transforms the string configuration into an int array
Args:
str_config (str): string of configuration. All alphabet characters need to be represented.
Returns:
list: list of int representing the alphabet positions of the config
"""
config = []
# TODO 7
return config
def getKey(self):
"""Get the key of the current transformation config
Returns:
str: string of characters of the current config
"""
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
key = ""
for idx in self.transformation:
key += self.alphabet[idx]
return key
def rol(self, string: str, n: int):
"""Rotating shift left of a string by n characters
example: n=2
"Test" => "stTe"
Args:
string (str): input string
n (int): number of bits to shift
Returns:
str: string rotated shift left by n chars
"""
# TODO 8
return string
def passthrough(self, idx: int):
"""Pass element through (index => element)
Args:
idx (int): index of character index to return
Returns:
int: new character index
"""
return self.transformation[idx]
def passthroughRev(self, elem):
"""Reverse Passthrough, enter character index and return list index
Returns:
int: index of character index
"""
return self.transformation.index(elem)
def rotate(self):
"""Rotate the rotors by one position
"""
self.transformation = self.rol(self.transformation, 1)
def setTransformation(self, transformation: list):
"""Set manually the tranformation. E.g. to reset the machine
Args:
transformation (list): transformation list to be used
"""
self.transformation = transformation
Test Scrambler¶
scrambler = Scrambler("E-t W_", 2)
scrambler.rol("Test", 1)
scrambler.rol("Test", 2)
Enigma Machine¶
Aufgabe 9 - 11
EnigmaMachine Klassendiagramm
class EnigmaMachine:
def __init__(self, nb_rotors: int = 3, rotor_types: list = ["i", "iii", "iii"], rotor_startpos: list = [1, 2, 3], rotor_custom_configs: list = None, reflector_type: str = "a", plugboard_config: list = None, print_specialchars: bool = False):
"""Enigma Virtual Machine
nb_rotors (int, optional): number of rotors in the machine. Defaults to 3.
rotor_types (list, optional): list of types rotors types ["etw"|"i"|"ii"|"iii"|"iv"|"v"|"vi"|"vii"|"viii"]. Needs to be size of nb_rotors. Defaults to ["i", "ii", "iii"].
rotor_startpos (list, optional): list of int representing thestart positions of the rotors. Needs to be the size of nb_rotors. Defaults to [1, 2, 3].
rotor_custom_configs (list, optional): list of int lists representing the custom rotor configuration, only needed if "custom" type is choosen. Needs to be the size of nb_rotors if used. Defaults to None
reflector_type (str, optional): type of reflector ["a"|"b"|"c"]. Defaults to "a".
plugboard_config (list, optional): list of character combinations. Defaults to None, will result in A<->Z, B<->Y, ...
print_specialchars (bool, optional): Print characters missing by enigma. Defaults to False.
"""
self.nb_rotors = nb_rotors
self.rotor_types = rotor_types
self.rotor_startpos = rotor_startpos
self.rotor_custom_configs = rotor_custom_configs
self.reflector_type = reflector_type
self.printspecialchars = print_specialchars
if plugboard_config is None:
self.plugboard_config = ["AZ", "BY", "CX", "DW", "EV", "FU", "GT", "HS", "IR", "JQ", "KP", "LO", "MN"]
else:
self.plugboard_config = plugboard_config
# create the rotors and reflector
self.rotors = []
self.original_rotors = []
self.reflector = None
self.plugboard = None
self.setupRotors()
self.setupReflector()
self.setupPlugboard()
def setupRotors(self):
"""Setup the rotors configuration
"""
for i in range(self.nb_rotors):
if self.rotor_custom_configs is None:
self.rotors.append(Scrambler(self.rotor_types[i], self.rotor_startpos[i]))
else:
self.rotors.append(Scrambler(self.rotor_types[i], self.rotor_startpos[i], self.rotor_custom_configs[i]))
self.original_rotors.append(self.rotors[i].transformation)
def setupReflector(self):
"""Setup the reflector
"""
self.reflector = Scrambler(self.reflector_type)
def setupPlugboard(self):
"""Setup the plugboard"""
# Transform into scrambler key
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
key = " " * 26
for elem in self.plugboard_config:
# TODO 9
pass
self.plugboard = Scrambler("custom", 0, key)
def printEnigmaSetup(self):
"""Print Enigma setup of plugboard, rotors and reflector
"""
print("Enigma Setup")
print("============\n")
# TODO 10
def reset(self):
"""Restart the original rotor start positions
"""
for i in range(0, self.nb_rotors):
self.rotors[i].setTransformation(self.original_rotors[i])
def encode(self, text: str):
"""Encode and decode a string
Args:
text (str): string to encode
Returns:
str : depending on the input string, the encoded or decoded output
"""
ln = 0
encrypted_text = ""
for l in text.lower():
# get char position in alphabet
num = ord(l) % 97
if (num > 25 or num < 0):
# Special character
if (self.printspecialchars):
encrypted_text += l
else:
# encodable character
ln += 1
# TODO 11
# pass through plugboard
num = self.plugboard.passthrough(num)
# pass through all rotors
# reflected by the reflector
# pass through reverse all the rotors
# pass through reverse the plugboard
# Encode character
encrypted_text += "" + chr(97 + num)
# rotate the rotors
for i in range(0, self.nb_rotors):
if (ln % ((i * 6) + 1) == 0):
self.rotors[i].rotate()
return encrypted_text
Test¶
plaintext = """Die Enigma-Maschinen waren eine Reihe von elektromechanischen Rotor-Chiffriermaschinen, die Anfang bis Mitte des 20. Jahrhunderts entwickelt und eingesetzt wurden, um die kommerzielle, diplomatische und militärische Kommunikation zu schützen. Die Enigma wurde von dem deutschen Ingenieur Arthur Scherbius am Ende des Ersten Weltkriegs erfunden. Frühe Modelle wurden ab den frühen 1920er Jahren kommerziell genutzt und von den Militär- und Regierungsstellen mehrerer Länder übernommen, vor allem von Nazi-Deutschland vor und während des Zweiten Weltkriegs. Es wurden mehrere verschiedene Enigma-Modelle hergestellt, aber die deutschen Militärmodelle mit einer Steckplatte waren die komplexesten. Auch japanische und italienische Modelle waren in Gebrauch."""
enigma = EnigmaMachine(nb_rotors=3, rotor_types=["i","iii","vi"], rotor_startpos=[17,23,12], reflector_type="a", print_specialchars=True)
print("\nPlaintext:\n{}\n".format(plaintext))
# Encrypt
enigma.reset()
encrypted_text = enigma.encode(plaintext)
print("Encrypted text:\n{}\n".format(encrypted_text))
# Decrypt
enigma.reset()
plaintext = enigma.encode(encrypted_text)
print("Plaintext:\n{}".format(plaintext))
Plaintext:
Die Enigma-Maschinen waren eine Reihe von elektromechanischen Rotor-Chiffriermaschinen, die Anfang bis Mitte des 20. Jahrhunderts entwickelt und eingesetzt wurden, um die kommerzielle, diplomatische und militärische Kommunikation zu schützen. Die Enigma wurde von dem deutschen Ingenieur Arthur Scherbius am Ende des Ersten Weltkriegs erfunden. Frühe Modelle wurden ab den frühen 1920er Jahren kommerziell genutzt und von den Militär- und Regierungsstellen mehrerer Länder übernommen, vor allem von Nazi-Deutschland vor und während des Zweiten Weltkriegs. Es wurden mehrere verschiedene Enigma-Modelle hergestellt, aber die deutschen Militärmodelle mit einer Steckplatte waren die komplexesten. Auch japanische und italienische Modelle waren in Gebrauch.
Encrypted text:
qwn hrvsdg-yhzjugglt egeyv xdkw bkxoo gnc pebiftctxlnhqdadgrm zprda-nyzqswazzqqkeefkax, xqo rfecur zta pbfqi abr 20. otspyftrtpjp dsoskrmtsm txa bwslnxwlrz ekwhir, qg kqa aychooioocri, pridavekyyqvs fzb oskbnäeqfbud nlpkdlvpyoftt nb eslüggso. jjz wajreq qlhcw hdi bcp jkxqomfiq pswzbehmk bwhven osbucjjfn dn rxst xyc tzabhe ekfhzsgrer ddgdbbcw. teüvc jystgfd knexyo gi bcj dsüffs 1920cj iprbke tmvaxpgytnb hdcrbyw yhl hev xjc lbsbxäe- vqb haexshgqbpopruqvj gtjmrlrj bäunoh üjxwqrepfq, gfu wvuyd lfx iyqc-jdryjqgdufu eqp lxk täeyowj uvb ildhemf zyhusgbzux. se oghuvi sdivbds zpnegdeatuka aqwuah-avkokmy ajgllwggedq, hili mls yzoqeszum bpfjvägltcdtdm sqh qpyfx cduptmznenk hddal tzi bzadplvxmjzx. svun dlvsomfdxf vqf jrzuhlbuyrrt swzmgyk gytdx su dhybjllp.
Plaintext:
die enigma-maschinen waren eine reihe von elektromechanischen rotor-chiffriermaschinen, die anfang bis mitte des 20. jahrhunderts entwickelt und eingesetzt wurden, um die kommerzielle, diplomatische und militärische kommunikation zu schützen. die enigma wurde von dem deutschen ingenieur arthur scherbius am ende des ersten weltkriegs erfunden. frühe modelle wurden ab den frühen 1920er jahren kommerziell genutzt und von den militär- und regierungsstellen mehrerer länder übernommen, vor allem von nazi-deutschland vor und während des zweiten weltkriegs. es wurden mehrere verschiedene enigma-modelle hergestellt, aber die deutschen militärmodelle mit einer steckplatte waren die komplexesten. auch japanische und italienische modelle waren in gebrauch.
Verschlüsselte Kommunikation mit eurem Kollegen¶
Als letzte Aufgabe schickt eurem Studienkollegen einen verschlüsselte Nachricht. Hierzu müsst Ihr Ihnen die verschlüsselte Nachricht aber auch das Setup übermitteln.
Verschlüsselte Nachricht¶
message = "TODO"
enigma = EnigmaMachine(nb_rotors=3, rotor_types=["i", "iii", "iv"], rotor_startpos=[17, 23, 12], reflector_type="a", print_specialchars=True)
enigma.printEnigmaSetup()
encrypted_text = enigma.encode(message)
print("\nNachricht\n=========\n")
print(encrypted_text)
Enigma Setup
============
* Rotor 0
- Type : i
- Key : USPAIBRCJEKMFLGDQVZNTOWYHX
- StartPos : 17
* Rotor 1
- Type : iii
- Key : SQOBDFHJLCPRTXVZNYEIWGAKMU
- StartPos : 23
* Rotor 2
- Type : iv
- Key : RHXLNFTGKDCMWBESOVPZJAYQUI
- StartPos : 12
* Reflector
- Type : a
* Plugboard
- Key : ['AZ', 'BY', 'CX', 'DW', 'EV', 'FU', 'GT', 'HS', 'IR', 'JQ', 'KP', 'LO', 'MN']
Nachricht
=========
wrqc
Nachricht des Kollegen entschlüsseln¶
Benutzt nun die erhalten Konfiguration und Nachricht eures Kollegen um diese zu entschlüsseln.
message = "message from Colleague"
# Change the parameter accordingly
enigma = EnigmaMachine(nb_rotors=3, rotor_types=["i", "iii", "iv"], rotor_startpos=[17, 23, 12], reflector_type="a", print_specialchars=True)
enigma.printEnigmaSetup()
decrypted_text = enigma.encode(message)
print("\nNachricht\n=========\n")
print(decrypted_text)
Enigma Setup
============
* Rotor 0
- Type : i
- Key : USPAIBRCJEKMFLGDQVZNTOWYHX
- StartPos : 17
* Rotor 1
- Type : iii
- Key : SQOBDFHJLCPRTXVZNYEIWGAKMU
- StartPos : 23
* Rotor 2
- Type : iv
- Key : RHXLNFTGKDCMWBESOVPZJAYQUI
- StartPos : 12
* Reflector
- Type : a
* Plugboard
- Key : ['AZ', 'BY', 'CX', 'DW', 'EV', 'FU', 'GT', 'HS', 'IR', 'JQ', 'KP', 'LO', 'MN']
Nachricht
=========
xzplxtj hpwh kjuvsmulj
Solution¶
Kein Schummeln!!
# TODO 1
alphabet[7]
def isLetter(character: str):
"""Checks if the character is a letter in the alphabet
Args:
character (str): character to verify
Returns:
bool: if character is a letter
"""
# TODO 2
return character in alphabet
def idxOfLetter(letter: str):
""" Function returns the index of the letter within the alphabet
Args:
letter (str): letter to search
Returns:
int: index of letter in the alphabet
"""
# TODO 3
return alphabet.index(letter)
def incrementIndex(index: int, k: int):
"""increments the index according to the k value, the output value stays within the range [0-25]
Args:
index (int): index of letter
k (int): index shift
Returns:
int: new index of letter
"""
# TODO 4
idx = index + k
return idx % 26
def cesarEncoding(text: str, k: int):
"""Takes a text and encodes it.
Args:
text (str): text to be encoded
k (int): character shift (positive and negative)
Returns:
str: encoded text
"""
text_encoded = ""
# Loop though every character in the input text
for char in text.upper():
# TODO 5
# 1. Kontrollieren ob der Character ein Buchstabe ist
if isLetter(char):
# 2. Suchen des Index des Buchstaben
index = idxOfLetter(char)
# 3. Inkrementieren oder dekrementieren des Indexes um k
new_index = incrementIndex(index, k)
# 4. Suchen des neuen Buchstaben
text_encoded += alphabet[new_index]
else:
# Append character if not a letter
text_encoded += char
return text_encoded
# Test of the function
plain_text = "Mit ihren Bachelor-Studiengaengen stellt die HES-SO Valais-Wallis in Sitten ein echtes Kompetenz und Innovationszentrum dar"
encoded_text = cesarEncoding(text=plain_text, k=3)
decoded_text = cesarEncoding(text=encoded_text, k=-3)
print(plain_text)
print()
print(encoded_text)
print()
print(decoded_text)
class Scrambler:
def __init__(self, type_key: str = None, startpos: int = 0, custom: str = None):
"""Create a alphabet shuffle, this represents one rotor or a reflector. The configuration can be choosen from the first ever Enigma, the latest WWII Enigma machine, a Random pattern or a custom setting.
The reflector and rotor settings are according to Wikipedia https://en.wikipedia.org/wiki/Enigma_rotor_details:
Args:
type_key (str): type of enigma rotor ["etw", "i", "ii", "iii", ..., "custom"]
startpos (int, optional): rotor startposition. Defaults to 0.
custom (string, optional): string of chars representing the custom configuration. Defaults to None.
"""
self.alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
# Possible rotor configurations
self.configs = {
"etw" : "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"i" : "EKMFLGDQVZNTOWYHXUSPAIBRCJ",
"ii" : "AJDKSIRUXBLHWTMCQGZNPYFVOE",
"iii" : "BDFHJLCPRTXVZNYEIWGAKMUSQO",
"iv" : "ESOVPZJAYQUIRHXLNFTGKDCMWB",
"v" : "VZBRGITYUPSDNHLXAWMJQOFECK",
"vi" : "JPGVOUMFYQBENHZRDKASXLICTW",
"vii" : "NZJHGRCXMYSWBOUFAIVLPEKQDT",
"viii" : "FKQHTLXOCBJSPDZRAMEWNIUYGV",
"a" : "EJMZALYXVBWFCRQUONTSPIKHGD",
"b" : "YRUHQSLDPXNGOKMIEBFZCWVJAT",
"c" : "FVPJIAOYEDRZXWGCTKUQSBNMHL",
}
# TODO 6
self.type_key = type_key.lower().replace(" ", "").replace("_", "").replace("-", "")
self.startpos = startpos
# get the key
if self.type_key == "custom":
self.transformation = self.getConfig(custom)
else:
self.transformation = self.getConfig(self.configs[self.type_key])
# setup initial position of rotors
self.transformation = self.rol(self.transformation, self.startpos)
self.key = self.getKey()
def getConfig(self, str_config: str):
"""Transforms the string configuration into an int array
Args:
str_config (str): string of configuration. All alphabet characters need to be represented.
Returns:
list: list of int representing the alphabet positions of the config
"""
config = []
# TODO 7
for char in str_config:
config.append(self.alphabet.index(char))
return config
def getKey(self):
"""Get the key of the current transformation config
Returns:
str: string of characters of the current config
"""
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
key = ""
for idx in self.transformation:
key += self.alphabet[idx]
return key
def rol(self, string: str, n: int):
"""Rotating shift left of a string by n characters
example: n=2
"Test" => "stTe"
Args:
string (str): input string
n (int): number of bits to shift
Returns:
str: string rotated shift left by n chars
"""
# TODO 8
return string[n:] + string[:n]
def passthrough(self, idx: int):
"""Pass element through (index => element)
Args:
idx (int): index of character index to return
Returns:
int: new character index
"""
return self.transformation[idx]
def passthroughRev(self, elem):
"""Reverse Passthrough, enter character index and return list index
Returns:
int: index of character index
"""
return self.transformation.index(elem)
def rotate(self):
"""Rotate the rotors by one position
"""
self.transformation = self.rol(self.transformation, 1)
def setTransformation(self, transformation: list):
"""Set manually the tranformation. E.g. to reset the machine
Args:
transformation (list): transformation list to be used
"""
self.transformation = transformation
class EnigmaMachine:
def __init__(self, nb_rotors: int = 3, rotor_types: list = ["i", "iii", "iii"], rotor_startpos: list = [1, 2, 3], rotor_custom_configs: list = None, reflector_type: str = "a", plugboard_config: list = None, print_specialchars: bool = False):
"""Enigma Virtual Machine
nb_rotors (int, optional): number of rotors in the machine. Defaults to 3.
rotor_types (list, optional): list of types rotors types ["etw"|"i"|"ii"|"iii"|"iv"|"v"|"vi"|"vii"|"viii"]. Needs to be size of nb_rotors. Defaults to ["i", "ii", "iii"].
rotor_startpos (list, optional): list of int representing thestart positions of the rotors. Needs to be the size of nb_rotors. Defaults to [1, 2, 3].
rotor_custom_configs (list, optional): list of int lists representing the custom rotor configuration, only needed if "custom" type is choosen. Needs to be the size of nb_rotors if used. Defaults to None
reflector_type (str, optional): type of reflector ["a"|"b"|"c"]. Defaults to "a".
plugboard_config (list, optional): list of character combinations. Defaults to None, will result in A<->Z, B<->Y, ...
print_specialchars (bool, optional): Print characters missing by enigma. Defaults to False.
"""
self.nb_rotors = nb_rotors
self.rotor_types = rotor_types
self.rotor_startpos = rotor_startpos
self.rotor_custom_configs = rotor_custom_configs
self.reflector_type = reflector_type
self.printspecialchars = print_specialchars
if plugboard_config is None:
self.plugboard_config = ["AZ", "BY", "CX", "DW", "EV", "FU", "GT", "HS", "IR", "JQ", "KP", "LO", "MN"]
else:
self.plugboard_config = plugboard_config
# create the rotors and reflector
self.rotors = []
self.original_rotors = []
self.reflector = None
self.plugboard = None
self.setupRotors()
self.setupReflector()
self.setupPlugboard()
def setupRotors(self):
"""Setup the rotors configuration
"""
for i in range(self.nb_rotors):
if self.rotor_custom_configs is None:
self.rotors.append(Scrambler(self.rotor_types[i], self.rotor_startpos[i]))
else:
self.rotors.append(Scrambler(self.rotor_types[i], self.rotor_startpos[i], self.rotor_custom_configs[i]))
self.original_rotors.append(self.rotors[i].transformation)
def setupReflector(self):
"""Setup the reflector
"""
self.reflector = Scrambler(self.reflector_type)
def setupPlugboard(self):
"""Setup the plugboard"""
# Transform into scrambler key
alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
key = " " * 26
for elem in self.plugboard_config:
# TODO 9
key = key[:alphabet.index(elem[0])] + elem[1] + key[alphabet.index(elem[0])+1:]
key = key[:alphabet.index(elem[1])] + elem[0] + key[alphabet.index(elem[1])+1:]
self.plugboard = Scrambler("custom", 0, key)
def printEnigmaSetup(self):
"""Print Enigma setup of plugboard, rotors and reflector
"""
print("Enigma Setup")
print("============\n")
# TODO 10
for i in range(self.nb_rotors):
print("* Rotor {}".format(i))
print(" - Type : {}".format(self.rotors[i].type_key))
print(" - Key : {}".format(self.rotors[i].key))
print(" - StartPos : {}".format(self.rotors[i].startpos))
print("* Reflector")
print(" - Type : {}".format(self.reflector.type_key))
if self.reflector.type_key == "custom" or self.reflector.type_key == "random":
print(" - Key : {}".format(self.reflector.key))
print(" - StartPos : {}".format(self.reflector.startpos))
print("* Plugboard")
print(" - Key : {}".format(self.plugboard_config))
def reset(self):
"""Restart the original rotor start positions
"""
for i in range(0, self.nb_rotors):
self.rotors[i].setTransformation(self.original_rotors[i])
def encode(self, text: str):
"""Encode and decode a string
Args:
text (str): string to encode
Returns:
str : depending on the input string, the encoded or decoded output
"""
ln = 0
encrypted_text = ""
for l in text.lower():
# get char position in alphabet
num = ord(l) % 97
if (num > 25 or num < 0):
# Special character
if (self.printspecialchars):
encrypted_text += l
else:
# encodable character
ln += 1
# TODO 11
# pass through plugboard
num = self.plugboard.passthrough(num)
# pass through rotors
for i in range(0, self.nb_rotors):
num = self.rotors[i].passthrough(num)
# reflected by the reflector
num = self.reflector.passthrough(num)
# pass through rotors from the other side
for i in range(0, self.nb_rotors):
num = self.rotors[self.nb_rotors - i - 1].passthroughRev(num)
# pass through plugboard from the other side
num = self.plugboard.passthroughRev(num)
# Encode character
encrypted_text += "" + chr(97 + num)
# rotate the rotors
for i in range(0, self.nb_rotors):
if (ln % ((i * 6) + 1) == 0):
self.rotors[i].rotate()
return encrypted_text