# =================================================================== # test the enigma machine simulation with and without rotor advance # =================================================================== # the user can advance the rotors with the "advance" command. # ------------------------------------------------------------------- # Note: because this is software, we do some special "stuff" with # characters not in the enigma machine's alphabet [A-Z]. see the # code/comments. # =================================================================== from EnigmaConfig import EnigmaConfig from EnigmaRotor import EnigmaRotor import EnigmaUtils import user_interface as ui import copy import re import sys class EnigmaMachine: # --------------------------------------------------------------- # ---- initialize object # --------------------------------------------------------------- def __init__(self,configfile): # ---- enigma machine configuration self.config = EnigmaConfig(configfile) # ---- enigma machine alphabet self.abc = self.config.abc self.abcidx = self.config.abcidx self.abclen = self.config.abclen self.abcunk = self.config.abcunk self.rotorkeys = self.config.rotorkeys # ---- left rotor, middle rotor, right rotor, # ---- reflector, plugboard self.lr = EnigmaRotor(self.config, self.config.default_rotor_names[0], self.config.default_rotor_starts[0]) self.mr = EnigmaRotor(self.config, self.config.default_rotor_names[1], self.config.default_rotor_starts[1]) self.rr = EnigmaRotor(self.config, self.config.default_rotor_names[2], self.config.default_rotor_starts[2]) self.rf = copy.deepcopy(self.config.reflst) self.pg = copy.deepcopy(self.config.pglst) # ---- automatic advance rotors self.auto_advance_rotors = True # ---- substitution debug messages self.substitution_debug = False # --------------------------------------------------------------- # ---- display internal state of enigma machine # --------------------------------------------------------------- def display_internal_state(self): print() print(' -- Left Rotor --- ' + '-- Middle Rotor - -- Right Rotor --') print('plugboard reflector ' + 'l-to-r r-to-l l-to-r r-to-l l-to-r r-to-l') print('--------- --------- -------- -------- ' + '-------- -------- -------- --------') for i in range(self.abclen): print(f'{i:>2} -> {self.pg[i]:<2} ' + f'{i:>2} -> {self.rf[i]:<2} ' + f'{i:>2} -> {self.lr.ltor[i]:<2} ' + f'{self.lr.rtol[i]:>2} <- {i:<2} ' + f'{i:>2} -> {self.mr.ltor[i]:<2} ' + f'{self.mr.rtol[i]:>2} <- {i:<2} ' + f'{i:>2} -> {self.rr.ltor[i]:<2} ' + f'{self.rr.rtol[i]:>2} <- {i:<2}') print(f'---- auto advance rotors ({self.auto_advance_rotors})') print(f'---- right start {self.rr.rotor_start:<2} ' + f'rotor {self.rr.rotor_name:<4} ' + f'count {self.rr.rotor_count}') print(f'---- middle start {self.mr.rotor_start:<2} ' + f'rotor {self.mr.rotor_name:<4} ' + f'count {self.mr.rotor_count}') print(f'--- left start {self.lr.rotor_start:<2} ' + f'rotor {self.lr.rotor_name:<4} ' + f'count {self.lr.rotor_count}') return # --------------------------------------------------------------- # ---- display rotor stats of enigma machine # --------------------------------------------------------------- def display_rotor_stats(self): print() print(f'auto advance rotors ({self.auto_advance_rotors})') print(f'right start {self.rr.rotor_start:<2} ' + f'rotor {self.rr.rotor_name:<4} ' + f'count {self.rr.rotor_count}') print(f'middle start {self.mr.rotor_start:<2} ' + f'rotor {self.mr.rotor_name:<4} ' + f'count {self.mr.rotor_count}') print(f'left start {self.lr.rotor_start:<2} ' + f'rotor {self.lr.rotor_name:<4} ' + f'count {self.lr.rotor_count}') return # --------------------------------------------------------------- # ---- convert character to index # --------------------------------------------------------------- def char_to_idx(self,c): if c in self.abc: for i in range(self.abclen): if self.abc[i] == c: return i return -1 # --------------------------------------------------------------- # ---- convert index to character # --------------------------------------------------------------- def idx_to_char(self,idx): return self.abc[idx] # --------------------------------------------------------------- # ---- advance the rotors one position # ---- advance each rotor until it makes a complete revolution # ---- then start over again # ---- note: tf is a true/false flag # ---- True have completed a full rotation # ---- False have not completed a complete full rotation # --------------------------------------------------------------- def advance_rotors(self): tf = self.advance(self.rr) # right rotor if tf: tf = self.advance(self.mr) # middle rotor if tf: tf = self.advance(self.lr) # left rotor return # --------------------------------------------------------------- # ---- advance a rotor # ---- return: # ---- True have completed a full rotation # ---- False have not completed a complete full rotation # --------------------------------------------------------------- def advance(self,r): # ---- advance rotor's rtol list rtollen = len(r.rtol) tmp = r.rtol[0] for i in range(rtollen-1): r.rtol[i] = (r.rtol[i+1] - 1) % rtollen r.rtol[-1] = (tmp-1) % rtollen # ---- sync rotor's ltor list with its rotl list for i in range(rtollen): r.ltor[r.rtol[i]] = i # ---- check for a complete rotation of the rotor # ---- (end of list) r.rotor_count += 1 if not r.rotor_count < rtollen: r.rotor_count = 0 return True return False # --------------------------------------------------------------- # ---- reset the internal state to the initial configuration # --------------------------------------------------------------- def reset(self): self.lr = EnigmaRotor(self.config, self.config.default_rotor_names[0], self.config.default_rotor_starts[0]) self.mr = EnigmaRotor(self.config, self.config.default_rotor_names[1], self.config.default_rotor_starts[1]) self.rr = EnigmaRotor(self.config, self.config.default_rotor_names[2], self.config.default_rotor_starts[2]) self.lr.rotor_count = 0 self.mr.rotor_count = 0 self.rr.rotor_count = 0 return # --------------------------------------------------------------- # ---- set rotor configuration # ---- the initial rotor configuration is still available # ---- using reset_initial_state function # --------------------------------------------------------------- def set(self,names,starts): # --- verify input lists if len(names) != 3 or len(starts) != 3: print() print('internal error - bad set rotor comfiguration ' + 'names or starts list size') print(f'({names}) ({starts})') print() sys.exit() for i in range(3): if names[i] not in self.rotorkeys: print() print('internal error - bad set rotor comfiguration ' + 'rotor name') print(f'({names})') print() sys.exit() for s in range(3): if s< 0 or s >self.config.abclen-1: print() print('internal error - bad set rotor comfiguration ' + 'start position') print(f'({starts})') print() sys.exit() # ---- set rotor configuration self.lr = EnigmaRotor(self.config,names[0],starts[0]) self.mr = EnigmaRotor(self.config,names[1],starts[1]) self.rr = EnigmaRotor(self.config,names[2],starts[2]) self.lr.rotor_count = 0 self.mr.rotor_count = 0 self.rr.rotor_count = 0 return # --------------------------------------------------------------- # ---- change auto advance # --------------------------------------------------------------- def auto_advance_on(self): self.auto_advance_rotors = True return True def auto_advance_off(self): self.auto_advance_rotors = False return False def auto_advance_state(self): return auto_advance_rotors # --------------------------------------------------------------- # ---- change substitution debug # --------------------------------------------------------------- def sub_debug_on(self): self.substitution_debug = True return True def sub_debug_off(self): self.substitution_debug = False return False def sub_debug_state(self): return self.substitution_debug # --------------------------------------------------------------- # ---- substitute character # --------------------------------------------------------------- def substitution(self,idx): idx1 = self.pg[idx] # plugboard idx2 = self.rr.rtol_substitution(idx1) idx3 = self.mr.rtol_substitution(idx2) idx4 = self.lr.rtol_substitution(idx3) idx5 = self.rf[idx4] # reflector idx6 = self.lr.ltor_substitution(idx5) idx7 = self.mr.ltor_substitution(idx6) idx8 = self.rr.ltor_substitution(idx7) idx9 = self.pg[idx8] # plugboard if self.substitution_debug: print(f'{idx} --> ',end='') print(f'{idx1} --> ',end='') print(f'{idx2} --> ',end='') print(f'{idx3} --> ',end='') print(f'{idx4} --> ',end='') print(f'{idx5} --> ',end='') print(f'{idx6} --> ',end='') print(f'{idx7} --> ',end='') print(f'{idx8} --> ',end='') print(f'{idx9}') print(f'{self.abc[idx]} --> ',end='') print(f'{self.abc[idx1]} --> ',end='') print(f'{self.abc[idx2]} --> ',end='') print(f'{self.abc[idx3]} --> ',end='') print(f'{self.abc[idx4]} --> ',end='') print(f'{self.abc[idx5]} --> ',end='') print(f'{self.abc[idx6]} --> ',end='') print(f'{self.abc[idx7]} --> ',end='') print(f'{self.abc[idx8]} --> ',end='') print(f'{self.abc[idx9]}') print(f'[{idx} ({self.abc[idx]})] --> ' + f'[{idx8} ({self.abc[idx9]})]') if self.auto_advance_rotors: self.advance_rotors() return idx9