Pilot_76
07-21-24, 05:59 PM
I had the idea of having MS Copilot to play a role as Bdu and to send us "Bdu" messages from time to time, but with the added feature of it actually sending encrypted messages using Enigma machine's encryption.
After many attempts I have actually made it wrote a working code in Python. "Working" on its own because when using other Enigma's emulators in order to check the encryption, for some reason the settings don't match at all. Rotor order, rotor's settings and starting positions do not encrypt/decrypt correctly against other Enigma machine simulators. Again, it does work when you encrypt/decrypt using its own messages. I know there are many other Enigma machine's written in Python, but I wanted to see MS Copilot to write one by itself.
So I am leaving the code here to see if any expert Python programmer can figure out why this code does not work when using other Enigma Machine simulators.
The code starts below this line:
import random
class Rotor:
def __init__(self, mappings, notch, ring_setting=0):
self.mappings = mappings
self.notch = notch
self.ring_setting = ring_setting
self.position = 0
def encode_forward(self, char):
index = (ord(char) - ord('A') + self.position - self.ring_setting) % 26
return chr((ord(self.mappings[index]) - ord('A') - self.position + self.ring_setting) % 26 + ord('A'))
def encode_backward(self, char):
index = (self.mappings.index(chr((ord(char) - ord('A') + self.position - self.ring_setting) % 26 + ord('A'))) - self.position + self.ring_setting) % 26
return chr(index + ord('A'))
def rotate(self):
self.position = (self.position + 1) % 26
return self.position == self.notch
def set_position(self, position):
self.position = position
class Reflector:
def __init__(self, mappings, name):
self.mappings = mappings
self.name = name
def reflect(self, char):
return self.mappings[ord(char) - ord('A')]
class Plugboard:
def __init__(self, connections):
self.connections = connections
def swap(self, char):
return self.connections.get(char, char)
class EnigmaMachine:
def __init__(self, rotors, reflector, plugboard):
self.rotors = rotors
self.reflector = reflector
self.plugboard = plugboard
self.initial_positions = [rotor.position for rotor in rotors]
def encode(self, message):
encoded_message = ''
for char in message:
if char.isalpha():
char = char.upper()
char = self.plugboard.swap(char)
for rotor in self.rotors:
char = rotor.encode_forward(char)
char = self.reflector.reflect(char)
for rotor in reversed(self.rotors):
char = rotor.encode_backward(char)
char = self.plugboard.swap(char)
encoded_message += char
for rotor in self.rotors:
if not rotor.rotate():
break
else:
encoded_message += char
return encoded_message
def reset_rotors(self):
for rotor, position in zip(self.rotors, self.initial_positions):
rotor.set_position(position)
def get_rotor_by_number(number, rotors):
return rotors[number - 1]
def get_random_position():
return random.randint(0, 25)
def get_random_plugboard():
letters = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
random.shuffle(letters)
connections = {}
for i in range(0, len(letters), 2):
connections[letters[i]] = letters[i+1]
connections[letters[i+1]] = letters[i]
return Plugboard(connections)
def get_specific_plugboard():
connections = {}
while True:
pair = input("Enter plugboard pair (e.g., AB) or press Enter to finish: ").strip().upper()
if not pair:
break
if len(pair) == 2 and pair[0] != pair[1]:
connections[pair[0]] = pair[1]
connections[pair[1]] = pair[0]
else:
print("Invalid pair. Please enter two different letters.")
return Plugboard(connections)
def format_output(message, group_size):
return ' '.join([message[i:i+group_size] for i in range(0, len(message), group_size)])
def display_configuration(rotors, reflector, plugboard):
roman_numerals = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "Beta", "Gamma"]
rotor_order = '-'.join([roman_numerals[rotors.index(rotor)] for rotor in rotors])
rotor_settings = '-'.join([f"{rotor.ring_setting + 1}" for rotor in rotors])
rotor_positions = '-'.join([chr(rotor.position + ord('A')) for rotor in rotors])
plugboard_info = ', '.join([f"{k}{v}" for k, v in plugboard.connections.items() if k < v])
config = [
f"Rotors Order: {rotor_order}",
f"Rotors Settings: {rotor_settings}",
f"Rotors Starting Positions: {rotor_positions}",
f"Reflector: {reflector.name}",
f"Plugboards: {plugboard_info if plugboard_info else 'None'}"
]
print("\nCurrent Enigma Configuration:")
print("\n".join(config))
def main():
rotors_m3 = [
Rotor("EKMFLGDQVZNTOWYHXUSPAIBRCJ", 16), # Rotor I
Rotor("AJDKSIRUXBLHWTMCQGZNPYFVOE", 4), # Rotor II
Rotor("BDFHJLCPRTXVZNYEIWGAKMUSQO", 21), # Rotor III
Rotor("ESOVPZJAYQUIRHXLNFTGKDCMWB", 9), # Rotor IV
Rotor("VZBRGITYUPSDNHLXAWMJQOFECK", 25), # Rotor V
Rotor("JPGVOUMFYQBENHZRDKASXLICTW", 12), # Rotor VI
Rotor("NZJHGRCXMYSWBOUFAIVLPEKQDT", 12), # Rotor VII
Rotor("FKQHTLXOCBJSPDZRAMEWNIUYGV", 12) # Rotor VIII
]
rotors_m4 = rotors_m3 + [
Rotor("LEYJVCNIXWPBQMDRTAKZGFUHOS", 0), # Rotor Beta
Rotor("FSOKANUERHMBTIYCWLQPZXVGJD", 0) # Rotor Gamma
]
reflector_b = Reflector("YRUHQSLDPXNGOKMIEBFZCWVJAT", "B")
reflector_c = Reflector("FVPJIAOYEDRZXWGCTKUQSBNMHL", "C")
while True:
machine_type = input("Select Enigma Machine Type (1 for M3, 2 for M4): ").strip()
if machine_type == "1":
num_rotors = 3
rotors = rotors_m3
elif machine_type == "2":
num_rotors = 4
rotors = rotors_m4
else:
print("Invalid machine type selected.")
continue
config_choice = input("Select Configuration (1 for Random, 2 for Specific): ").strip()
selected_rotors = []
if config_choice == "1":
selected_rotors = random.sample(rotors, num_rotors)
for rotor in selected_rotors:
rotor.position = get_random_position()
rotor.ring_setting = get_random_position()
reflector = random.choice([reflector_b, reflector_c])
plugboard = get_random_plugboard()
elif config_choice == "2":
for i in range(num_rotors):
rotor_number = int(input(f"Select Rotor {i+1} (1-8 for M3, 1-10 for M4): "))
rotor = get_rotor_by_number(rotor_number, rotors)
start_position = input(f"Enter starting position for Rotor {i+1} (A-Z): ").strip().upper()
rotor.position = ord(start_position) - ord('A')
ring_setting = int(input(f"Enter ring setting for Rotor {i+1} (1-26): ")) - 1
rotor.ring_setting = ring_setting
selected_rotors.append(rotor)
reflector_choice = input("Select Reflector (B/C): ").strip().upper()
if reflector_choice == "B":
reflector = reflector_b
elif reflector_choice == "C":
reflector = reflector_c
else:
print("Invalid reflector selected.")
continue
plugboard = get_specific_plugboard()
else:
print("Invalid configuration selected.")
continue
display_configuration(selected_rotors, reflector, plugboard)
group_size = int(input("Enter group size for output (4 or 5): "))
if group_size not in [4, 5]:
print("Invalid group size selected.")
continue
enigma = EnigmaMachine(selected_rotors, reflector, plugboard)
while True:
print("\nChoose an action:")
print("1. Encode")
print("2. Decode")
print("3. Change Machine")
print("4. Change Configuration")
action = input("Enter the number of your choice: ").strip()
if action == "1" or action == "2":
message = input("Enter message to encode/decode: ")
enigma.reset_rotors() # Reset rotors to initial positions before encoding/decoding
encoded_message = enigma.encode(message)
formatted_message = format_output(encoded_message, group_size)
print(f"Encoded/Decoded Message: {formatted_message}")
elif action == "3":
break
elif action == "4":
break
else:
print("Invalid action selected.")
if action == "3" or action == "4":
continue
if __name__ == "__main__":
main()
After many attempts I have actually made it wrote a working code in Python. "Working" on its own because when using other Enigma's emulators in order to check the encryption, for some reason the settings don't match at all. Rotor order, rotor's settings and starting positions do not encrypt/decrypt correctly against other Enigma machine simulators. Again, it does work when you encrypt/decrypt using its own messages. I know there are many other Enigma machine's written in Python, but I wanted to see MS Copilot to write one by itself.
So I am leaving the code here to see if any expert Python programmer can figure out why this code does not work when using other Enigma Machine simulators.
The code starts below this line:
import random
class Rotor:
def __init__(self, mappings, notch, ring_setting=0):
self.mappings = mappings
self.notch = notch
self.ring_setting = ring_setting
self.position = 0
def encode_forward(self, char):
index = (ord(char) - ord('A') + self.position - self.ring_setting) % 26
return chr((ord(self.mappings[index]) - ord('A') - self.position + self.ring_setting) % 26 + ord('A'))
def encode_backward(self, char):
index = (self.mappings.index(chr((ord(char) - ord('A') + self.position - self.ring_setting) % 26 + ord('A'))) - self.position + self.ring_setting) % 26
return chr(index + ord('A'))
def rotate(self):
self.position = (self.position + 1) % 26
return self.position == self.notch
def set_position(self, position):
self.position = position
class Reflector:
def __init__(self, mappings, name):
self.mappings = mappings
self.name = name
def reflect(self, char):
return self.mappings[ord(char) - ord('A')]
class Plugboard:
def __init__(self, connections):
self.connections = connections
def swap(self, char):
return self.connections.get(char, char)
class EnigmaMachine:
def __init__(self, rotors, reflector, plugboard):
self.rotors = rotors
self.reflector = reflector
self.plugboard = plugboard
self.initial_positions = [rotor.position for rotor in rotors]
def encode(self, message):
encoded_message = ''
for char in message:
if char.isalpha():
char = char.upper()
char = self.plugboard.swap(char)
for rotor in self.rotors:
char = rotor.encode_forward(char)
char = self.reflector.reflect(char)
for rotor in reversed(self.rotors):
char = rotor.encode_backward(char)
char = self.plugboard.swap(char)
encoded_message += char
for rotor in self.rotors:
if not rotor.rotate():
break
else:
encoded_message += char
return encoded_message
def reset_rotors(self):
for rotor, position in zip(self.rotors, self.initial_positions):
rotor.set_position(position)
def get_rotor_by_number(number, rotors):
return rotors[number - 1]
def get_random_position():
return random.randint(0, 25)
def get_random_plugboard():
letters = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
random.shuffle(letters)
connections = {}
for i in range(0, len(letters), 2):
connections[letters[i]] = letters[i+1]
connections[letters[i+1]] = letters[i]
return Plugboard(connections)
def get_specific_plugboard():
connections = {}
while True:
pair = input("Enter plugboard pair (e.g., AB) or press Enter to finish: ").strip().upper()
if not pair:
break
if len(pair) == 2 and pair[0] != pair[1]:
connections[pair[0]] = pair[1]
connections[pair[1]] = pair[0]
else:
print("Invalid pair. Please enter two different letters.")
return Plugboard(connections)
def format_output(message, group_size):
return ' '.join([message[i:i+group_size] for i in range(0, len(message), group_size)])
def display_configuration(rotors, reflector, plugboard):
roman_numerals = ["I", "II", "III", "IV", "V", "VI", "VII", "VIII", "Beta", "Gamma"]
rotor_order = '-'.join([roman_numerals[rotors.index(rotor)] for rotor in rotors])
rotor_settings = '-'.join([f"{rotor.ring_setting + 1}" for rotor in rotors])
rotor_positions = '-'.join([chr(rotor.position + ord('A')) for rotor in rotors])
plugboard_info = ', '.join([f"{k}{v}" for k, v in plugboard.connections.items() if k < v])
config = [
f"Rotors Order: {rotor_order}",
f"Rotors Settings: {rotor_settings}",
f"Rotors Starting Positions: {rotor_positions}",
f"Reflector: {reflector.name}",
f"Plugboards: {plugboard_info if plugboard_info else 'None'}"
]
print("\nCurrent Enigma Configuration:")
print("\n".join(config))
def main():
rotors_m3 = [
Rotor("EKMFLGDQVZNTOWYHXUSPAIBRCJ", 16), # Rotor I
Rotor("AJDKSIRUXBLHWTMCQGZNPYFVOE", 4), # Rotor II
Rotor("BDFHJLCPRTXVZNYEIWGAKMUSQO", 21), # Rotor III
Rotor("ESOVPZJAYQUIRHXLNFTGKDCMWB", 9), # Rotor IV
Rotor("VZBRGITYUPSDNHLXAWMJQOFECK", 25), # Rotor V
Rotor("JPGVOUMFYQBENHZRDKASXLICTW", 12), # Rotor VI
Rotor("NZJHGRCXMYSWBOUFAIVLPEKQDT", 12), # Rotor VII
Rotor("FKQHTLXOCBJSPDZRAMEWNIUYGV", 12) # Rotor VIII
]
rotors_m4 = rotors_m3 + [
Rotor("LEYJVCNIXWPBQMDRTAKZGFUHOS", 0), # Rotor Beta
Rotor("FSOKANUERHMBTIYCWLQPZXVGJD", 0) # Rotor Gamma
]
reflector_b = Reflector("YRUHQSLDPXNGOKMIEBFZCWVJAT", "B")
reflector_c = Reflector("FVPJIAOYEDRZXWGCTKUQSBNMHL", "C")
while True:
machine_type = input("Select Enigma Machine Type (1 for M3, 2 for M4): ").strip()
if machine_type == "1":
num_rotors = 3
rotors = rotors_m3
elif machine_type == "2":
num_rotors = 4
rotors = rotors_m4
else:
print("Invalid machine type selected.")
continue
config_choice = input("Select Configuration (1 for Random, 2 for Specific): ").strip()
selected_rotors = []
if config_choice == "1":
selected_rotors = random.sample(rotors, num_rotors)
for rotor in selected_rotors:
rotor.position = get_random_position()
rotor.ring_setting = get_random_position()
reflector = random.choice([reflector_b, reflector_c])
plugboard = get_random_plugboard()
elif config_choice == "2":
for i in range(num_rotors):
rotor_number = int(input(f"Select Rotor {i+1} (1-8 for M3, 1-10 for M4): "))
rotor = get_rotor_by_number(rotor_number, rotors)
start_position = input(f"Enter starting position for Rotor {i+1} (A-Z): ").strip().upper()
rotor.position = ord(start_position) - ord('A')
ring_setting = int(input(f"Enter ring setting for Rotor {i+1} (1-26): ")) - 1
rotor.ring_setting = ring_setting
selected_rotors.append(rotor)
reflector_choice = input("Select Reflector (B/C): ").strip().upper()
if reflector_choice == "B":
reflector = reflector_b
elif reflector_choice == "C":
reflector = reflector_c
else:
print("Invalid reflector selected.")
continue
plugboard = get_specific_plugboard()
else:
print("Invalid configuration selected.")
continue
display_configuration(selected_rotors, reflector, plugboard)
group_size = int(input("Enter group size for output (4 or 5): "))
if group_size not in [4, 5]:
print("Invalid group size selected.")
continue
enigma = EnigmaMachine(selected_rotors, reflector, plugboard)
while True:
print("\nChoose an action:")
print("1. Encode")
print("2. Decode")
print("3. Change Machine")
print("4. Change Configuration")
action = input("Enter the number of your choice: ").strip()
if action == "1" or action == "2":
message = input("Enter message to encode/decode: ")
enigma.reset_rotors() # Reset rotors to initial positions before encoding/decoding
encoded_message = enigma.encode(message)
formatted_message = format_output(encoded_message, group_size)
print(f"Encoded/Decoded Message: {formatted_message}")
elif action == "3":
break
elif action == "4":
break
else:
print("Invalid action selected.")
if action == "3" or action == "4":
continue
if __name__ == "__main__":
main()