{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Enigma machine\n", "\n", "This is an implementation of an Enigma machine in Python. See below for links that describe the Enigma machine and how it was used.\n", "\n", "The Enigma machine has a bunch of components which can be swapped or modified to change its behaviour. The components are:\n", "\n", "* The plugboard\n", "* The wheels (three chosen from a set of five)\n", "* The reflector (two available, though generally not swapped)\n", "\n", "The plugboard and wheel selection were changed every day. The wheel orientation was changed every message.\n", "\n", "## Design sketch\n", "Each of the components can be thought of as a \"letter transformer\", which take a letter and input and give a different letter as output. From a given setup (plugboard setting or wheel orientation), these transformations are deterministic: if nothing moves, the same letter input will give the same letter output. Depending on the component, forward and backward transformations can be different. For instance, the wheel I converts `a` to `e` forward, and `a` to `u` backward.\n", "\n", "This means we can take an object oriented approach to building the Enigma machine. The machine itself is a collection (aggregation) of components. Each component keeps track of its current state. \n", "\n", "The components have an inheritance hierarchy.\n", "\n", "* [LetterTransformer](#lettertransformer)\n", " * [Plugobard](#plugboard)\n", " * [Reflector](#reflector)\n", " * [SimpleWheel](#simplewheel)\n", " * [Wheel](#wheel)\n", "\n", "The `LetterTransformer` is the base class and defines the basic operations all the transformers apply. \n", "\n", "A `Plugboard` is a type of `LetterTransformer` that swaps only some letters, and acts the same both forward and backward. The `Reflector` acts like a `Plugboard` but with 13 pairs of swaps.\n", "\n", "A `SimpleWheel` has different forward and backward transforms, and also rotates. A `Wheel` is the same, but the indicator \"ring\" around the outside can be rotated around the core. This ring of a `Wheel` has a notch that can control when other `SimpleWheel`s and `Wheel`s are rotated in the Enigma.\n", "\n", "Note that all the logic of when the wheels rotate is controlled by the Enigma machine.\n", "\n", "* [Engima](#enigma)\n", "* [Testing Enigma](#testingenigma)\n", "\n", "### Implmentation note\n", "The normal way to define a class in Python is to define all the methods, class variables, and instance variables at the same time. However, that makes it difficult to place the discussion of the various methods and what they do near the definitions. \n", "\n", "This notebook takes a variant approach of defining the base class, then defining methods in separate cells and adding them to the class with the `setattr()` procedure.\n", "\n", "\n", "## See also\n", "* Specification from [Codes and Ciphers](http://www.codesandciphers.org.uk/enigma/rotorspec.htm) page.\n", "\n", "* Example Enigma machines from [Louise Dale](http://enigma.louisedade.co.uk/enigma.html) (full simulation) and [EnigmaCo](http://enigmaco.de/enigma/enigma.html) (good animation of the wheels, but no ring settings).\n", "\n", "* There's also the nice Enigma simulator for Android by [Franklin Heath](https://franklinheath.co.uk/2012/02/04/our-first-app-published-enigma-simulator/), available on the [Google Play store](https://play.google.com/store/apps/details?id=uk.co.franklinheath.enigmasim&hl=en_GB).\n", "\n", "* Enigma wiring from the [Crypto Museum](http://www.cryptomuseum.com/crypto/enigma/wiring.htm)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, some general-purpose and utility imports. \n", "\n", "`pos` and `unpos` convert between letters and numbers (in range 0-25 inclusive)." ] }, { "cell_type": "code", "execution_count": 108, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import string\n", "import collections\n", "\n", "cat = ''.join\n", "\n", "def clean(text): return cat(l.lower() for l in text if l in string.ascii_letters)\n", "\n", "def pos(letter): \n", " if letter in string.ascii_lowercase:\n", " return ord(letter) - ord('a')\n", " elif letter in string.ascii_uppercase:\n", " return ord(letter) - ord('A')\n", " else:\n", " return ''\n", " \n", "def unpos(number): return chr(number % 26 + ord('a'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The wheel specifications show what positions `a` to `z` (ignoring the ring) go to. For instance, Wheel 1 converts `a` to `e` forward, and `a` to `u` backward. The notch positions show where the wheel advance notches are on the wheel rings. The reflector specifications show the reflected pairs." ] }, { "cell_type": "code", "execution_count": 109, "metadata": { "collapsed": true }, "outputs": [], "source": [ "wheel_i_spec = 'ekmflgdqvzntowyhxuspaibrcj'\n", "wheel_ii_spec = 'ajdksiruxblhwtmcqgznpyfvoe'\n", "wheel_iii_spec = 'bdfhjlcprtxvznyeiwgakmusqo'\n", "wheel_iv_spec = 'esovpzjayquirhxlnftgkdcmwb'\n", "wheel_v_spec = 'vzbrgityupsdnhlxawmjqofeck'\n", "wheel_vi_spec = 'jpgvoumfyqbenhzrdkasxlictw'\n", "wheel_vii_spec = 'nzjhgrcxmyswboufaivlpekqdt'\n", "wheel_viii_spec = 'fkqhtlxocbjspdzramewniuygv'\n", "beta_wheel_spec = 'leyjvcnixwpbqmdrtakzgfuhos'\n", "gamma_wheel_spec = 'fsokanuerhmbtiycwlqpzxvgjd'\n", "\n", "wheel_i_notches = ['q']\n", "wheel_ii_notches = ['e']\n", "wheel_iii_notches = ['v']\n", "wheel_iv_notches = ['j']\n", "wheel_v_notches = ['z']\n", "wheel_vi_notches = ['z', 'm']\n", "wheel_vii_notches = ['z', 'm']\n", "wheel_viii_notches = ['z', 'm']\n", "\n", "reflector_b_spec = 'ay br cu dh eq fs gl ip jx kn mo tz vw'\n", "reflector_c_spec = 'af bv cp dj ei go hy kr lz mx nw tq su'" ] }, { "cell_type": "code", "execution_count": 110, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# class LetterTransformer(object):\n", "# def __init__(self, specification, raw_transform=False):\n", "# if raw_transform:\n", "# transform = specification\n", "# else:\n", "# transform = self.parse_specification(specification)\n", "# self.validate_transform(transform)\n", "# self.make_transform_map(transform)\n", " \n", "# def parse_specification(self, specification):\n", "# return list(zip(string.ascii_lowercase, clean(specification)))\n", "# # return specification\n", " \n", "# def validate_transform(self, transform):\n", "# \"\"\"A set of pairs, of from-to\"\"\"\n", "# if len(transform) != 26:\n", "# raise ValueError(\"Transform specification has {} pairs, requires 26\".\n", "# format(len(transform)))\n", "# for p in transform:\n", "# if len(p) != 2:\n", "# raise ValueError(\"Not all mappings in transform \"\n", "# \"have two elements\")\n", "# if len(set([p[0] for p in transform])) != 26:\n", "# raise ValueError(\"Transform specification must list 26 origin letters\") \n", "# if len(set([p[1] for p in transform])) != 26:\n", "# raise ValueError(\"Transform specification must list 26 destination letters\") \n", "\n", "# def make_empty_transform(self):\n", "# self.forward_map = [0] * 26\n", "# self.backward_map = [0] * 26\n", " \n", "# def make_transform_map(self, transform):\n", "# self.make_empty_transform()\n", "# for p in transform:\n", "# self.forward_map[pos(p[0])] = pos(p[1])\n", "# self.backward_map[pos(p[1])] = pos(p[0])\n", "# return self.forward_map, self.backward_map\n", " \n", "# def forward(self, letter):\n", "# if letter in string.ascii_lowercase:\n", "# return unpos(self.forward_map[pos(letter)])\n", "# else:\n", "# return ''\n", " \n", "# def backward(self, letter):\n", "# if letter in string.ascii_lowercase:\n", "# return unpos(self.backward_map[pos(letter)])\n", "# else:\n", "# return ''" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "# Letter transformer\n", "[Top](#top)\n", "\n", "A generic transformer of letters. All components in the Enigma are based on this. \n", "\n", "The transformer has two directions, `forward` and `backward`. In each direction, a given letter is transformed into a different letter. Both transformations are general permutations of the alphabet (i.e. each letter goes to one and only one new letter). There is no general requirement for the `forward` and `backward` transformations to have any particular relationship to each other (even though most do in the Enigma machine).\n", "\n", "When created, it must be given the transformation which should be applied. A raw transform is a sequence of letter pairs, such that `p[0]` is transformed to `p[1]` forwards, and `p[1]` goes to `p[0]` backwards.\n", "\n", "If the transform is not raw, it's assumed that the specification is a sequence of the `p[1]`s, and the standard alphabet gives the `p[0]`s." ] }, { "cell_type": "code", "execution_count": 111, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class LetterTransformer(object):\n", " def __init__(self, specification, raw_transform=False):\n", " if raw_transform:\n", " transform = specification\n", " else:\n", " transform = self.parse_specification(specification)\n", " self.validate_transform(transform)\n", " self.make_transform_map(transform)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Parse a specification: convert a string of destination letters into a list of pairs. " ] }, { "cell_type": "code", "execution_count": 112, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def parse_specification(self, specification):\n", " return list(zip(string.ascii_lowercase, clean(specification)))\n", " # return specification\n", "\n", "setattr(LetterTransformer, \"parse_specification\", parse_specification)" ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('a', 'e'),\n", " ('b', 'k'),\n", " ('c', 'm'),\n", " ('d', 'f'),\n", " ('e', 'l'),\n", " ('f', 'g'),\n", " ('g', 'd'),\n", " ('h', 'q'),\n", " ('i', 'v'),\n", " ('j', 'z'),\n", " ('k', 'n'),\n", " ('l', 't'),\n", " ('m', 'o'),\n", " ('n', 'w'),\n", " ('o', 'y'),\n", " ('p', 'h'),\n", " ('q', 'x'),\n", " ('r', 'u'),\n", " ('s', 's'),\n", " ('t', 'p'),\n", " ('u', 'a'),\n", " ('v', 'i'),\n", " ('w', 'b'),\n", " ('x', 'r'),\n", " ('y', 'c'),\n", " ('z', 'j')]" ] }, "execution_count": 113, "metadata": {}, "output_type": "execute_result" } ], "source": [ "parse_specification(None, wheel_i_spec)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Checks that a transform is valid." ] }, { "cell_type": "code", "execution_count": 114, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def validate_transform(self, transform):\n", " \"\"\"A set of pairs, of from-to\"\"\"\n", " if len(transform) != 26:\n", " raise ValueError(\"Transform specification has {} pairs, requires 26\".\n", " format(len(transform)))\n", " for p in transform:\n", " if len(p) != 2:\n", " raise ValueError(\"Not all mappings in transform \"\n", " \"have two elements\")\n", " if len(set([p[0] for p in transform])) != 26:\n", " raise ValueError(\"Transform specification must list 26 origin letters\") \n", " if len(set([p[1] for p in transform])) != 26:\n", " raise ValueError(\"Transform specification must list 26 destination letters\") \n", "\n", "setattr(LetterTransformer, \"validate_transform\", validate_transform) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The empty transform maps each letter to itself, forward and backward. A useful starting point for creating the maps needed.\n", "\n", "The forward and backward maps are `list`s of numbers (rather than `dict`s of letters to letters) to make the calculations easier when it comes to the wheels, and wheels with turnable indicator rings." ] }, { "cell_type": "code", "execution_count": 115, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# def make_empty_transform(self):\n", "# self.forward_map = [0] * 26\n", "# self.backward_map = [0] * 26\n", "\n", "# setattr(LetterTransformer, \"make_empty_transform\", make_empty_transform) " ] }, { "cell_type": "code", "execution_count": 116, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def make_empty_transform(self):\n", " self.forward_map = list(range(26))\n", " self.backward_map = list(range(26))\n", "\n", "setattr(LetterTransformer, \"make_empty_transform\", make_empty_transform) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Make the transform. Starting from an empty transform, mutate it to include the swaps. Note that the forward and backward swaps are stored separately. " ] }, { "cell_type": "code", "execution_count": 117, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def make_transform_map(self, transform):\n", " self.make_empty_transform()\n", " for p in transform:\n", " self.forward_map[pos(p[0])] = pos(p[1])\n", " self.backward_map[pos(p[1])] = pos(p[0])\n", " return self.forward_map, self.backward_map\n", "\n", "setattr(LetterTransformer, \"make_transform_map\", make_transform_map)" ] }, { "cell_type": "code", "execution_count": 118, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def forward(self, letter):\n", " if letter in string.ascii_lowercase:\n", " return unpos(self.forward_map[pos(letter)])\n", " else:\n", " return ''\n", "\n", "def backward(self, letter):\n", " if letter in string.ascii_lowercase:\n", " return unpos(self.backward_map[pos(letter)])\n", " else:\n", " return ''\n", "\n", "setattr(LetterTransformer, \"forward\", forward)\n", "setattr(LetterTransformer, \"backward\", backward) " ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('z', 'a'),\n", " ('a', 'b'),\n", " ('b', 'c'),\n", " ('c', 'd'),\n", " ('d', 'e'),\n", " ('e', 'f'),\n", " ('f', 'g'),\n", " ('g', 'h'),\n", " ('h', 'i'),\n", " ('i', 'j'),\n", " ('j', 'k'),\n", " ('k', 'l'),\n", " ('l', 'm'),\n", " ('m', 'n'),\n", " ('n', 'o'),\n", " ('o', 'p'),\n", " ('p', 'q'),\n", " ('q', 'r'),\n", " ('r', 's'),\n", " ('s', 't'),\n", " ('t', 'u'),\n", " ('u', 'v'),\n", " ('v', 'w'),\n", " ('w', 'x'),\n", " ('x', 'y'),\n", " ('y', 'z')]" ] }, "execution_count": 119, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tmap = [('z', 'a')] + [(l, string.ascii_lowercase[i+1]) for i, l in enumerate(string.ascii_lowercase[:-1])]\n", "tmap" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'zyxwcabdefghijklmnopqrstuv'" ] }, "execution_count": 120, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))" ] }, { "cell_type": "code", "execution_count": 121, "metadata": { "collapsed": true, "scrolled": true }, "outputs": [], "source": [ "tmap2 = list(zip(string.ascii_lowercase, cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))))" ] }, { "cell_type": "code", "execution_count": 122, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "([1,\n", " 2,\n", " 3,\n", " 4,\n", " 5,\n", " 6,\n", " 7,\n", " 8,\n", " 9,\n", " 10,\n", " 11,\n", " 12,\n", " 13,\n", " 14,\n", " 15,\n", " 16,\n", " 17,\n", " 18,\n", " 19,\n", " 20,\n", " 21,\n", " 22,\n", " 23,\n", " 24,\n", " 25,\n", " 0],\n", " [25,\n", " 0,\n", " 1,\n", " 2,\n", " 3,\n", " 4,\n", " 5,\n", " 6,\n", " 7,\n", " 8,\n", " 9,\n", " 10,\n", " 11,\n", " 12,\n", " 13,\n", " 14,\n", " 15,\n", " 16,\n", " 17,\n", " 18,\n", " 19,\n", " 20,\n", " 21,\n", " 22,\n", " 23,\n", " 24])" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lt = LetterTransformer(tmap, raw_transform = True)\n", "assert(lt.forward_map == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0])\n", "assert(lt.backward_map == [25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24])\n", "lt.forward_map, lt.backward_map" ] }, { "cell_type": "code", "execution_count": 123, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "([25,\n", " 24,\n", " 23,\n", " 22,\n", " 2,\n", " 0,\n", " 1,\n", " 3,\n", " 4,\n", " 5,\n", " 6,\n", " 7,\n", " 8,\n", " 9,\n", " 10,\n", " 11,\n", " 12,\n", " 13,\n", " 14,\n", " 15,\n", " 16,\n", " 17,\n", " 18,\n", " 19,\n", " 20,\n", " 21],\n", " [5,\n", " 6,\n", " 4,\n", " 7,\n", " 8,\n", " 9,\n", " 10,\n", " 11,\n", " 12,\n", " 13,\n", " 14,\n", " 15,\n", " 16,\n", " 17,\n", " 18,\n", " 19,\n", " 20,\n", " 21,\n", " 22,\n", " 23,\n", " 24,\n", " 25,\n", " 3,\n", " 2,\n", " 1,\n", " 0])" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase)))\n", "assert(lt.forward_map == [25, 24, 23, 22, 2, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])\n", "assert(lt.backward_map == [5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 3, 2, 1, 0])\n", "assert(cat(lt.forward(l) for l in string.ascii_lowercase) == 'zyxwcabdefghijklmnopqrstuv')\n", "assert(cat(lt.backward(l) for l in string.ascii_lowercase) == 'fgehijklmnopqrstuvwxyzdcba')\n", "lt.forward_map, lt.backward_map" ] }, { "cell_type": "code", "execution_count": 124, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'zyxwcabdefghijklmnopqrstuv'" ] }, "execution_count": 124, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(lt.forward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'fgehijklmnopqrstuvwxyzdcba'" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(lt.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Plugboard\n", "[Top](#top)\n", "\n", "A `Plugboard` is a `LetterTransformer` that swaps some pairs of letters, and does the same swaps forward and backward." ] }, { "cell_type": "code", "execution_count": 126, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Plugboard(LetterTransformer):\n", " def parse_specification(self, specification):\n", " return [tuple(clean(p)) for p in specification.split()]\n", " \n", " def validate_transform(self, transform):\n", " \"\"\"A set of pairs, of from-to\"\"\"\n", " for p in transform:\n", " if len(p) != 2:\n", " raise ValueError(\"Not all mappings in transform\"\n", " \"have two elements\")\n", " \n", "# def make_empty_transform(self):\n", "# self.forward_map = list(range(26))\n", "# self.backward_map = list(range(26))\n", " \n", " def make_transform_map(self, transform):\n", " expanded_transform = transform + [tuple(reversed(p)) for p in transform]\n", " return super(Plugboard, self).make_transform_map(expanded_transform)" ] }, { "cell_type": "code", "execution_count": 127, "metadata": { "collapsed": true }, "outputs": [], "source": [ "pb = Plugboard([('a', 'z'), ('b', 'y')], raw_transform=True)" ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'zycdefghijklmnopqrstuvwxba'" ] }, "execution_count": 128, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(pb.forward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'zycdefghijklmnopqrstuvwxba'" ] }, "execution_count": 129, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(pb.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 130, "metadata": { "collapsed": true }, "outputs": [], "source": [ "pb = Plugboard('az by')" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('zycdefghijklmnopqrstuvwxba', 'zycdefghijklmnopqrstuvwxba')" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(pb.forward(l) for l in string.ascii_lowercase), cat(pb.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('ugcdypblnzkhmisfrqoxavwtej', 'ugcdypblnzkhmisfrqoxavwtej')" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper())\n", "assert(pb.forward_map == pb.backward_map)\n", "assert(pb.forward_map == [20, 6, 2, 3, 24, 15, 1, 11, 13, 25, 10, 7, 12, 8, 18, 5, 17, 16, 14, 23, 0, 21, 22, 19, 4, 9])\n", "assert(cat(pb.forward(l) for l in string.ascii_lowercase) == 'ugcdypblnzkhmisfrqoxavwtej')\n", "assert(cat(pb.backward(l) for l in string.ascii_lowercase) == 'ugcdypblnzkhmisfrqoxavwtej')\n", "cat(pb.forward(l) for l in string.ascii_lowercase), cat(pb.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Reflector\n", "[Top](#top)\n", "\n", "A `Reflector` is a `Plugboard` that takes exactly 13 pairs of letters to swap." ] }, { "cell_type": "code", "execution_count": 133, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Reflector(Plugboard):\n", " def validate_transform(self, transform):\n", " if len(transform) != 13:\n", " raise ValueError(\"Reflector specification has {} pairs, requires 13\".\n", " format(len(transform)))\n", " if len(set([p[0] for p in transform] + \n", " [p[1] for p in transform])) != 26:\n", " raise ValueError(\"Reflector specification does not contain 26 letters\")\n", " try:\n", " super(Reflector, self).validate_transform(transform)\n", " except ValueError as v:\n", " raise ValueError(\"Not all mappings in reflector have two elements\")" ] }, { "cell_type": "code", "execution_count": 134, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[('a', 'y'),\n", " ('b', 'r'),\n", " ('c', 'u'),\n", " ('d', 'h'),\n", " ('e', 'q'),\n", " ('f', 's'),\n", " ('g', 'l'),\n", " ('i', 'p'),\n", " ('j', 'x'),\n", " ('k', 'n'),\n", " ('m', 'o'),\n", " ('t', 'z'),\n", " ('v', 'w')]" ] }, "execution_count": 134, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# reflector_b_text = '(AY) (BR) (CU) (DH) (EQ) (FS) (GL) (IP) (JX) (KN) (MO) (TZ) (VW)'\n", "reflector_b_l = [tuple(clean(p)) for p in reflector_b_spec.split()]\n", "reflector_b_l" ] }, { "cell_type": "code", "execution_count": 135, "metadata": { "collapsed": true }, "outputs": [], "source": [ "reflector_b = Reflector(reflector_b_spec)\n", "assert(reflector_b.forward_map == reflector_b.backward_map)\n", "assert(reflector_b.forward_map == [24, 17, 20, 7, 16, 18, 11, 3, 15, 23, 13, 6, 14, 10, 12, 8, 4, 1, 5, 25, 2, 22, 21, 9, 0, 19])\n", "assert(cat(reflector_b.forward(l) for l in string.ascii_lowercase) == 'yruhqsldpxngokmiebfzcwvjat')\n", "assert(cat(reflector_b.backward(l) for l in string.ascii_lowercase) == 'yruhqsldpxngokmiebfzcwvjat')" ] }, { "cell_type": "code", "execution_count": 136, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'yruhqsldpxngokmiebfzcwvjat'" ] }, "execution_count": 136, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(reflector_b.forward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 137, "metadata": { "collapsed": true }, "outputs": [], "source": [ "reflector_c = Reflector(reflector_c_spec)" ] }, { "cell_type": "code", "execution_count": 138, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'fvpjiaoyedrzxwgctkuqsbnmhl'" ] }, "execution_count": 138, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(reflector_c.forward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## SimpleWheel\n", "[Top](#top)\n", "\n", "A `SimpleWheel` has different forward and backward maps, and also a position. The position is set with the `set_position` method (and initially in the creator), and the wheel can advance using the `advance` method. \n", "\n", "How the position is used is best explained with an example. The Enigma wheel 1, in the neutral position, transforms `a` to `e` (+4 letters) and `b` to `k` (+10 letters). When the wheel is in position `b` and an `a` in enciphered, it's the _second_ element of the map that's used, so `a` would be advanced 10 letters, to give `j`.\n", "\n", "This means that when using the letter transformation maps, you use the element in the map that's offset by the position of the wheel. When enciphering a `c`, you'd normally use transformation at position 2 in the map; if the wheel is in position 7, you'd instead use the transform at position 2 + 7 = 9 in the map.\n", "\n", "There are various modulus operators to keep the numbers in the requried range, meaning you can wrap around the map and around the wheel.\n", "\n", "Note the use of `__getattribute__` to give a more human-friendly version of the position without making it a method call. That allows you to write `wheel.position` and `wheel.position_l` and get the appropriate answers." ] }, { "cell_type": "code", "execution_count": 139, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class SimpleWheel(LetterTransformer):\n", " def __init__(self, transform, position='a', raw_transform=False):\n", " super(SimpleWheel, self).__init__(transform, raw_transform)\n", " self.set_position(position)\n", " \n", " def __getattribute__(self, name):\n", " if name=='position_l':\n", " return unpos(self.position)\n", " else:\n", " return object.__getattribute__(self, name) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set the wheel to a new position. Note that it expects a letter, not a number." ] }, { "cell_type": "code", "execution_count": 140, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def set_position(self, position):\n", " self.position = ord(position) - ord('a')\n", " \n", "setattr(SimpleWheel, 'set_position', set_position) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Advance the wheel one step. Note that advancing beyond position 25 moves back to 0." ] }, { "cell_type": "code", "execution_count": 141, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def advance(self):\n", " self.position = (self.position + 1) % 26\n", " return self.position\n", "\n", "setattr(SimpleWheel, 'advance', advance) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do the encipherment forward and backward. Note how the map element to use is affected by the wheel position, and how the modulus wraps that map element around the wheel if needed." ] }, { "cell_type": "code", "execution_count": 142, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def forward(self, letter):\n", " if letter in string.ascii_lowercase:\n", " return unpos((self.forward_map[(pos(letter) + self.position) % 26] - self.position))\n", " else:\n", " return ''\n", "\n", "def backward(self, letter):\n", " if letter in string.ascii_lowercase:\n", " return unpos((self.backward_map[(pos(letter) + self.position) % 26] - self.position))\n", " else:\n", " return ''\n", " \n", "setattr(SimpleWheel, 'forward', forward) \n", "setattr(SimpleWheel, 'backward', backward) " ] }, { "cell_type": "code", "execution_count": 143, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[('a', 'e'),\n", " ('b', 'k'),\n", " ('c', 'm'),\n", " ('d', 'f'),\n", " ('e', 'l'),\n", " ('f', 'g'),\n", " ('g', 'd'),\n", " ('h', 'q'),\n", " ('i', 'v'),\n", " ('j', 'z'),\n", " ('k', 'n'),\n", " ('l', 't'),\n", " ('m', 'o'),\n", " ('n', 'w'),\n", " ('o', 'y'),\n", " ('p', 'h'),\n", " ('q', 'x'),\n", " ('r', 'u'),\n", " ('s', 's'),\n", " ('t', 'p'),\n", " ('u', 'a'),\n", " ('v', 'i'),\n", " ('w', 'b'),\n", " ('x', 'r'),\n", " ('y', 'c'),\n", " ('z', 'j')]" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower()))\n", "rotor_1_transform" ] }, { "cell_type": "code", "execution_count": 144, "metadata": { "collapsed": true }, "outputs": [], "source": [ "rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower()))\n", "wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True)\n", "assert(cat(wheel_1.forward(l) for l in string.ascii_lowercase) == 'ekmflgdqvzntowyhxuspaibrcj')\n", "assert(cat(wheel_1.backward(l) for l in string.ascii_lowercase) == 'uwygadfpvzbeckmthxslrinqoj')" ] }, { "cell_type": "code", "execution_count": 145, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "[4,\n", " 10,\n", " 12,\n", " 5,\n", " 11,\n", " 6,\n", " 3,\n", " 16,\n", " 21,\n", " 25,\n", " 13,\n", " 19,\n", " 14,\n", " 22,\n", " 24,\n", " 7,\n", " 23,\n", " 20,\n", " 18,\n", " 15,\n", " 0,\n", " 8,\n", " 1,\n", " 17,\n", " 2,\n", " 9]" ] }, "execution_count": 145, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_1.forward_map" ] }, { "cell_type": "code", "execution_count": 146, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'j'" ] }, "execution_count": 146, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_1.advance()\n", "wheel_1.forward('a')" ] }, { "cell_type": "code", "execution_count": 147, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('jlekfcpuymsnvxgwtrozhaqbid', 'vxfzceouyadbjlsgwrkqhmpnit')" ] }, "execution_count": 147, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(wheel_1.forward(l) for l in string.ascii_lowercase), cat(wheel_1.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 148, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'b'" ] }, "execution_count": 148, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_1.position_l" ] }, { "cell_type": "code", "execution_count": 149, "metadata": { "collapsed": true }, "outputs": [], "source": [ "wheel_2 = SimpleWheel(wheel_ii_spec)\n", "assert(cat(wheel_2.forward(l) for l in string.ascii_lowercase) == 'ajdksiruxblhwtmcqgznpyfvoe')\n", "assert(cat(wheel_2.backward(l) for l in string.ascii_lowercase) == 'ajpczwrlfbdkotyuqgenhxmivs')" ] }, { "cell_type": "code", "execution_count": 150, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('ajdksiruxblhwtmcqgznpyfvoe', 'ajpczwrlfbdkotyuqgenhxmivs')" ] }, "execution_count": 150, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(wheel_2.forward(l) for l in string.ascii_lowercase), cat(wheel_2.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 151, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('bdfhjlcprtxvznyeiwgakmusqo', 'tagbpcsdqeufvnzhyixjwlrkom')" ] }, "execution_count": 151, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_3 = SimpleWheel(wheel_iii_spec)\n", "wheel_3.set_position('a')\n", "wheel_3.advance()\n", "assert(cat(wheel_3.forward(l) for l in string.ascii_lowercase) == 'cegikboqswuymxdhvfzjltrpna')\n", "assert(cat(wheel_3.backward(l) for l in string.ascii_lowercase) == 'zfaobrcpdteumygxhwivkqjnls')\n", "assert(wheel_3.position == 1)\n", "assert(wheel_3.position_l == 'b')\n", "\n", "for _ in range(24): wheel_3.advance()\n", "assert(wheel_3.position == 25)\n", "assert(wheel_3.position_l == 'z')\n", "assert(cat(wheel_3.forward(l) for l in string.ascii_lowercase) == 'pcegikmdqsuywaozfjxhblnvtr')\n", "assert(cat(wheel_3.backward(l) for l in string.ascii_lowercase) == 'nubhcqdterfvgwoaizjykxmslp')\n", "\n", "wheel_3.advance()\n", "assert(wheel_3.position == 0)\n", "assert(wheel_3.position_l == 'a')\n", "assert(cat(wheel_3.forward(l) for l in string.ascii_lowercase) == 'bdfhjlcprtxvznyeiwgakmusqo')\n", "assert(cat(wheel_3.backward(l) for l in string.ascii_lowercase) == 'tagbpcsdqeufvnzhyixjwlrkom')\n", "\n", "cat(wheel_3.forward(l) for l in string.ascii_lowercase), cat(wheel_3.backward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Wheel\n", "[Top](#top)\n", "\n", "This is the same as a `SimpleWheel`, but with the addition of a ring.\n", "\n", "The ring is moveable around the core of the wheel (with the wiring). This means that moving the ring changes the orientation of the core and wiring for the same setting.\n", "\n", "| Wheel with notch | Notch showing peg to hold it in place |\n", "| ---------------- | ------------------------------------- |\n", "| \"Enigma | \"Enigma |\n", "| (From [Crypto museum](http://www.cryptomuseum.com/crypto/enigma/img/300879/035/full.jpg)) | From [Matematik Sider](http://www.matematiksider.dk/enigma/dtu_notch_big.jpg) |\n", "\n", "Though it's not very visible in the right hand image, the extra metal below the ring shows a spring-loaded peg on the wheel core which drops into the ring, with one hole per letter. The ring setting is where the peg drops into the ring.\n", "\n", "The notch setting is used in the full Enigma to control when the wheels step forward.\n", "\n", "Note that the constructor calls the superclass's constructor, then sets the positions properly with a call to `set_position`." ] }, { "cell_type": "code", "execution_count": 152, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Wheel(SimpleWheel):\n", " def __init__(self, transform, ring_notch_letters, ring_setting=1, position='a', raw_transform=False):\n", " self.ring_notch_letters = ring_notch_letters\n", " self.ring_setting = ring_setting\n", " super(Wheel, self).__init__(transform, position=position, raw_transform=raw_transform)\n", " self.set_position(position)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `position` of the wheel is the orientation of the core. It's the same as the ring if the ring setting is 1. The `position_l` attribute is used to report the position of the ring letter, which is what the Enigma operator would see. " ] }, { "cell_type": "code", "execution_count": 153, "metadata": {}, "outputs": [], "source": [ "def __getattribute__(self,name):\n", " if name=='position_l':\n", " return unpos(self.position + self.ring_setting - 1)\n", " else:\n", " return object.__getattribute__(self, name)\n", "\n", "def set_position(self, position):\n", "# self.position = (pos(position) - self.ring_setting + 1) % 26\n", " if isinstance(position, str):\n", " self.position = (pos(position) - self.ring_setting + 1) % 26\n", " else:\n", " self.position = (position - self.ring_setting) % 26\n", "# self.notch_positions = [(pos(position) - pos(p)) % 26 for p in self.ring_notch_letters]\n", " self.notch_positions = [(self.position + self.ring_setting - 1 - pos(p)) % 26 for p in self.ring_notch_letters]\n", " \n", "setattr(Wheel, '__getattribute__', __getattribute__) \n", "setattr(Wheel, 'set_position', set_position) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Advance the wheel. Again, note the superclass call, followed by the update of the notch positions." ] }, { "cell_type": "code", "execution_count": 154, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def advance(self):\n", " super(Wheel, self).advance()\n", " self.notch_positions = [(p + 1) % 26 for p in self.notch_positions]\n", " # self.position_l = unpos(self.position + self.ring_setting - 1)\n", " return self.position\n", " \n", "setattr(Wheel, 'advance', advance) " ] }, { "cell_type": "code", "execution_count": 155, "metadata": { "collapsed": true }, "outputs": [], "source": [ "wheel_3 = Wheel(wheel_iii_spec, wheel_iii_notches, position='b', ring_setting=1)" ] }, { "cell_type": "code", "execution_count": 156, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(1, [6])" ] }, "execution_count": 156, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_3.position, wheel_3.notch_positions" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(25, [2, 15])" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_6 = Wheel(wheel_vi_spec, wheel_vi_notches, position='b', ring_setting=3)\n", "wheel_6.position, wheel_6.notch_positions" ] }, { "cell_type": "code", "execution_count": 158, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 [3, 16]\n", "1 [4, 17]\n", "2 [5, 18]\n", "3 [6, 19]\n", "4 [7, 20]\n", "5 [8, 21]\n", "6 [9, 22]\n", "7 [10, 23]\n", "8 [11, 24]\n", "9 [12, 25]\n", "10 [13, 0]\n", "11 [14, 1]\n", "12 [15, 2]\n", "13 [16, 3]\n", "14 [17, 4]\n", "15 [18, 5]\n", "16 [19, 6]\n", "17 [20, 7]\n", "18 [21, 8]\n", "19 [22, 9]\n", "20 [23, 10]\n", "21 [24, 11]\n", "22 [25, 12]\n", "23 [0, 13]\n", "24 [1, 14]\n", "25 [2, 15]\n", "0 [3, 16]\n" ] } ], "source": [ "for _ in range(27):\n", " wheel_6.advance()\n", " print(wheel_6.position, wheel_6.notch_positions)" ] }, { "cell_type": "code", "execution_count": 159, "metadata": {}, "outputs": [], "source": [ "wheel = Wheel(wheel_iii_spec, wheel_iii_notches, position='b', \n", " ring_setting=3)\n", "wheel.set_position(12)\n", "assert(wheel.position == 9)\n", "assert(16 in wheel.notch_positions)\n", "assert(wheel.position_l =='l')" ] }, { "cell_type": "code", "execution_count": 160, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[16]" ] }, "execution_count": 160, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel.notch_positions" ] }, { "cell_type": "code", "execution_count": 161, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'l'" ] }, "execution_count": 161, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel.position_l" ] }, { "cell_type": "code", "execution_count": 162, "metadata": { "collapsed": true }, "outputs": [], "source": [ "wheel_3 = Wheel(wheel_iii_spec, wheel_iii_notches, position='b', ring_setting=1)\n", "assert(wheel_3.position == 1)\n", "assert(wheel_3.notch_positions == [6])\n", "assert(wheel_3.position_l == 'b')\n", "wheel_3.advance()\n", "assert(wheel_3.position == 2)\n", "assert(wheel_3.notch_positions == [7])\n", "assert(wheel_3.position_l == 'c')" ] }, { "cell_type": "code", "execution_count": 163, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2, 15]" ] }, "execution_count": 163, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_6 = Wheel(wheel_vi_spec, wheel_vi_notches, position='b', ring_setting=3)\n", "wheel_6.notch_positions" ] }, { "cell_type": "code", "execution_count": 164, "metadata": { "collapsed": true }, "outputs": [], "source": [ "wheel_6 = Wheel(wheel_vi_spec, wheel_vi_notches, position='b', ring_setting=3)\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'xkqhwpvngzrcfoiaselbtymjdu')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'ptlyrmidoxbswhnfckquzgeavj')\n", "assert(wheel_6.position == 25)\n", "assert(2 in wheel_6.notch_positions)\n", "assert(15 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'b')\n", "\n", "wheel_6.advance()\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'jpgvoumfyqbenhzrdkasxlictw')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'skxqlhcnwarvgmebjptyfdzuio')\n", "assert(wheel_6.position == 0)\n", "assert(3 in wheel_6.notch_positions)\n", "assert(16 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'c')\n", "\n", "for _ in range(22): wheel_6.advance()\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'mgxantkzsyqjcufirldvhoewbp')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'dymswobuplgraevzkqifntxcjh')\n", "assert(wheel_6.position == 22)\n", "assert(25 in wheel_6.notch_positions)\n", "assert(12 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'y')\n", "\n", "wheel_6.advance()\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'fwzmsjyrxpibtehqkcugndvaol')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'xlrvnatokfqzduyjphemswbigc')\n", "assert(wheel_6.position == 23)\n", "assert(0 in wheel_6.notch_positions)\n", "assert(13 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'z')\n", "\n", "wheel_6.advance()\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'vylrixqwohasdgpjbtfmcuznke')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'kqumzsnjepyctxiogdlrvahfbw')\n", "assert(wheel_6.position == 24)\n", "assert(1 in wheel_6.notch_positions)\n", "assert(14 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'a')\n", "\n", "wheel_6.advance()\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'xkqhwpvngzrcfoiaselbtymjdu')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'ptlyrmidoxbswhnfckquzgeavj')\n", "assert(wheel_6.position == 25)\n", "assert(2 in wheel_6.notch_positions)\n", "assert(15 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'b')\n", "\n", "wheel_6.advance()\n", "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'jpgvoumfyqbenhzrdkasxlictw')\n", "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'skxqlhcnwarvgmebjptyfdzuio')\n", "assert(wheel_6.position == 0)\n", "assert(3 in wheel_6.notch_positions)\n", "assert(16 in wheel_6.notch_positions)\n", "assert(wheel_6.position_l == 'c')" ] }, { "cell_type": "code", "execution_count": 165, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0, 'c', [3, 16])" ] }, "execution_count": 165, "metadata": {}, "output_type": "execute_result" } ], "source": [ "wheel_6.position, wheel_6.position_l, wheel_6.notch_positions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Enigma\n", "[Top](#top)\n", "\n", "This is the full Enigma machine.\n", "\n", "It's a collection of the various components defined above. There are three wheels (left, middle, and right), a plugboard, and a reflector.\n", "\n", "The `__getattribute__` method returns the state of the machine in friendly form, generally by asking the components to return the relevant attributes." ] }, { "cell_type": "code", "execution_count": 167, "metadata": {}, "outputs": [], "source": [ "class Enigma(object):\n", " def __init__(self, reflector_spec,\n", " left_wheel_spec, left_wheel_notches,\n", " middle_wheel_spec, middle_wheel_notches,\n", " right_wheel_spec, right_wheel_notches,\n", " left_ring_setting, middle_ring_setting, right_ring_setting,\n", " plugboard_setting):\n", " self.reflector = Reflector(reflector_spec)\n", " self.left_wheel = Wheel(left_wheel_spec, left_wheel_notches, ring_setting=left_ring_setting)\n", " self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_notches, ring_setting=middle_ring_setting)\n", " self.right_wheel = Wheel(right_wheel_spec, right_wheel_notches, ring_setting=right_ring_setting)\n", " self.plugboard = Plugboard(plugboard_setting)\n", " \n", " def __getattribute__(self,name):\n", " if name=='wheel_positions':\n", " return self.left_wheel.position, self.middle_wheel.position, self.right_wheel.position \n", " elif name=='wheel_positions_l':\n", " return self.left_wheel.position_l, self.middle_wheel.position_l, self.right_wheel.position_l \n", " elif name=='notch_positions':\n", " return (self.left_wheel.notch_positions, \n", " self.middle_wheel.notch_positions, \n", " self.right_wheel.notch_positions)\n", " else:\n", " return object.__getattribute__(self, name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Set the wheels to the initial positions. " ] }, { "cell_type": "code", "execution_count": 168, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def set_wheels(self, left_wheel_position, middle_wheel_position, right_wheel_position):\n", " self.left_wheel.set_position(left_wheel_position)\n", " self.middle_wheel.set_position(middle_wheel_position)\n", " self.right_wheel.set_position(right_wheel_position)\n", "\n", "setattr(Enigma, 'set_wheels', set_wheels)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`lookup` just follows a path through the machine without changing the positions of any parts. It just follows a signal from the input, thorough all the components, to the reflector, back through all the components, to the output." ] }, { "cell_type": "code", "execution_count": 169, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def lookup(self, letter):\n", " a = self.plugboard.forward(letter)\n", " b = self.right_wheel.forward(a)\n", " c = self.middle_wheel.forward(b)\n", " d = self.left_wheel.forward(c)\n", " e = self.reflector.forward(d)\n", " f = self.left_wheel.backward(e)\n", " g = self.middle_wheel.backward(f)\n", " h = self.right_wheel.backward(g)\n", " i = self.plugboard.backward(h)\n", " return i\n", "\n", "setattr(Enigma, 'lookup', lookup)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`advance` moves the wheels on one step. The right wheel always advances. If the notch is in the zero position, the wheel also advances the wheel to the left. \n", "\n", "It follows the 'double stepping' behaviour of the engima machines." ] }, { "cell_type": "code", "execution_count": 170, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def advance(self):\n", " advance_middle = False\n", " advance_left = False\n", " if 0 in self.right_wheel.notch_positions:\n", " advance_middle = True\n", " if 0 in self.middle_wheel.notch_positions:\n", " advance_left = True\n", " advance_middle = True\n", " self.right_wheel.advance()\n", " if advance_middle: self.middle_wheel.advance()\n", " if advance_left: self.left_wheel.advance()\n", "\n", "setattr(Enigma, 'advance', advance) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, encipher letters and messages. \n", "\n", "Note that the wheels advance _before_ the letter signal is sent through the machine: in the physical machine, the advancing is done by pressing the key on the keyboard. \n", "\n", "Also note that the messages are cleaned before use, so letters are converted to lower case and non-letters are removed." ] }, { "cell_type": "code", "execution_count": 220, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def encipher_letter(self, letter):\n", " self.advance()\n", " return self.lookup(letter)\n", "\n", "def encipher(self, message, debug=False):\n", " enciphered = ''\n", " for letter in clean(message):\n", " enciphered += self.encipher_letter(letter)\n", " if debug:\n", " print('Wheels now', list(self.wheel_positions_l), 'enciphering {} -> {}'.format(letter, self.lookup(letter)))\n", " return enciphered\n", "\n", "setattr(Enigma, 'encipher_letter', encipher_letter)\n", "setattr(Enigma, 'encipher', encipher)\n", "setattr(Enigma, 'decipher', encipher)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## Testing Enigma\n", "[Top](#top)\n", "\n", "Some tests of the Enigma machine." ] }, { "cell_type": "code", "execution_count": 172, "metadata": { "collapsed": true }, "outputs": [], "source": [ "enigma = Enigma(reflector_b_spec, \n", " wheel_i_spec, wheel_i_notches,\n", " wheel_ii_spec, wheel_ii_notches,\n", " wheel_iii_spec, wheel_iii_notches,\n", " 1, 1, 1,\n", " '')" ] }, { "cell_type": "code", "execution_count": 173, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'u'" ] }, "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.lookup('a')" ] }, { "cell_type": "code", "execution_count": 174, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'a'" ] }, "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.lookup('u')" ] }, { "cell_type": "code", "execution_count": 175, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'uejobtpzwcnsrkdgvmlfaqiyxh'" ] }, "execution_count": 175, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(enigma.lookup(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 176, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'abcdefghijklmnopqrstuvwxyz'" ] }, "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(enigma.lookup(enigma.lookup(l)) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 177, "metadata": { "collapsed": true }, "outputs": [], "source": [ "assert(cat(enigma.lookup(enigma.lookup(l)) for l in string.ascii_lowercase) == string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 178, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0 :: a a a ; [10] [22] [5] uejobtpzwcnsrkdgvmlfaqiyxh\n", "1 :: a a b ; [10] [22] [6] baqmfexihswpdytlcvjozrkgnu\n", "2 :: a a c ; [10] [22] [7] djralkwpobfeyqihncxzvugsmt\n", "3 :: a a d ; [10] [22] [8] zlejcuitgdmbkonsvxphfqyrwa\n", "4 :: a a e ; [10] [22] [9] gcblwtakqzhdosmxiunfryepvj\n", "5 :: a a f ; [10] [22] [10] osnirgfmdpvuhcajwebxlkqtzy\n", "6 :: a a g ; [10] [22] [11] wymvnqzjlhoicekuftxrpdasbg\n", "7 :: a a h ; [10] [22] [12] cjafkdztpbeuormiwnvhlsqyxg\n", "8 :: a a i ; [10] [22] [13] xijuyslvbczgnmqwotfrdhpaek\n", "9 :: a a j ; [10] [22] [14] lfzrwbytjisaovmuxdkhpneqgc\n", "10 :: a a k ; [10] [22] [15] tkezcqynuwbpvhslfxoaimjrgd\n", "11 :: a a l ; [10] [22] [16] kiwfnduxbsaotelqpvjmgrchzy\n", "12 :: a a m ; [10] [22] [17] sfkutbpoxycrnmhgwlaedzqijv\n", "13 :: a a n ; [10] [22] [18] baqwlkhgrsfextpocijnvudmzy\n", "14 :: a a o ; [10] [22] [19] teofbdzxqkjyrscvimnawpuhlg\n", "15 :: a a p ; [10] [22] [20] mhypswrbzxqvaondkgeutlfjci\n", "16 :: a a q ; [10] [22] [21] cpasnrhgkuixzevbyfdwjotlqm\n", "17 :: a a r ; [10] [22] [22] dlfatcjwygvbnmzrxpueskhqio\n", "18 :: a a s ; [10] [22] [23] lxymzjuqtfpadsrkhonigwvbce\n", "19 :: a a t ; [10] [22] [24] puvioztjdhxmlyeawsrgbcqknf\n", "20 :: a a u ; [10] [22] [25] baigpldqcowfyzjehvtsxrkumn\n", "21 :: a a v ; [10] [22] [0] mnvfydiwgzsoablrxpkutchqej\n", "22 :: a b w ; [10] [23] [1] ulfopcykswhbzvderqixanjtgm\n", "23 :: a b x ; [10] [23] [2] qmwftdyovursbzhxaklejicpgn\n", "24 :: a b y ; [10] [23] [3] oljmzxrvucybdqasngpwihtfke\n", "25 :: a b z ; [10] [23] [4] fwevcalzxutgysrqponkjdbimh\n" ] } ], "source": [ "# check the middle wheel turns over\n", "enigma.set_wheels('a', 'a', 'a')\n", "for i in range(26):\n", " print(i, '::', \n", " enigma.left_wheel.position_l, enigma.middle_wheel.position_l, enigma.right_wheel.position_l, ';',\n", " enigma.left_wheel.notch_positions, enigma.middle_wheel.notch_positions, enigma.right_wheel.notch_positions, \n", " cat(enigma.lookup(l) for l in string.ascii_lowercase))\n", " enigma.advance()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Formal test of middle wheel turnover" ] }, { "cell_type": "code", "execution_count": 179, "metadata": { "collapsed": true, "scrolled": true }, "outputs": [], "source": [ "enigma.set_wheels('a', 'a', 't')\n", "assert(enigma.wheel_positions == (0, 0, 19))\n", "assert(cat(enigma.wheel_positions_l) == 'aat')\n", "assert(enigma.notch_positions == ([10], [22], [24]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'puvioztjdhxmlyeawsrgbcqknf')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 0, 20))\n", "assert(cat(enigma.wheel_positions_l) == 'aau')\n", "assert(enigma.notch_positions == ([10], [22], [25]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baigpldqcowfyzjehvtsxrkumn')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 0, 21))\n", "assert(cat(enigma.wheel_positions_l) == 'aav')\n", "assert(enigma.notch_positions == ([10], [22], [0]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'mnvfydiwgzsoablrxpkutchqej')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 1, 22))\n", "assert(cat(enigma.wheel_positions_l) == 'abw')\n", "assert(enigma.notch_positions == ([10], [23], [1]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ulfopcykswhbzvderqixanjtgm')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 1, 23))\n", "assert(cat(enigma.wheel_positions_l) == 'abx')\n", "assert(enigma.notch_positions == ([10], [23], [2]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qmwftdyovursbzhxaklejicpgn')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 1, 24))\n", "assert(cat(enigma.wheel_positions_l) == 'aby')\n", "assert(enigma.notch_positions == ([10], [23 ], [3]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'oljmzxrvucybdqasngpwihtfke')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Test of middle wheel advancing the left wheel, exhibiting the \"double step\" behaviour." ] }, { "cell_type": "code", "execution_count": 180, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 5, 24) ('b', 'f', 'y') ([11], [1], [3]) baknstqzrmcxjdvygiefwoulph\n" ] } ], "source": [ "enigma.set_wheels('a', 'd', 't')\n", "assert(enigma.wheel_positions == (0, 3, 19))\n", "assert(cat(enigma.wheel_positions_l) == 'adt')\n", "assert(enigma.notch_positions == ([10], [25], [24]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'zcbpqxwsjiuonmldethrkygfva')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 3, 20))\n", "assert(cat(enigma.wheel_positions_l) == 'adu')\n", "assert(enigma.notch_positions == ([10], [25], [25]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ehprawjbngotxikcsdqlzyfmvu')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 3, 21))\n", "assert(cat(enigma.wheel_positions_l) == 'adv')\n", "assert(enigma.notch_positions == ([10], [25], [0]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'eqzxarpihmnvjkwgbfuyslodtc')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 4, 22))\n", "assert(cat(enigma.wheel_positions_l) == 'aew')\n", "assert(enigma.notch_positions == ([10], [0], [1]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qedcbtpluzmhkongavwfirsyxj')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 23))\n", "assert(cat(enigma.wheel_positions_l) == 'bfx')\n", "assert(enigma.notch_positions == ([11], [1], [2]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'iwuedhsfazqxytvrkpgncoblmj')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 24))\n", "assert(cat(enigma.wheel_positions_l) == 'bfy')\n", "assert(enigma.notch_positions == ([11], [1], [3]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baknstqzrmcxjdvygiefwoulph')\n", "\n", "print(enigma.wheel_positions, enigma.wheel_positions_l, enigma.notch_positions, \n", " cat(enigma.lookup(l) for l in string.ascii_lowercase))\n" ] }, { "cell_type": "code", "execution_count": 181, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'olpfhnvflyn'" ] }, "execution_count": 181, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.set_wheels('a', 'a', 'a')\n", "ct = enigma.encipher('testmessage')\n", "assert(ct == 'olpfhnvflyn')\n", "ct" ] }, { "cell_type": "code", "execution_count": 182, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'lawnjgpwjik'" ] }, "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.set_wheels('a', 'd', 't')\n", "ct = enigma.encipher('testmessage')\n", "assert(ct == 'lawnjgpwjik')\n", "ct" ] }, { "cell_type": "code", "execution_count": 183, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 3, 20))\n", "assert(cat(enigma.wheel_positions_l) == 'adu')\n", "assert(enigma.notch_positions == ([10], [25], [25]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ehprawjbngotxikcsdqlzyfmvu')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 3, 21))\n", "assert(cat(enigma.wheel_positions_l) == 'adv')\n", "assert(enigma.notch_positions == ([10], [25], [0]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'eqzxarpihmnvjkwgbfuyslodtc')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (0, 4, 22))\n", "assert(cat(enigma.wheel_positions_l) == 'aew')\n", "assert(enigma.notch_positions == ([10], [0], [1]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qedcbtpluzmhkongavwfirsyxj')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 23))\n", "assert(cat(enigma.wheel_positions_l) == 'bfx')\n", "assert(enigma.notch_positions == ([11], [1], [2]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'iwuedhsfazqxytvrkpgncoblmj')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 24))\n", "assert(cat(enigma.wheel_positions_l) == 'bfy')\n", "assert(enigma.notch_positions == ([11], [1], [3]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baknstqzrmcxjdvygiefwoulph')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 25))\n", "assert(cat(enigma.wheel_positions_l) == 'bfz')\n", "assert(enigma.notch_positions == ([11], [1], [4]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'mudcgteyjiwravxzslqfbnkohp')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 0))\n", "assert(cat(enigma.wheel_positions_l) == 'bfa')\n", "assert(enigma.notch_positions == ([11], [1], [5]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'gjkuotarmbcziwesvhpfdqnyxl')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 1))\n", "assert(cat(enigma.wheel_positions_l) == 'bfb')\n", "assert(enigma.notch_positions == ([11], [1], [6]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'fcbmpaqihxytdrvegnwlzosjku')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 2))\n", "assert(cat(enigma.wheel_positions_l) == 'bfc')\n", "assert(enigma.notch_positions == ([11], [1], [7]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'jktrlpmywabegzqfodxcvuishn')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 3))\n", "assert(cat(enigma.wheel_positions_l) == 'bfd')\n", "assert(enigma.notch_positions == ([11], [1], [8]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baetclivgurfzqponkxdjhyswm')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 4))\n", "assert(cat(enigma.wheel_positions_l) == 'bfe')\n", "assert(enigma.notch_positions == ([11], [1], [9]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'wvjlkpzxtcedqsyfmunirbahog')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 5))\n", "assert(cat(enigma.wheel_positions_l) == 'bff')\n", "assert(enigma.notch_positions == ([11], [1], [10]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'zpqiogfsdlmjkyebcvhxwrutna')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 6))\n", "assert(cat(enigma.wheel_positions_l) == 'bfg')\n", "assert(enigma.notch_positions == ([11], [1], [11]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'fjmnwayslbxicdpouthrqzekgv')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 7))\n", "assert(cat(enigma.wheel_positions_l) == 'bfh')\n", "assert(enigma.notch_positions == ([11], [1], [12]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'csafzdyloxuhnmitwvbpkrqjge')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 8))\n", "assert(cat(enigma.wheel_positions_l) == 'bfi')\n", "assert(enigma.notch_positions == ([11], [1], [13]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'kihyvulcbtagwrqzonxjfemsdp')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 9))\n", "assert(cat(enigma.wheel_positions_l) == 'bfj')\n", "assert(enigma.notch_positions == ([11], [1], [14]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'pgzrytbksqhwxvuajdifonlmec')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 10))\n", "assert(cat(enigma.wheel_positions_l) == 'bfk')\n", "assert(enigma.notch_positions == ([11], [1], [15]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'fkirsazncwbvyhpoudexqljtmg')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 11))\n", "assert(cat(enigma.wheel_positions_l) == 'bfl')\n", "assert(enigma.notch_positions == ([11], [1], [16]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'mhkronubsvctafeqpdilgjxwzy')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 12))\n", "assert(cat(enigma.wheel_positions_l) == 'bfm')\n", "assert(enigma.notch_positions == ([11], [1], [17]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'gnkuoxarzycmlbetvhwpdqsfji')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 13))\n", "assert(cat(enigma.wheel_positions_l) == 'bfn')\n", "assert(enigma.notch_positions == ([11], [1], [18]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'bainslqkcxhfudpogtermwvjzy')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 14))\n", "assert(cat(enigma.wheel_positions_l) == 'bfo')\n", "assert(enigma.notch_positions == ([11], [1], [19]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'xemfbdnwjitycgzusvqkprhalo')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 15))\n", "assert(cat(enigma.wheel_positions_l) == 'bfp')\n", "assert(enigma.notch_positions == ([11], [1], [20]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qixksmhgbtdvfonrapejwluczy')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 16))\n", "assert(cat(enigma.wheel_positions_l) == 'bfq')\n", "assert(enigma.notch_positions == ([11], [1], [21]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'cgaulmbskwiefrtzynhodxjvqp')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 17))\n", "assert(cat(enigma.wheel_positions_l) == 'bfr')\n", "assert(enigma.notch_positions == ([11], [1], [22]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'iwqfldszaxvenmyrcpgutkbjoh')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 18))\n", "assert(cat(enigma.wheel_positions_l) == 'bfs')\n", "assert(enigma.notch_positions == ([11], [1], [23]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'vxykrjilgfdhqtusmepnoazbcw')\n", "\n", "enigma.advance()\n", "assert(enigma.wheel_positions == (1, 5, 19))\n", "assert(cat(enigma.wheel_positions_l) == 'bft')\n", "assert(enigma.notch_positions == ([11], [1], [24]))\n", "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ieysbvkjahgmlpxnwtdrzfqocu')\n", "\n" ] } ], "source": [ "enigma.set_wheels('a', 'd', 't')\n", "for i in range(26):\n", " enigma.advance()\n", " print('enigma.advance()')\n", " print(\"assert(enigma.wheel_positions == {})\".format(enigma.wheel_positions))\n", " print(\"assert(cat(enigma.wheel_positions_l) == '{}')\".format(cat(enigma.wheel_positions_l)))\n", " print(\"assert(enigma.notch_positions == {})\".format(enigma.notch_positions))\n", " print(\"assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(enigma.lookup(l) for l in string.ascii_lowercase)))\n", " print()" ] }, { "cell_type": "code", "execution_count": 184, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'bahxvfrpdc'" ] }, "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.set_wheels('a', 'd', 't')\n", "ct = enigma.encipher('hellothere')\n", "assert(ct == 'bahxvfrpdc')\n", "ct" ] }, { "cell_type": "code", "execution_count": 185, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr'" ] }, "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.set_wheels('b', 'd', 'q')\n", "ct = enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')\n", "assert(ct == 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr')\n", "assert(enigma.left_wheel.position_l == 'c')\n", "assert(enigma.middle_wheel.position_l == 'h')\n", "assert(enigma.right_wheel.position_l == 'a')\n", "ct" ] }, { "cell_type": "code", "execution_count": 186, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'c'" ] }, "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma.left_wheel.position_l" ] }, { "cell_type": "code", "execution_count": 187, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm\n", "# Enigma simulation settings are \n", "# http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJFE;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX\n", "w_enigma = Enigma(reflector_b_spec, \n", " wheel_i_spec, wheel_i_notches,\n", " wheel_v_spec, wheel_v_notches,\n", " wheel_iii_spec, wheel_iii_notches,\n", " 6, 20, 24,\n", " 'ua pf rq so ni ey bg hl tx zj')" ] }, { "cell_type": "code", "execution_count": 188, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm\n", "# # Enigma simulation settings are \n", "# # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJFE;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX\n", "# enigma = Enigma(reflector_b_spec, \n", "# wheel_i_spec, wheel_i_notches,\n", "# wheel_v_spec, wheel_v_notches,\n", "# wheel_iii_spec, wheel_iii_notches,\n", "# 6, 20, 24,\n", "# 'ua pf rq so ni ey bg hl tx zj')" ] }, { "cell_type": "code", "execution_count": 189, "metadata": { "collapsed": true }, "outputs": [], "source": [ "w_enigma.set_wheels('j', 'e', 'u')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (4, 11, 24))\n", "assert(cat(w_enigma.wheel_positions_l) == 'jev')\n", "assert(w_enigma.notch_positions == ([19], [5], [0]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mvqjlyowkdieasgzcunxrbhtfp')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (4, 12, 25))\n", "assert(cat(w_enigma.wheel_positions_l) == 'jfw')\n", "assert(w_enigma.notch_positions == ([19], [6], [1]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'sjolzuyvrbwdpxcmtiaqfhknge')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (4, 12, 0))\n", "assert(cat(w_enigma.wheel_positions_l) == 'jfx')\n", "assert(w_enigma.notch_positions == ([19], [6], [2]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'qrxedkoywufmlvgsabpzjnicht')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (4, 12, 1))\n", "assert(cat(w_enigma.wheel_positions_l) == 'jfy')\n", "assert(w_enigma.notch_positions == ([19], [6], [3]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hpsukliagqefwvtbjxcodnmrzy')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (4, 12, 2))\n", "assert(cat(w_enigma.wheel_positions_l) == 'jfz')\n", "assert(w_enigma.notch_positions == ([19], [6], [4]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'zevnbpyqowrtxdifhkulscjmga')\n" ] }, { "cell_type": "code", "execution_count": 190, "metadata": { "collapsed": true }, "outputs": [], "source": [ "w_enigma.set_wheels('i', 'd', 'z')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 3))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ida')\n", "assert(w_enigma.notch_positions == ([18], [4], [5]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ikhpqrvcambzjondefwyxgsutl')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 4))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idb')\n", "assert(w_enigma.notch_positions == ([18], [4], [6]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cdabskhgzwfmlqvunyexpojtri')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 5))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idc')\n", "assert(w_enigma.notch_positions == ([18], [4], [7]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pcbwiqhgemyvjsuaftnroldzkx')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 6))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idd')\n", "assert(w_enigma.notch_positions == ([18], [4], [8]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xcbfvdnouptmlghjzwykierasq')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 7))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ide')\n", "assert(w_enigma.notch_positions == ([18], [4], [9]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xfvglbdynuseriwqpmkzjcoaht')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 8))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idf')\n", "assert(w_enigma.notch_positions == ([18], [4], [10]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'tfpqlbouynsewjgcdxkahzmriv')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 9))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idg')\n", "assert(w_enigma.notch_positions == ([18], [4], [11]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cjaunvlwtbygzexrspqidfhokm')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 10))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idh')\n", "assert(w_enigma.notch_positions == ([18], [4], [12]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yltxkrqvowebzpingfucshjdam')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 11))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idi')\n", "assert(w_enigma.notch_positions == ([18], [4], [13]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'myktluzrnxceaiqsohpdfwvjbg')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 12))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idj')\n", "assert(w_enigma.notch_positions == ([18], [4], [14]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pynjrmiugdqxfcvakewzhoslbt')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 13))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idk')\n", "assert(w_enigma.notch_positions == ([18], [4], [15]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mwvedyplnoxhaijgrqtszcbkfu')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 14))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idl')\n", "assert(w_enigma.notch_positions == ([18], [4], [16]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'qcbrfeutvoxpnmjladzhgiykws')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 15))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idm')\n", "assert(w_enigma.notch_positions == ([18], [4], [17]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'dnoahryetsmukbcvwfjilpqzgx')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 16))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idn')\n", "assert(w_enigma.notch_positions == ([18], [4], [18]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nidcfehgbqsovalyjzkxwmutpr')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 17))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ido')\n", "assert(w_enigma.notch_positions == ([18], [4], [19]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'joifxdulcarhzpbntkwqgysevm')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 18))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idp')\n", "assert(w_enigma.notch_positions == ([18], [4], [20]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ptnlsxvozmwdjchayuebrgkfqi')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 19))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idq')\n", "assert(w_enigma.notch_positions == ([18], [4], [21]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'slwopzqnmxybihdeguavrtcjkf')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 20))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idr')\n", "assert(w_enigma.notch_positions == ([18], [4], [22]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hcbedwlamzogixkytsrqvufnpj')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 21))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ids')\n", "assert(w_enigma.notch_positions == ([18], [4], [23]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'odxbjwzrmelkisavuhnyqpfctg')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 22))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idt')\n", "assert(w_enigma.notch_positions == ([18], [4], [24]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'udgbfeclrwnhxksvtioqapjmzy')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 23))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idu')\n", "assert(w_enigma.notch_positions == ([18], [4], [25]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nrdczqxmowvshaiufblypkjgte')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 24))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idv')\n", "assert(w_enigma.notch_positions == ([18], [4], [0]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hkifjdoacebqtzgulyvmpsxwrn')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 25))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iew')\n", "assert(w_enigma.notch_positions == ([18], [5], [1]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yptzuhofqvnmlkgbixwcejsrad')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 0))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iex')\n", "assert(w_enigma.notch_positions == ([18], [5], [2]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'vkdcwhqfjibzsptngumoraeyxl')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 1))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iey')\n", "assert(w_enigma.notch_positions == ([18], [5], [3]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'wenpbqrouxlkychdfgzvitajms')\n" ] }, { "cell_type": "code", "execution_count": 191, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([18], [5], [3])" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "w_enigma.notch_positions" ] }, { "cell_type": "code", "execution_count": 192, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('verylongtestmessagewithanextrabitofmessageforgoodmeasure',\n", " (3, 12, 6),\n", " ('i', 'f', 'd'),\n", " ([18], [6], [8]),\n", " 'urygzpdmxtwshqvfnbljaokice')" ] }, "execution_count": 192, "metadata": {}, "output_type": "execute_result" } ], "source": [ "w_enigma.set_wheels('i', 'd', 'z')\n", "ct = w_enigma.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure')\n", "assert(ct == 'gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz')\n", "assert(w_enigma.wheel_positions == (3, 12, 6))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ifd')\n", "assert(w_enigma.notch_positions == ([18], [6], [8]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'urygzpdmxtwshqvfnbljaokice')\n", "\n", "w_enigma.set_wheels('i', 'd', 'z')\n", "pt = w_enigma.encipher('gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz')\n", "assert(pt == 'verylongtestmessagewithanextrabitofmessageforgoodmeasure')\n", "\n", "pt, w_enigma.wheel_positions, w_enigma.wheel_positions_l, w_enigma.notch_positions, cat(w_enigma.lookup(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 193, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 3))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ida')\n", "assert(w_enigma.notch_positions == ([18], [4], [5]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ikhpqrvcambzjondefwyxgsutl')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 4))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idb')\n", "assert(w_enigma.notch_positions == ([18], [4], [6]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cdabskhgzwfmlqvunyexpojtri')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 5))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idc')\n", "assert(w_enigma.notch_positions == ([18], [4], [7]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pcbwiqhgemyvjsuaftnroldzkx')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 6))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idd')\n", "assert(w_enigma.notch_positions == ([18], [4], [8]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xcbfvdnouptmlghjzwykierasq')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 7))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ide')\n", "assert(w_enigma.notch_positions == ([18], [4], [9]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xfvglbdynuseriwqpmkzjcoaht')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 8))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idf')\n", "assert(w_enigma.notch_positions == ([18], [4], [10]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'tfpqlbouynsewjgcdxkahzmriv')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 9))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idg')\n", "assert(w_enigma.notch_positions == ([18], [4], [11]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cjaunvlwtbygzexrspqidfhokm')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 10))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idh')\n", "assert(w_enigma.notch_positions == ([18], [4], [12]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yltxkrqvowebzpingfucshjdam')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 11))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idi')\n", "assert(w_enigma.notch_positions == ([18], [4], [13]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'myktluzrnxceaiqsohpdfwvjbg')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 12))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idj')\n", "assert(w_enigma.notch_positions == ([18], [4], [14]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pynjrmiugdqxfcvakewzhoslbt')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 13))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idk')\n", "assert(w_enigma.notch_positions == ([18], [4], [15]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mwvedyplnoxhaijgrqtszcbkfu')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 14))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idl')\n", "assert(w_enigma.notch_positions == ([18], [4], [16]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'qcbrfeutvoxpnmjladzhgiykws')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 15))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idm')\n", "assert(w_enigma.notch_positions == ([18], [4], [17]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'dnoahryetsmukbcvwfjilpqzgx')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 16))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idn')\n", "assert(w_enigma.notch_positions == ([18], [4], [18]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nidcfehgbqsovalyjzkxwmutpr')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 17))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ido')\n", "assert(w_enigma.notch_positions == ([18], [4], [19]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'joifxdulcarhzpbntkwqgysevm')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 18))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idp')\n", "assert(w_enigma.notch_positions == ([18], [4], [20]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ptnlsxvozmwdjchayuebrgkfqi')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 19))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idq')\n", "assert(w_enigma.notch_positions == ([18], [4], [21]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'slwopzqnmxybihdeguavrtcjkf')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 20))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idr')\n", "assert(w_enigma.notch_positions == ([18], [4], [22]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hcbedwlamzogixkytsrqvufnpj')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 21))\n", "assert(cat(w_enigma.wheel_positions_l) == 'ids')\n", "assert(w_enigma.notch_positions == ([18], [4], [23]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'odxbjwzrmelkisavuhnyqpfctg')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 22))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idt')\n", "assert(w_enigma.notch_positions == ([18], [4], [24]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'udgbfeclrwnhxksvtioqapjmzy')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 23))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idu')\n", "assert(w_enigma.notch_positions == ([18], [4], [25]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nrdczqxmowvshaiufblypkjgte')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 10, 24))\n", "assert(cat(w_enigma.wheel_positions_l) == 'idv')\n", "assert(w_enigma.notch_positions == ([18], [4], [0]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hkifjdoacebqtzgulyvmpsxwrn')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 25))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iew')\n", "assert(w_enigma.notch_positions == ([18], [5], [1]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yptzuhofqvnmlkgbixwcejsrad')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 0))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iex')\n", "assert(w_enigma.notch_positions == ([18], [5], [2]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'vkdcwhqfjibzsptngumoraeyxl')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 1))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iey')\n", "assert(w_enigma.notch_positions == ([18], [5], [3]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'wenpbqrouxlkychdfgzvitajms')\n", "\n", "w_enigma.advance()\n", "assert(w_enigma.wheel_positions == (3, 11, 2))\n", "assert(cat(w_enigma.wheel_positions_l) == 'iez')\n", "assert(w_enigma.notch_positions == ([18], [5], [4]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'szgyqvclkoihurjwenaxmfptdb')\n", "\n" ] } ], "source": [ "w_enigma.set_wheels('i', 'd', 'z')\n", "\n", "for i in range(26):\n", " w_enigma.advance()\n", " print('w_enigma.advance()')\n", " print(\"assert(w_enigma.wheel_positions == {})\".format(w_enigma.wheel_positions))\n", " print(\"assert(cat(w_enigma.wheel_positions_l) == '{}')\".format(cat(w_enigma.wheel_positions_l)))\n", " print(\"assert(w_enigma.notch_positions == {})\".format(w_enigma.notch_positions))\n", " print(\"assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n", " print()" ] }, { "cell_type": "code", "execution_count": 215, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "self.assertEqual(self.enigma31.wheel_positions, (21, 5, 22))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayt')\n", "self.assertEqual(self.enigma31.notch_positions, ([10], [25], [24]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'izhrgtecaslkywvqpdjfxonumb')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (21, 5, 23))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayu')\n", "self.assertEqual(self.enigma31.notch_positions, ([10], [25], [25]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'dtoavihgflwjnmcsrqpbzekyxu')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (21, 5, 24))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayv')\n", "self.assertEqual(self.enigma31.notch_positions, ([10], [25], [0]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'xquhtpsdwkjonmlfbvgecriazy')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (21, 6, 25))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azw')\n", "self.assertEqual(self.enigma31.notch_positions, ([10], [0], [1]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'dofapcluvmngjkbezyxwhitsrq')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 0))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bax')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [2]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'jdlbmswztapcexrkuofiqygnvh')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 1))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bay')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [3]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'iydcnuhgawsoxelztvkqfrjmbp')\n", "\n" ] } ], "source": [ "w_enigma.set_wheels('a', 'y', 't')\n", "\n", "print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n", "print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n", "print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n", "print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n", "print()\n", "\n", "for i in range(5):\n", " w_enigma.advance()\n", " print('self.enigma31.advance()')\n", " print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n", " print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n", " print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n", " print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n", " print()" ] }, { "cell_type": "code", "execution_count": 216, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "self.assertEqual(self.enigma31.wheel_positions, (21, 6, 22))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azt')\n", "self.assertEqual(self.enigma31.notch_positions, ([10], [0], [24]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'idjbptqwacsvnmregokfzlhyxu')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 23))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bau')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [25]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'rniszouwcxtvqbfymadkglhjpe')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 24))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bav')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [0]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'qijfsdmkbchugxtwazeolypnvr')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 8, 25))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbw')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [2], [1]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'xprtlozyskjewqfbncidvumahg')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 8, 0))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbx')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [2], [2]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'vtfuyczoqxmpkwhlisrbdanjeg')\n", "\n", "self.enigma31.advance()\n", "self.assertEqual(self.enigma31.wheel_positions, (22, 8, 1))\n", "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bby')\n", "self.assertEqual(self.enigma31.notch_positions, ([11], [2], [3]))\n", "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'tjrhzpqdobwxyuifgcvansklme')\n", "\n" ] } ], "source": [ "w_enigma.set_wheels('a', 'z', 't')\n", "\n", "print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n", "print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n", "print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n", "print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n", "print()\n", "\n", "for i in range(5):\n", " w_enigma.advance()\n", " print('self.enigma31.advance()')\n", " print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n", " print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n", " print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n", " print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n", " print()" ] }, { "cell_type": "code", "execution_count": 221, "metadata": {}, "outputs": [], "source": [ "w_enigma.set_wheels('i', 'z', 'd')\n", "ct = w_enigma.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure')\n", "assert(ct == 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki')\n", "assert(w_enigma.wheel_positions == (4, 9, 10))\n", "assert(cat(w_enigma.wheel_positions_l) == 'jch')\n", "assert(w_enigma.notch_positions == ([19], [3], [12]))\n", "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mopnigfuesqwadbcktjrhylzvx')\n", "\n", "w_enigma.set_wheels('i', 'z', 'd')\n", "pt = w_enigma.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki')\n", "assert(pt == 'verylongtestmessagewithanextrabitofmessageforgoodmeasure')" ] }, { "cell_type": "code", "execution_count": 218, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "([19], [3], [12])" ] }, "execution_count": 218, "metadata": {}, "output_type": "execute_result" } ], "source": [ "w_enigma.notch_positions" ] }, { "cell_type": "code", "execution_count": 194, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# Reflector B\n", "# Rotors III, I, II with rings 17, 11, 19\n", "# Plugboard pairs GU FZ BD LK TC PS HV WN JE AM\n", "\n", "tbt_enigma = Enigma(reflector_b_spec, \n", " wheel_iii_spec, wheel_iii_notches,\n", " wheel_i_spec, wheel_i_notches,\n", " wheel_ii_spec, wheel_ii_notches,\n", " 17, 11, 19,\n", " 'GU FZ BD LK TC PS HV WN JE AM'.lower())" ] }, { "cell_type": "code", "execution_count": 195, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'jbvbwwzfslhxnhzzccsngebmrnswgjonwbjnzcfgadeuoyameylmpvny'" ] }, "execution_count": 195, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tbt_enigma.set_wheels('a', 'q', 'v')\n", "ct = tbt_enigma.encipher('very long test message with an extra bit of message for good measure')\n", "ct" ] }, { "cell_type": "code", "execution_count": 196, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'SLGNC SZXLT KZEBG HSTGY WDMPR'" ] }, "execution_count": 196, "metadata": {}, "output_type": "execute_result" } ], "source": [ "target_ct = 'SLGNC SZXLT KZEBG HSTGY WDMPR'\n", "target_ct" ] }, { "cell_type": "code", "execution_count": 197, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Theyw erede tecte d byBri tishs hipsi nclud'" ] }, "execution_count": 197, "metadata": {}, "output_type": "execute_result" } ], "source": [ "target_pt = 'Theyw erede tecte d byBri tishs hipsi nclud'\n", "target_pt" ] }, { "cell_type": "code", "execution_count": 198, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(29, 43)" ] }, "execution_count": 198, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(target_ct), len(target_pt)" ] }, { "cell_type": "code", "execution_count": 199, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SLGNC SZXLT KZEBG HSTGY WDMPR\n", "Theyw erede tecte d byBri tishs hipsi nclud\n" ] } ], "source": [ "print('{}\\n{}'.format(target_ct, target_pt))" ] }, { "cell_type": "code", "execution_count": 200, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Theyw erede tecte d byBri tishs hipsi nclud\n", "SLGNC SZXLT KZEBG HSTGY WDMPR\n", "slgncszxltkzebghstgywdmprucuzqdqzpve\n", "theyweredetectedbybritish\n" ] } ], "source": [ "tbt_enigma.set_wheels('a', 'a', 'a')\n", "this_pt = tbt_enigma.encipher(target_ct)\n", "\n", "tbt_enigma.set_wheels('a', 'a', 'a')\n", "this_ct = tbt_enigma.encipher(target_pt)\n", "\n", "\n", "print('{}\\n{}\\n{}\\n{}'.format(target_pt, target_ct, this_ct, this_pt))" ] }, { "cell_type": "code", "execution_count": 201, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import itertools" ] }, { "cell_type": "code", "execution_count": 202, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def str_ham(s1, s2):\n", " \"\"\"Hamming distance for strings\"\"\"\n", " return sum(1 for c1, c2 in zip(s1, s2) if c1 != c2)" ] }, { "cell_type": "code", "execution_count": 203, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 203, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str_ham('hello', 'hello')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A brute-force check of all message settings, looking for the one that generates the target text." ] }, { "cell_type": "code", "execution_count": 204, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "best ('a', 'a', 'a') 29\n", "best ('a', 'a', 'a') 29\n", "best ('a', 'a', 'a') 29\n", "best ('a', 'a', 'a') 29\n", "1 loop, best of 3: 20.6 s per loop\n" ] } ], "source": [ "%%timeit\n", "best = ('a', 'a', 'a')\n", "best_hd = 10000\n", "for w1, w2, w3 in itertools.product(string.ascii_lowercase, repeat=3):\n", " tbt_enigma.set_wheels(w1, w2, w3)\n", " this_ct = tbt_enigma.encipher(target_pt)\n", " if this_ct == target_ct:\n", " print(w1, w2, w3)\n", " if str_ham(this_ct, target_ct) < best_hd:\n", " best = (w1, w2, w3)\n", " best_hd = str_ham(this_ct, target_ct)\n", "print('best', best, best_hd)" ] }, { "cell_type": "code", "execution_count": 205, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wheels now ['a', 'a', 'b'] enciphering t -> s\n", "Wheels now ['a', 'a', 'c'] enciphering h -> l\n", "Wheels now ['a', 'a', 'd'] enciphering e -> g\n", "Wheels now ['a', 'a', 'e'] enciphering y -> n\n", "Wheels now ['a', 'b', 'f'] enciphering w -> c\n", "Wheels now ['a', 'b', 'g'] enciphering e -> s\n", "Wheels now ['a', 'b', 'h'] enciphering r -> z\n", "Wheels now ['a', 'b', 'i'] enciphering e -> x\n", "Wheels now ['a', 'b', 'j'] enciphering d -> l\n", "Wheels now ['a', 'b', 'k'] enciphering e -> t\n", "Wheels now ['a', 'b', 'l'] enciphering t -> k\n", "Wheels now ['a', 'b', 'm'] enciphering e -> z\n", "Wheels now ['a', 'b', 'n'] enciphering c -> e\n", "Wheels now ['a', 'b', 'o'] enciphering t -> b\n", "Wheels now ['a', 'b', 'p'] enciphering e -> g\n", "Wheels now ['a', 'b', 'q'] enciphering d -> h\n", "Wheels now ['a', 'b', 'r'] enciphering b -> s\n", "Wheels now ['a', 'b', 's'] enciphering y -> t\n", "Wheels now ['a', 'b', 't'] enciphering b -> g\n", "Wheels now ['a', 'b', 'u'] enciphering r -> y\n", "Wheels now ['a', 'b', 'v'] enciphering i -> w\n", "Wheels now ['a', 'b', 'w'] enciphering t -> d\n", "Wheels now ['a', 'b', 'x'] enciphering i -> m\n", "Wheels now ['a', 'b', 'y'] enciphering s -> p\n", "Wheels now ['a', 'b', 'z'] enciphering h -> r\n", "Wheels now ['a', 'b', 'a'] enciphering s -> u\n", "Wheels now ['a', 'b', 'b'] enciphering h -> c\n", "Wheels now ['a', 'b', 'c'] enciphering i -> u\n", "Wheels now ['a', 'b', 'd'] enciphering p -> z\n", "Wheels now ['a', 'b', 'e'] enciphering s -> q\n", "Wheels now ['a', 'c', 'f'] enciphering i -> d\n", "Wheels now ['a', 'c', 'g'] enciphering n -> q\n", "Wheels now ['a', 'c', 'h'] enciphering c -> z\n", "Wheels now ['a', 'c', 'i'] enciphering l -> p\n", "Wheels now ['a', 'c', 'j'] enciphering u -> v\n", "Wheels now ['a', 'c', 'k'] enciphering d -> e\n" ] }, { "data": { "text/plain": [ "('slgncszxltkzebghstgywdmprucuzqdqzpve', 'SLGNC SZXLT KZEBG HSTGY WDMPR')" ] }, "execution_count": 205, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tbt_enigma.set_wheels('a', 'a', 'a')\n", "tbt_enigma.encipher(target_pt, debug=True), target_ct" ] }, { "cell_type": "code", "execution_count": 206, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Wheels now ['a', 'a', 'b'] enciphering s -> t\n", "Wheels now ['a', 'a', 'c'] enciphering l -> h\n", "Wheels now ['a', 'a', 'd'] enciphering g -> e\n", "Wheels now ['a', 'a', 'e'] enciphering n -> y\n", "Wheels now ['a', 'b', 'f'] enciphering c -> w\n", "Wheels now ['a', 'b', 'g'] enciphering s -> e\n", "Wheels now ['a', 'b', 'h'] enciphering z -> r\n", "Wheels now ['a', 'b', 'i'] enciphering x -> e\n", "Wheels now ['a', 'b', 'j'] enciphering l -> d\n", "Wheels now ['a', 'b', 'k'] enciphering t -> e\n", "Wheels now ['a', 'b', 'l'] enciphering k -> t\n", "Wheels now ['a', 'b', 'm'] enciphering z -> e\n", "Wheels now ['a', 'b', 'n'] enciphering e -> c\n", "Wheels now ['a', 'b', 'o'] enciphering b -> t\n", "Wheels now ['a', 'b', 'p'] enciphering g -> e\n", "Wheels now ['a', 'b', 'q'] enciphering h -> d\n", "Wheels now ['a', 'b', 'r'] enciphering s -> b\n", "Wheels now ['a', 'b', 's'] enciphering t -> y\n", "Wheels now ['a', 'b', 't'] enciphering g -> b\n", "Wheels now ['a', 'b', 'u'] enciphering y -> r\n", "Wheels now ['a', 'b', 'v'] enciphering w -> i\n", "Wheels now ['a', 'b', 'w'] enciphering d -> t\n", "Wheels now ['a', 'b', 'x'] enciphering m -> i\n", "Wheels now ['a', 'b', 'y'] enciphering p -> s\n", "Wheels now ['a', 'b', 'z'] enciphering r -> h\n" ] }, { "data": { "text/plain": [ "('theyweredetectedbybritish', 'Theyw erede tecte d byBri tishs hipsi nclud')" ] }, "execution_count": 206, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tbt_enigma.set_wheels('a', 'a', 'a')\n", "tbt_enigma.encipher(target_ct, debug=True), target_pt" ] }, { "cell_type": "code", "execution_count": 207, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('theyweredetectedbybritish', 'Theyw erede tecte d byBri tishs hipsi nclud')" ] }, "execution_count": 207, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tbt_enigma.set_wheels('a', 'a', 'a')\n", "tbt_enigma.encipher(target_ct), target_pt" ] }, { "cell_type": "code", "execution_count": 208, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'mdtbjzuvielkawosqrpcghnxyf'" ] }, "execution_count": 208, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cat(tbt_enigma.plugboard.forward(l) for l in string.ascii_lowercase)" ] }, { "cell_type": "code", "execution_count": 209, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10" ] }, "execution_count": 209, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tbt_enigma.set_wheels('a', 'a', 'a')\n", "tbt_enigma.left_wheel.position" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.3" } }, "nbformat": 4, "nbformat_minor": 1 }