# Pocket Enigma ![centre-aligned Pocket Engima](pocket-enigma-small.jpg) Stateful cipher --- # Pocket Enigma Emulates the Enigma machine from WWII Mechanical cipher machine * Substitution cipher * Substitution changes with every letter Ciphering method: advance the wheel, then follow the lines to encipher the letter ## Stateful enciphering The cipher depends on the position of the wheel We need to encapsulate that state Objects have state --- # The PocketEnigma object What do we want it to do? What data should the object hold? --- # The PocketEnigma object What do we want it to do? * Initialise with the appropriate wheel (and possible starting position) * Spin the wheel to a given position * Advance the wheel one position * Look up a letter given the wheel position * Encipher a letter (advance the wheel then look up the letter) * Encipher a message (optionally give the key) * Make aliases for deciphering (same as enciphering) * Accept user-defined wheels * ...and validate them What data should it hold? * A description of the wheel being used * The current position of the wheel --- # Data structures What's a convenient representation of the wheel 1. for the object to use internally 2. for a person to use to describe the wheel They may not be the same, and we'll have to translate between them --- # Data structures ### Internal use: list of transpositions. ```python [2, 3, 0, 1, 22, 8, 15, 12, 5, ... ``` so position 0 ('a') swaps with position 2 ('c'), position 3 ('d') swaps with position 1 ('b'), and so on. * This will be a nightmare to enter correctly ### Exernal use: list of pairs ```python [('a', 'c'), ('b', 'd'), ...] ``` Easier to enter * Need to validate the human-entered list, to check it's valid --- # Validating the wheel description What tests? --- # Validating the wheel specification What tests? * 13 elements... * ...each a pair... * ...and 26 letters mentioned overall --- # Making the PocketEnigma class ```python class PocketEnigma(object): def __init__(self, wheel=1, position='a'): self.wheel1 = [('a', 'z'), ('b', 'e'), ('c', 'x'), ('d', 'k'), ('f', 'h'), ('g', 'j'), ('i', 'm'), ('l', 'r'), ('n', 'o'), ('p', 'v'), ('q', 't'), ('s', 'u'), ('w', 'y')] self.wheel2 = [('a', 'c'), ('b', 'd'), ('e', 'w'), ('f', 'i'), ('g', 'p'), ('h', 'm'), ('j', 'k'), ('l', 'n'), ('o', 'q'), ('r', 'z'), ('s', 'u'), ('t', 'v'), ('x', 'y')] # Rest of initialisation code here def make_wheel_map(self, wheel_spec): ... self.wheel_map = ... ... def validate_wheel_spec(self, wheel_spec): if len(wheel_spec) != 13: raise ValueError("Wheel specification has {} pairs, requires 13". format(len(wheel_spec))) ... ``` --- # Looking up the enciphered version of a letter *Not* advancing the wheel before Keep `self.position` to record where the wheel is * `__init__` can be passed a letter, but internally it's a number But the wheel map only works if the wheel arrow is pointing at 'a' Idea: 1. Rotate the source letter back `position` spaces 2. Do the lookup 3. Rotate the destination letter forward `position` spaces (all mod 26)