Still struggling with this code. The fact is, and I hate to admit it, my mind just doesn't work right anymore.
Ten years ago, I'd have done this in one evening. But it's slow now.
I'm going to post my latest attempt, so any help would be appreciated.
There are a lot of debugging printouts in this code that show exactly what's going on.
The code seems to work perfectly for the right rotor, but not for the middle or left.
The code should be pretty much the same for all three, except for the rotor advancement
before the first rotor encryption.
The setup for the test is (left to right):
Rings: 12, 4, 8
Roller Window: Q, U, O
PlugBoard: MZ, NS
Rotors: V, III, II
Reflector: B
Message: Enigma
-----------------------
The step by step operation should look like:
------------------------
Ten years ago, I'd have done this in one evening. But it's slow now.
I'm going to post my latest attempt, so any help would be appreciated.
There are a lot of debugging printouts in this code that show exactly what's going on.
The code seems to work perfectly for the right rotor, but not for the middle or left.
The code should be pretty much the same for all three, except for the rotor advancement
before the first rotor encryption.
The setup for the test is (left to right):
Rings: 12, 4, 8
Roller Window: Q, U, O
PlugBoard: MZ, NS
Rotors: V, III, II
Reflector: B
Message: Enigma
-----------------------
The step by step operation should look like:
------------------------
import EnigmaPaths import string import collections import json import copy class EncryptDammit: def __init__(self, plugboard_pairs, rotors, rings, rotor_window, reflector): self.rotors = rotors self.rings = rings self.rotor_window = rotor_window self.reflector = reflector self.epath = EnigmaPaths.EnigmaPaths() with self.epath.enigma_info.open() as f: self.init_data = json.load(f) # Set up ciphers for test, V, III, I (left to right) # Initial cipher shifted (ring setting - window letter index) = # 12 - abs((ord('O') - ord('A')))| = for right rotor test self.rotor_info = {} self.set_up_rotor_info() self.window_incr = [0, 0, 0] def set_up_rotor_info(self): # Set up ciphers for test, V, III, I (left to right) # Initial cipher shifted by self.rotor_info[side]['current_offset'] self.rotor_info['plugboard'] = {} self.rotor_info['plugboard']['cipher'] = collections.deque('ABCDEFGHIJKLZSOPQRNTUVWXYM') self.rotor_info['alpha'] = collections.deque(string.ascii_uppercase) self.rotor_info['reflector_cipher'] = collections.deque(self.init_data[f'reflector_{self.reflector}']) self.sides = {'left': 0, 'middle': 1, 'right': 2} # Keep three ciphers, one for ring offset, one for roller offset, one unmodified for side, side_idx in self.sides.items(): self.rotor_info[side] = {} self.rotor_info[side]['rotor'] = self.rotors[side_idx] self.rotor_info[side]['ring_offset'] = self.rings[side_idx] self.rotor_info[side]['roller_window'] = copy.deepcopy(self.rotor_window[side_idx]) self.rotor_info[side]['roller_window_offset'] = ord(self.rotor_info[side]['roller_window']) - ord('A') self.rotor_info[side]['alpha'] = collections.deque(string.ascii_uppercase) rotor_name = f"rotor{self.rotor_info[side]['rotor']}_info" self.rotor_info[side]['notch'] = self.init_data['rotor_info'][rotor_name]['notches'][0] self.rotor_info[side]['cipher'] = collections.deque(self.init_data['rotor_info'][rotor_name]['cipher']) # self.rotate_ring_cipher() # for side, side_idx in self.sides.items(): # self.rotate_roller_cipher(side, self.rotor_info[side]['roller_window_offset']) def rotate_ring_cipher(self): """ Rotate ring cipher (call once only) :return: None """ for side, side_idx in self.sides.items(): self.rotor_info[side]['ring_cipher'].rotate(-self.rotor_info[side]['ring_offset']) def next_letter(self, letter): """ Perpetual letter incrementor :param letter: :return: Next letter in alphabet, rotates to 'A' if input is 'Z' """ return chr(((ord(letter) - ord('A') + 1) % 26) + ord('A')) def incr_roller(self, side): letter = self.rotor_info[side]['roller_window'] = self.next_letter(self.rotor_info[side]['roller_window']) self.rotor_info[side]['roller_window_offset'] = ord(self.rotor_info[side]['roller_window']) - ord('A') return letter def rotate_roller(self, side): letter = self.incr_roller(side) if letter == self.rotor_info[side]['notch']: if side == 'right': letter = self.incr_roller('middle') if letter == self.rotor_info['middle']['notch']: letter = self.incr_roller('left') elif side == 'middle': letter = self.incr_roller('left') # def update_roller_windows(self, side): # self.rotor_info[side]['roller_window'] = self.rotor_info[side]['roller_cipher'][0] # self.rotor_info[side]['roller_window_offset'] = ord(self.rotor_info[side]['roller_cipher'][0]) - ord('A') def get_alpha_index(self, letter): return ord(letter) - ord('A') def show_current_settings(self): pass print(f"\nPlugBoard cipher:........ {self.rotor_info['plugboard']['cipher']}") for side in ('left', 'middle', 'right'): print(f"\n{side} rotor:................ {self.rotor_info[side]['rotor']}") print(f"{side} ring offset:.......... {self.rotor_info[side]['ring_offset']}") print(f"{side} roller window:........ {self.rotor_info[side]['roller_window']}") print(f"{side} roller window offset:. {self.rotor_info[side]['roller_window_offset']}") print(f"{side}_alpha:................ {self.rotor_info[side]['alpha']}") print(f"{side}_cipher.......:........ {self.rotor_info[side]['cipher']}") print(f"{side}_notch:................ {self.rotor_info[side]['notch']}") print(f"\nreflector_cipher:............ {self.rotor_info['reflector_cipher']}") # print(f"self.current_{side}_offset:.. {self.rotor_info[side]['current_offset']}") def plugboard_encrypt(self, letter): idx = self.get_alpha_index(letter) return self.rotor_info['plugboard']['cipher'][idx] def rotor_encrypt(self, side, inchar): # Values needed print(f'\nrotor_encrypt - side: {side} input character: {inchar}') ring_offset = self.rotor_info[side]['ring_offset'] cipher = self.rotor_info[side]['cipher'] alpha_index = (self.get_alpha_index(inchar) + ring_offset) % 26 # Alpha_char serves no purpose except for test aid alpha_char = self.rotor_info[side]['alpha'][alpha_index] cipher_char = cipher[alpha_index] print(f'add rotor {self.sides[side]} offset: alpha_index: {alpha_index}, alpha_char: {alpha_char}') print(f'rotor {self.sides[side]} from right: cipher_char: {cipher_char}') cipher_char_idx = (self.get_alpha_index(cipher_char) - (ring_offset + 1)) % 26 outchar = cipher[cipher_char_idx] print(f'subtract rotor {self.sides[side]} offset: cipher_char_idx: {cipher_char_idx}, outchar: {outchar}') print(f'{outchar} <-- {side} ring encrypt <-- {inchar}') return outchar def encrypt_forward(self, letter): # run through patchboard pbchar = self.plugboard_encrypt(letter) print(f'\nplugboard --> {letter}') self.rotate_roller('right') rightchar = self.rotor_encrypt('right', pbchar) middlechar = self.rotor_encrypt('middle', rightchar) leftchar = self.rotor_encrypt('left', middlechar) # iidx = self.rotor_info[side]['ring_offset'] + (self.get_alpha_index(tchar) % 26) # output_letter = self.rotor_info[side]['cipher'][iidx] # O # print(f'right output_letter: {output_letter}') # out_ltr_index = self.get_alpha_index(output_letter) # print(f'out_ltr_index: {out_ltr_index}') # rightchar = self.rotor_info[side]['cipher'][self.get_alpha_index(output_letter) - # (self.rotor_info[side]['ring_offset'] + 1)] # side = 'middle' # ochar = rightchar # iidx = self.rotor_info[side]['ring_offset'] + (self.get_alpha_index(rightchar) % 26) # output_letter = self.rotor_info[side]['cipher'][iidx] # O # middlechar = self.rotor_info[side]['cipher'][((self.get_alpha_index(output_letter) - # (self.rotor_info[side]['ring_offset'] + 1)) % 26)] # print(f'\n{middlechar} <-- middle ring encrypt <-- {ochar}') # # side = 'left' # ochar = middlechar # iidx = self.rotor_info[side]['ring_offset'] + (self.get_alpha_index(middlechar) % 26) # output_letter = self.rotor_info[side]['cipher'][iidx] # O # idx1 = self.get_alpha_index(output_letter) # stepn = ((self.rotor_info[side]['ring_offset'] + 1) % 26) # leftchar = self.rotor_info[side]['cipher'][(idx1 - stepn) % 26] # print(f'iidx: {iidx}, output_letter: {output_letter}, idx1: {idx1}, stepn: {stepn}') # leftchar = self.rotor_info[side]['cipher'][((self.get_alpha_index(output_letter) - # (self.rotor_info[side]['ring_offset'] + 1)) % 26)] # print(f'\n{leftchar} <-- middle ring encrypt <-- {ochar}') # Should have a T at this point # UKW W.3 W.2 W.1 ETW STK # ┌- t <-- s <-- o <-- e <-- e <-- e (REIN) # └> m --> y --> q --> g --> g --> g (RAUS) return 'A' def encrypt_message(self, message): for letter in message: self.show_current_settings() outchar = self.encrypt_forward(letter) if __name__ == '__main__': # message = 'ENIGMA' message = 'E' az = EncryptDammit(plugboard_pairs=[['M', 'Z'], ['N', 'S']], rotors=[5, 3, 1], rings=[12, 4, 8], rotor_window=['Q', 'U', 'O'], reflector='B') # az.show_current_settings() az.encrypt_message(message)EnigmaPaths.py
from pathlib import Path class EnigmaPaths: def __init__(self): self.homepath = Path('.') self.datapath = self.homepath / 'data' self.imagepath = self.homepath / 'image' self.enigma_info = self.datapath / 'enigma_info.json' self.color_info = self.datapath / 'color_info.json' self.patchboard_image = self.imagepath / 'patchboard.ppm'
Attached Files
enigma_info.json (Size: 908 bytes / Downloads: 205)