From: Neil Smith Date: Tue, 17 May 2016 10:58:12 +0000 (+0100) Subject: Added bombe X-Git-Url: https://git.njae.me.uk/?a=commitdiff_plain;h=628c83045960e5d94ea55fe90437b465a860d4c7;p=cipher-tools.git Added bombe --- diff --git a/bombe.ipynb b/bombe.ipynb new file mode 100644 index 0000000..ab1b457 --- /dev/null +++ b/bombe.ipynb @@ -0,0 +1,1836 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 797, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import string\n", + "import collections\n", + "import multiprocessing\n", + "import itertools\n", + "from enigma import *" + ] + }, + { + "cell_type": "code", + "execution_count": 798, + "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_pegs = ['q']\n", + "# wheel_ii_pegs = ['e']\n", + "# wheel_iii_pegs = ['v']\n", + "# wheel_iv_pegs = ['j']\n", + "# wheel_v_pegs = ['z']\n", + "# wheel_vi_pegs = ['z', 'm']\n", + "# wheel_vii_pegs = ['z', 'm']\n", + "# wheel_viii_pegs = ['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": 799, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Bank(object):\n", + " def __init__(self):\n", + " self.signals = dict(zip(string.ascii_lowercase, [False]*len(string.ascii_lowercase)))" + ] + }, + { + "cell_type": "code", + "execution_count": 800, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': False,\n", + " 'b': False,\n", + " 'c': False,\n", + " 'd': False,\n", + " 'e': False,\n", + " 'f': False,\n", + " 'g': False,\n", + " 'h': False,\n", + " 'i': False,\n", + " 'j': False,\n", + " 'k': False,\n", + " 'l': False,\n", + " 'm': False,\n", + " 'n': False,\n", + " 'o': False,\n", + " 'p': False,\n", + " 'q': False,\n", + " 'r': False,\n", + " 's': False,\n", + " 't': False,\n", + " 'u': False,\n", + " 'v': False,\n", + " 'w': False,\n", + " 'x': False,\n", + " 'y': False,\n", + " 'z': False}" + ] + }, + "execution_count": 800, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c1 = Bank()\n", + "c1.signals" + ] + }, + { + "cell_type": "code", + "execution_count": 801, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "Signal = collections.namedtuple('Signal', ['bank', 'wire'])\n", + "Connection = collections.namedtuple('Connection', ['banks', 'scrambler'])\n", + "MenuItem = collections.namedtuple('MenuIem', ['before', 'after', 'number'])" + ] + }, + { + "cell_type": "code", + "execution_count": 802, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class Scrambler(object):\n", + " def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec,\n", + " wheel1_pos='a', wheel2_pos='a', wheel3_pos='a'):\n", + " self.wheel1 = SimpleWheel(wheel1_spec, position=wheel1_pos)\n", + " self.wheel2 = SimpleWheel(wheel2_spec, position=wheel2_pos)\n", + " self.wheel3 = SimpleWheel(wheel3_spec, position=wheel3_pos)\n", + " self.reflector = Reflector(reflector_spec)\n", + " \n", + " def __getattribute__(self, name):\n", + " if name=='wheel_positions':\n", + " return self.wheel1.position, self.wheel2.position, self.wheel3.position \n", + " elif name=='wheel_positions_l':\n", + " return self.wheel1.position_l, self.wheel2.position_l, self.wheel3.position_l \n", + " else:\n", + " return object.__getattribute__(self, name)\n", + " \n", + " def advance(self, wheel1=False, wheel2=False, wheel3=True):\n", + " if wheel1: self.wheel1.advance()\n", + " if wheel2: self.wheel2.advance()\n", + " if wheel3: self.wheel3.advance()\n", + " \n", + " def lookup(self, letter):\n", + " a = self.wheel3.forward(letter)\n", + " b = self.wheel2.forward(a)\n", + " c = self.wheel1.forward(b)\n", + " d = self.reflector.forward(c)\n", + " e = self.wheel1.backward(d)\n", + " f = self.wheel2.backward(e)\n", + " g = self.wheel3.backward(f)\n", + " return g\n", + " \n", + " def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):\n", + " self.wheel1.set_position(wheel1_pos)\n", + " self.wheel2.set_position(wheel2_pos)\n", + " self.wheel3.set_position(wheel3_pos) " + ] + }, + { + "cell_type": "code", + "execution_count": 803, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "class Bombe(object):\n", + " def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec,\n", + " menu=None, start_signal=None, use_diagonal_board=True, verify_plugboard=True):\n", + " self.connections = []\n", + " self.wheel1_spec = wheel1_spec\n", + " self.wheel2_spec = wheel2_spec\n", + " self.wheel3_spec = wheel3_spec\n", + " self.reflector_spec = reflector_spec\n", + " if menu:\n", + " self.read_menu(menu)\n", + " if start_signal:\n", + " self.test_start = start_signal\n", + " self.use_diagonal_board = use_diagonal_board\n", + " self.verify_plugboard = verify_plugboard\n", + " \n", + " def __getattribute__(self, name):\n", + " if name=='wheel_positions':\n", + " return self.connections[0].scrambler.wheel_positions\n", + " elif name=='wheel_positions_l':\n", + " return self.connections[0].scrambler.wheel_positions_l\n", + " else:\n", + " return object.__getattribute__(self, name)\n", + " \n", + " def __call__(self, start_positions):\n", + " return start_positions, self.test(start_positions=start_positions, \n", + " use_diagonal_board=self.use_diagonal_board,\n", + " verify_plugboard=self.verify_plugboard)\n", + " \n", + " def add_connection(self, bank_before, bank_after, scrambler):\n", + " self.connections += [Connection([bank_before, bank_after], scrambler)]\n", + " \n", + " def read_menu(self, menu):\n", + " for item in menu:\n", + " scrambler = Scrambler(self.wheel1_spec, self.wheel2_spec, self.wheel3_spec,\n", + " self.reflector_spec,\n", + " wheel3_pos=unpos(item.number - 1))\n", + " self.add_connection(item.before, item.after, scrambler)\n", + " most_common_letter = (collections.Counter(m.before for m in menu) + \\\n", + " collections.Counter(m.after for m in menu)).most_common(1)[0][0]\n", + " self.test_start = Signal(most_common_letter, most_common_letter)\n", + " \n", + " def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos):\n", + " for i, c in enumerate(self.connections):\n", + " c.scrambler.set_positions(wheel1_pos, wheel2_pos, unpos(pos(wheel3_pos) + i))\n", + " \n", + " def test(self, initial_signal=None, start_positions=None, use_diagonal_board=True,\n", + " verify_plugboard=True):\n", + " self.banks = {label: \n", + " dict(zip(string.ascii_lowercase, [False]*len(string.ascii_lowercase)))\n", + " for label in string.ascii_lowercase}\n", + " if start_positions:\n", + " self.set_positions(*start_positions)\n", + " if not initial_signal:\n", + " initial_signal = self.test_start\n", + " self.pending = [initial_signal]\n", + " self.propagate(use_diagonal_board)\n", + " live_wire_count = len([self.banks[self.test_start.bank][w] \n", + " for w in self.banks[self.test_start.bank] \n", + " if self.banks[self.test_start.bank][w]])\n", + " if live_wire_count < 26:\n", + " if verify_plugboard:\n", + " possibles = self.possible_plugboards()\n", + " return all(s0.isdisjoint(s1) for s0 in possibles for s1 in possibles if s0 != s1)\n", + " else:\n", + " return True\n", + " else:\n", + " return False\n", + " \n", + " def propagate(self, use_diagonal_board):\n", + " while self.pending:\n", + " current = self.pending[0]\n", + " # print(\"processing\", current)\n", + " self.pending = self.pending[1:]\n", + " if not self.banks[current.bank][current.wire]:\n", + " self.banks[current.bank][current.wire] = True\n", + " if use_diagonal_board:\n", + " self.pending += [Signal(current.wire, current.bank)]\n", + " for c in self.connections:\n", + " if current.bank in c.banks:\n", + " other_bank = [b for b in c.banks if b != current.bank][0]\n", + " other_wire = c.scrambler.lookup(current.wire)\n", + " # print(\" adding\", other_bank, other_wire, \"because\", c.banks)\n", + " self.pending += [Signal(other_bank, other_wire)]\n", + " \n", + " def run(self, run_start=None, wheel1_pos='a', wheel2_pos='a', wheel3_pos='a', use_diagonal_board=True):\n", + " if not run_start:\n", + " run_start = self.test_start\n", + " self.solutions = []\n", + " self.set_positions(wheel1_pos, wheel2_pos, wheel3_pos)\n", + " for run_index in range(26*26*26):\n", + " if self.test(initial_signal=run_start, use_diagonal_board=use_diagonal_board):\n", + " self.solutions += [self.connections[0].scrambler.wheel_positions_l]\n", + " advance3 = True\n", + " advance2 = False\n", + " advance1 = False\n", + " if (run_index + 1) % 26 == 0: advance2 = True\n", + " if (run_index + 1) % (26*26) == 0: advance1 = True\n", + " for c in self.connections:\n", + " c.scrambler.advance(advance1, advance2, advance3)\n", + " return self.solutions\n", + " \n", + " def possible_plugboards(self):\n", + " possibles = set()\n", + " for b in self.banks:\n", + " active = [w for w in self.banks[b] if self.banks[b][w]]\n", + " inactive = [w for w in self.banks[b] if not self.banks[b][w]]\n", + " if len(active) == 1:\n", + " possibles = possibles.union({frozenset((b, active[0]))})\n", + " if len(inactive) == 1:\n", + " possibles = possibles.union({frozenset((b, inactive[0]))})\n", + " return possibles" + ] + }, + { + "cell_type": "code", + "execution_count": 804, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "bombe = Bombe(wheel_i_spec, wheel_ii_spec, wheel_iii_spec, reflector_b_spec)\n", + "# len(bombe.banks), bombe.banks['a'] == bombe.banks['b']" + ] + }, + { + "cell_type": "code", + "execution_count": 805, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "test_enigma = Enigma(reflector_b_spec, \n", + " wheel_i_spec, wheel_i_pegs,\n", + " wheel_ii_spec, wheel_ii_pegs,\n", + " wheel_iii_spec, wheel_iii_pegs,\n", + " 1, 1, 1,\n", + " '')" + ] + }, + { + "cell_type": "code", + "execution_count": 806, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'opgndxcrwomnlnecjz'" + ] + }, + "execution_count": 806, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test_enigma.set_wheels('a', 'a', 'a')\n", + "pt = 'thisisatestmessage'\n", + "ct = test_enigma.encipher(pt)\n", + "ct" + ] + }, + { + "cell_type": "code", + "execution_count": 807, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'aas'" + ] + }, + "execution_count": 807, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cat(test_enigma.wheel_positions_l)" + ] + }, + { + "cell_type": "code", + "execution_count": 808, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[MenuIem(before='t', after='o', number=1),\n", + " MenuIem(before='h', after='p', number=2),\n", + " MenuIem(before='i', after='g', number=3),\n", + " MenuIem(before='s', after='n', number=4),\n", + " MenuIem(before='i', after='d', number=5),\n", + " MenuIem(before='s', after='x', number=6),\n", + " MenuIem(before='a', after='c', number=7),\n", + " MenuIem(before='t', after='r', number=8),\n", + " MenuIem(before='e', after='w', number=9),\n", + " MenuIem(before='s', after='o', number=10),\n", + " MenuIem(before='t', after='m', number=11),\n", + " MenuIem(before='m', after='n', number=12),\n", + " MenuIem(before='e', after='l', number=13),\n", + " MenuIem(before='s', after='n', number=14),\n", + " MenuIem(before='s', after='e', number=15),\n", + " MenuIem(before='a', after='c', number=16),\n", + " MenuIem(before='g', after='j', number=17),\n", + " MenuIem(before='e', after='z', number=18)]" + ] + }, + "execution_count": 808, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "menu = [MenuItem(p, c, i+1) for i, (p, c) in enumerate(zip(pt, ct))]\n", + "menu" + ] + }, + { + "cell_type": "code", + "execution_count": 809, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def make_menu(plaintext, ciphertext):\n", + " return [MenuItem(p, c, i+1) \n", + " for i, (p, c) in enumerate(zip(plaintext, ciphertext))]" + ] + }, + { + "cell_type": "code", + "execution_count": 810, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[MenuIem(before='t', after='o', number=1),\n", + " MenuIem(before='h', after='p', number=2),\n", + " MenuIem(before='i', after='g', number=3),\n", + " MenuIem(before='s', after='n', number=4),\n", + " MenuIem(before='i', after='d', number=5),\n", + " MenuIem(before='s', after='x', number=6),\n", + " MenuIem(before='a', after='c', number=7),\n", + " MenuIem(before='t', after='r', number=8),\n", + " MenuIem(before='e', after='w', number=9),\n", + " MenuIem(before='s', after='o', number=10),\n", + " MenuIem(before='t', after='m', number=11),\n", + " MenuIem(before='m', after='n', number=12),\n", + " MenuIem(before='e', after='l', number=13),\n", + " MenuIem(before='s', after='n', number=14),\n", + " MenuIem(before='s', after='e', number=15),\n", + " MenuIem(before='a', after='c', number=16),\n", + " MenuIem(before='g', after='j', number=17),\n", + " MenuIem(before='e', after='z', number=18)]" + ] + }, + "execution_count": 810, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "make_menu(pt, ct)" + ] + }, + { + "cell_type": "code", + "execution_count": 811, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'s'" + ] + }, + "execution_count": 811, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(collections.Counter(m.before for m in menu) + collections.Counter(m.after for m in menu)).most_common(1)[0][0]" + ] + }, + { + "cell_type": "code", + "execution_count": 812, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "bombe.read_menu(menu)" + ] + }, + { + "cell_type": "code", + "execution_count": 813, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "18" + ] + }, + "execution_count": 813, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(bombe.connections)" + ] + }, + { + "cell_type": "code", + "execution_count": 814, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['t', 'o'] aaa\n", + "['h', 'p'] aab\n", + "['i', 'g'] aac\n", + "['s', 'n'] aad\n", + "['i', 'd'] aae\n", + "['s', 'x'] aaf\n", + "['a', 'c'] aag\n", + "['t', 'r'] aah\n", + "['e', 'w'] aai\n", + "['s', 'o'] aaj\n", + "['t', 'm'] aak\n", + "['m', 'n'] aal\n", + "['e', 'l'] aam\n", + "['s', 'n'] aan\n", + "['s', 'e'] aao\n", + "['a', 'c'] aap\n", + "['g', 'j'] aaq\n", + "['e', 'z'] aar\n" + ] + } + ], + "source": [ + "for c in bombe.connections:\n", + " print(c.banks, cat(c.scrambler.wheel_positions_l))" + ] + }, + { + "cell_type": "code", + "execution_count": 815, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 815, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bombe.test(Signal('t', 't'))" + ] + }, + { + "cell_type": "code", + "execution_count": 816, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'a': True,\n", + " 'b': True,\n", + " 'c': True,\n", + " 'd': True,\n", + " 'e': True,\n", + " 'f': True,\n", + " 'g': True,\n", + " 'h': True,\n", + " 'i': True,\n", + " 'j': True,\n", + " 'k': True,\n", + " 'l': True,\n", + " 'm': True,\n", + " 'n': True,\n", + " 'o': True,\n", + " 'p': True,\n", + " 'q': True,\n", + " 'r': True,\n", + " 's': True,\n", + " 't': True,\n", + " 'u': True,\n", + " 'v': True,\n", + " 'w': True,\n", + " 'x': True,\n", + " 'y': True,\n", + " 'z': True}" + ] + }, + "execution_count": 816, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bombe.banks['t']" + ] + }, + { + "cell_type": "code", + "execution_count": 817, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "b : AbCDEfGHIJkLMNOPqRSTuvWXyZ\n", + "c : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "d : ABCDEFGHIJKLMNOPQRSTUVWXyZ\n", + "e : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "f : AbCDEfGHIJkLMNOPqRSTuvWXyZ\n", + "g : ABCDEFGHIJKLMNOPQRSTuVWXYZ\n", + "h : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "i : ABCDEFGHIJKLMNOPQRSTUvWXYZ\n", + "j : ABCDEFGHIjKLMNOPQRSTUVWXYZ\n", + "k : AbCDEfGHIJkLMNOPqRSTuvWXyZ\n", + "l : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "m : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "n : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "o : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "p : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "q : AbCDEfGHIJkLMNOPqRSTuvWXyZ\n", + "r : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "s : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "t : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "u : AbCDEfgHIJkLMNOPqRSTuvWXyZ\n", + "v : AbCDEfGHiJkLMNOPqRSTuvWXyZ\n", + "w : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "x : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", + "y : AbCdEfGHIJkLMNOPqRSTuvWXyZ\n", + "z : ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" + ] + } + ], + "source": [ + "for b in sorted(bombe.banks):\n", + " print(b, ': ', end='')\n", + " for w in sorted(bombe.banks[b]):\n", + " if bombe.banks[b][w]:\n", + " print(w.upper(), end='')\n", + " else:\n", + " print(w, end='')\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": 818, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('a', 'a', 'a')" + ] + }, + "execution_count": 818, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bombe.wheel_positions_l" + ] + }, + { + "cell_type": "code", + "execution_count": 819, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['t', 'o'] pqr\n", + "['h', 'p'] pqs\n", + "['i', 'g'] pqt\n", + "['s', 'n'] pqu\n", + "['i', 'd'] pqv\n", + "['s', 'x'] pqw\n", + "['a', 'c'] pqx\n", + "['t', 'r'] pqy\n", + "['e', 'w'] pqz\n", + "['s', 'o'] pqa\n", + "['t', 'm'] pqb\n", + "['m', 'n'] pqc\n", + "['e', 'l'] pqd\n", + "['s', 'n'] pqe\n", + "['s', 'e'] pqf\n", + "['a', 'c'] pqg\n", + "['g', 'j'] pqh\n", + "['e', 'z'] pqi\n" + ] + } + ], + "source": [ + "bombe.set_positions('p', 'q', 'r')\n", + "for c in bombe.connections:\n", + " print(c.banks, cat(c.scrambler.wheel_positions_l))" + ] + }, + { + "cell_type": "code", + "execution_count": 820, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# bombe.run()\n", + "# print('x')" + ] + }, + { + "cell_type": "code", + "execution_count": 821, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a : .b.defghijklmnopqrstuvwxyz\n", + "b : a.cde.ghij.lmnop.rst..wx.z\n", + "c : .b.defghijklmnopqrstuvwxyz\n", + "d : abc.e.gh.jklmnopqrstuvwxyz\n", + "e : abcd.fghijklmnopqrstuvwxyz\n", + "f : a.c.e.g.ij.lmno..rst..wx.z\n", + "g : abcdef.h..klmnopqrstuvwxyz\n", + "h : abcde.g.ij.lmno.qrst..wxyz\n", + "i : abc.ef.h.jklmnopqrstuvwxyz\n", + "j : abcdef.hi.klmnopqrstuvwx.z\n", + "k : a.cde.g.ij.lmno..rst..wx.z\n", + "l : abcdefghijk.mnopqrstuvwxyz\n", + "m : abcdefghijkl.nopqrstuvwxyz\n", + "n : abcdefghijklm.opqrstuvwxyz\n", + "o : abcdefghijklmn.pqrstuvwxyz\n", + "p : abcde.g.ij.lmno.qrst..wxyz\n", + "q : a.cde.ghij.lmnop.rst..wx.z\n", + "r : abcdefghijklmnopq.stuvwxyz\n", + "s : abcdefghijklmnopqr.tuvwxyz\n", + "t : abcdefghijklmnopqrs.uvwxyz\n", + "u : a.cde.g.ij.lmno..rst..wx.z\n", + "v : a.cde.g.ij.lmno..rst..wx.z\n", + "w : abcdefghijklmnopqrstuv.xyz\n", + "x : abcdefghijklmnopqrstuvw.yz\n", + "y : a.cde.ghi..lmnop.rst..wx.z\n", + "z : abcdefghijklmnopqrstuvwxy.\n" + ] + } + ], + "source": [ + "bombe.set_positions('a', 'a', 'b')\n", + "bombe.test(Signal('s', 'a'))\n", + "\n", + "for b in sorted(bombe.banks):\n", + " print(b, ': ', end='')\n", + " for w in sorted(bombe.banks[b]):\n", + " if bombe.banks[b][w]:\n", + " print(w, end='')\n", + " else:\n", + " print('.', end='')\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": 822, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a : ..........................\n", + "b : ..........................\n", + "c : ..........................\n", + "d : ..........................\n", + "e : ....e.....................\n", + "f : ..........................\n", + "g : ..........................\n", + "h : ..........................\n", + "i : ..........................\n", + "j : ..........................\n", + "k : ..........................\n", + "l : ...........l..............\n", + "m : ............m.............\n", + "n : .............n............\n", + "o : ..............o...........\n", + "p : ..........................\n", + "q : ..........................\n", + "r : .................r........\n", + "s : ..................s.......\n", + "t : ...................t......\n", + "u : ..........................\n", + "v : ..........................\n", + "w : ......................w...\n", + "x : .......................x..\n", + "y : ..........................\n", + "z : .........................z\n" + ] + } + ], + "source": [ + "bombe.set_positions('a', 'a', 'b')\n", + "bombe.test()\n", + "\n", + "for b in sorted(bombe.banks):\n", + " print(b, ': ', end='')\n", + " for w in sorted(bombe.banks[b]):\n", + " if bombe.banks[b][w]:\n", + " print(w, end='')\n", + " else:\n", + " print('.', end='')\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": 823, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 823, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len([bombe.banks['t'][w] for w in bombe.banks['t'] if bombe.banks['t'][w]])" + ] + }, + { + "cell_type": "code", + "execution_count": 824, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# %%timeit\n", + "# results = bombe.run()\n", + "# print(len(results), ('a', 'a', 'b') in results)" + ] + }, + { + "cell_type": "code", + "execution_count": 825, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# %%timeit\n", + "# results = bombe.run(use_diagonal_board=False)\n", + "# print(len(results), ('a', 'a', 'b') in results)" + ] + }, + { + "cell_type": "code", + "execution_count": 826, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('a', 'a', 'b')" + ] + }, + "execution_count": 826, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bombe.wheel_positions_l" + ] + }, + { + "cell_type": "code", + "execution_count": 827, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 827, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bombe.test(Signal('t', 't'), ('p', 'p', 'p'))" + ] + }, + { + "cell_type": "code", + "execution_count": 828, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('p', 'p', 'p')" + ] + }, + "execution_count": 828, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "bombe.wheel_positions_l" + ] + }, + { + "cell_type": "code", + "execution_count": 829, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a : abcdefghijklmnop.rst.vwxyz\n", + "b : a.cde.g.ij.lmno..rst..wx.z\n", + "c : abcdefghijklmnop.rst.vwxyz\n", + "d : abcdefghijklmnopqrstuvwxyz\n", + "e : abcdefghijklmnopqrstuvwxyz\n", + "f : a.cde.g.ij.lmno..rst..wx.z\n", + "g : abcdefghijklmnopqrstuvwxyz\n", + "h : a.cde.ghijklmnop.rst.vwxyz\n", + "i : abcdefghijklmnopqrstuvwxyz\n", + "j : abcdefghijklmnopqrstuvwxyz\n", + "k : a.cde.ghij.lmnop.rst..wx.z\n", + "l : abcdefghijklmnopqrstuvwxyz\n", + "m : abcdefghijklmnopqrstuvwxyz\n", + "n : abcdefghijklmnopqrstuvwxyz\n", + "o : abcdefghijklmnopqrstuvwxyz\n", + "p : a.cde.ghijklmnop.rst.vwxyz\n", + "q : ...de.g.ij.lmno..rst..wx.z\n", + "r : abcdefghijklmnopqrstuvwxyz\n", + "s : abcdefghijklmnopqrstuvwxyz\n", + "t : abcdefghijklmnopqrstuvwxyz\n", + "u : ...de.g.ij.lmno..rst..wx.z\n", + "v : a.cde.ghij.lmnop.rst..wx.z\n", + "w : abcdefghijklmnopqrstuvwxyz\n", + "x : abcdefghijklmnopqrstuvwxyz\n", + "y : a.cde.ghij.lmnop.rst..wx.z\n", + "z : abcdefghijklmnopqrstuvwxyz\n" + ] + } + ], + "source": [ + "for b in sorted(bombe.banks):\n", + " print(b, ': ', end='')\n", + " for w in sorted(bombe.banks[b]):\n", + " if bombe.banks[b][w]:\n", + " print(w, end='')\n", + " else:\n", + " print('.', end='')\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": 830, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "17576" + ] + }, + "execution_count": 830, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "allwheels = itertools.product(string.ascii_lowercase, repeat=3)\n", + "len(list(allwheels))" + ] + }, + { + "cell_type": "code", + "execution_count": 831, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(('a', 'a', 'b'), True)" + ] + }, + "execution_count": 831, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b = Bombe(wheel_i_spec, wheel_ii_spec, wheel_iii_spec, reflector_b_spec, menu=menu)\n", + "b(('a', 'a', 'b'))" + ] + }, + { + "cell_type": "code", + "execution_count": 832, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "b = Bombe(wheel_i_spec, wheel_ii_spec, wheel_iii_spec, reflector_b_spec, menu=menu)(('a', 'a', 'b'))" + ] + }, + { + "cell_type": "code", + "execution_count": 833, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 'a', 'b')]" + ] + }, + "execution_count": 833, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "allwheels = itertools.product(string.ascii_lowercase, repeat=3)\n", + "\n", + "with multiprocessing.Pool() as pool:\n", + " res = pool.map(Bombe(wheel_i_spec, wheel_ii_spec, wheel_iii_spec, reflector_b_spec, menu=menu),\n", + " allwheels)\n", + "[r[0] for r in res if r[1]]" + ] + }, + { + "cell_type": "code", + "execution_count": 857, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def run_multi_bombe(wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu,\n", + " start_signal=None, use_diagonal_board=True, \n", + " verify_plugboard=True):\n", + " allwheels = itertools.product(string.ascii_lowercase, repeat=3)\n", + "\n", + " with multiprocessing.Pool() as pool:\n", + " res = pool.map(Bombe(wheel1_spec, wheel2_spec, wheel3_spec, \n", + " reflector_spec, menu=menu, start_signal=start_signal, \n", + " use_diagonal_board=use_diagonal_board, \n", + " verify_plugboard=verify_plugboard),\n", + " allwheels)\n", + " return [r[0] for r in res if r[1]]" + ] + }, + { + "cell_type": "code", + "execution_count": 835, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 'a', 'b')]" + ] + }, + "execution_count": 835, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[r[0] for r in res if r[1]]" + ] + }, + { + "cell_type": "code", + "execution_count": 836, + "metadata": { + "collapsed": false + }, + "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_pegs,\n", + " wheel_v_spec, wheel_v_pegs,\n", + " wheel_iii_spec, wheel_iii_pegs,\n", + " 6, 20, 24,\n", + " 'ua pf rq so ni ey bg hl tx zj')" + ] + }, + { + "cell_type": "code", + "execution_count": 837, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('e', 'l', 'e')" + ] + }, + "execution_count": 837, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_enigma.set_wheels('j', 'e', 'b')\n", + "tuple(unpos(p) for p in w_enigma.wheel_positions)" + ] + }, + { + "cell_type": "code", + "execution_count": 838, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'dhnpforeeimgg'" + ] + }, + "execution_count": 838, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_enigma.set_wheels('j', 'e', 'b')\n", + "pt = 'someplaintext'\n", + "ct = w_enigma.encipher(pt)\n", + "ct" + ] + }, + { + "cell_type": "code", + "execution_count": 839, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "('j', 'e', 'o')" + ] + }, + "execution_count": 839, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_enigma.wheel_positions_l" + ] + }, + { + "cell_type": "code", + "execution_count": 840, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[MenuIem(before='s', after='d', number=1),\n", + " MenuIem(before='o', after='h', number=2),\n", + " MenuIem(before='m', after='n', number=3),\n", + " MenuIem(before='e', after='p', number=4),\n", + " MenuIem(before='p', after='f', number=5),\n", + " MenuIem(before='l', after='o', number=6),\n", + " MenuIem(before='a', after='r', number=7),\n", + " MenuIem(before='i', after='e', number=8),\n", + " MenuIem(before='n', after='e', number=9),\n", + " MenuIem(before='t', after='i', number=10),\n", + " MenuIem(before='e', after='m', number=11),\n", + " MenuIem(before='x', after='g', number=12),\n", + " MenuIem(before='t', after='g', number=13)]" + ] + }, + "execution_count": 840, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_menu = [MenuItem(p, c, i+1) for i, (p, c) in enumerate(zip(pt, ct))]\n", + "w_menu" + ] + }, + { + "cell_type": "code", + "execution_count": 841, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('a', 'y', 'm'),\n", + " ('c', 'p', 'v'),\n", + " ('c', 's', 'f'),\n", + " ('c', 'w', 'j'),\n", + " ('d', 'r', 'k'),\n", + " ('e', 'l', 'f'),\n", + " ('e', 's', 'v'),\n", + " ('e', 'y', 'd'),\n", + " ('e', 'y', 'o'),\n", + " ('f', 'z', 'x'),\n", + " ('g', 'b', 'l'),\n", + " ('g', 'c', 'd'),\n", + " ('g', 'c', 'f'),\n", + " ('g', 'j', 'p'),\n", + " ('h', 'c', 'i'),\n", + " ('h', 'm', 'w'),\n", + " ('h', 'o', 'd'),\n", + " ('h', 'p', 'b'),\n", + " ('h', 's', 't'),\n", + " ('i', 'b', 's'),\n", + " ('i', 'v', 'b'),\n", + " ('j', 'y', 'u'),\n", + " ('k', 'b', 'x'),\n", + " ('k', 'f', 't'),\n", + " ('k', 'l', 'e'),\n", + " ('k', 'l', 'm'),\n", + " ('k', 'r', 'z'),\n", + " ('k', 's', 'p'),\n", + " ('l', 'd', 'z'),\n", + " ('l', 'i', 'y'),\n", + " ('l', 'y', 'f'),\n", + " ('m', 'b', 'h'),\n", + " ('m', 'p', 'l'),\n", + " ('n', 'l', 'r'),\n", + " ('o', 'k', 'x'),\n", + " ('p', 'a', 'g'),\n", + " ('p', 'c', 'v'),\n", + " ('p', 'f', 'o'),\n", + " ('p', 'm', 'i'),\n", + " ('p', 'x', 'n'),\n", + " ('p', 'x', 'p'),\n", + " ('q', 'q', 'n'),\n", + " ('q', 'r', 'w'),\n", + " ('q', 'v', 'l'),\n", + " ('q', 'x', 't'),\n", + " ('s', 'a', 'h'),\n", + " ('s', 'h', 'v'),\n", + " ('s', 'l', 'p'),\n", + " ('s', 'l', 's'),\n", + " ('u', 'r', 'h'),\n", + " ('v', 'v', 'v'),\n", + " ('v', 'x', 'a'),\n", + " ('w', 'j', 'z'),\n", + " ('w', 'k', 'u'),\n", + " ('x', 'f', 'p'),\n", + " ('x', 'j', 'n'),\n", + " ('x', 'o', 'q'),\n", + " ('x', 'x', 'x'),\n", + " ('y', 'n', 'c'),\n", + " ('y', 'r', 'f'),\n", + " ('z', 't', 'y'),\n", + " ('z', 'z', 'k')]" + ] + }, + "execution_count": 841, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "allwheels = itertools.product(string.ascii_lowercase, repeat=3)\n", + "\n", + "with multiprocessing.Pool() as pool:\n", + " res = pool.map(Bombe(wheel_i_spec, wheel_v_spec, wheel_iii_spec, reflector_b_spec, \n", + " menu=w_menu, verify_plugboard=False),\n", + " allwheels)\n", + "[r[0] for r in res if r[1]]" + ] + }, + { + "cell_type": "code", + "execution_count": 842, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "62" + ] + }, + "execution_count": 842, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len([r[0] for r in res if r[1]])" + ] + }, + { + "cell_type": "code", + "execution_count": 843, + "metadata": { + "collapsed": false, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('c', 'p', 'v'),\n", + " ('c', 's', 'f'),\n", + " ('e', 'l', 'f'),\n", + " ('g', 'c', 'f'),\n", + " ('j', 'y', 'u'),\n", + " ('o', 'k', 'x'),\n", + " ('p', 'a', 'g'),\n", + " ('q', 'q', 'n'),\n", + " ('q', 'v', 'l'),\n", + " ('q', 'x', 't'),\n", + " ('s', 'l', 'p'),\n", + " ('u', 'r', 'h'),\n", + " ('y', 'n', 'c')]" + ] + }, + "execution_count": 843, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "allwheels = itertools.product(string.ascii_lowercase, repeat=3)\n", + "\n", + "with multiprocessing.Pool() as pool:\n", + " res = pool.map(Bombe(wheel_i_spec, wheel_v_spec, wheel_iii_spec, reflector_b_spec, \n", + " menu=w_menu, verify_plugboard=True),\n", + " allwheels)\n", + "[r[0] for r in res if r[1]]" + ] + }, + { + "cell_type": "code", + "execution_count": 858, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[('c', 'p', 'v'),\n", + " ('c', 's', 'f'),\n", + " ('e', 'l', 'f'),\n", + " ('g', 'c', 'f'),\n", + " ('j', 'y', 'u'),\n", + " ('o', 'k', 'x'),\n", + " ('p', 'a', 'g'),\n", + " ('q', 'q', 'n'),\n", + " ('q', 'v', 'l'),\n", + " ('q', 'x', 't'),\n", + " ('s', 'l', 'p'),\n", + " ('u', 'r', 'h'),\n", + " ('y', 'n', 'c')]" + ] + }, + "execution_count": 858, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "run_multi_bombe(wheel_i_spec, wheel_v_spec, wheel_iii_spec, reflector_b_spec, w_menu)" + ] + }, + { + "cell_type": "code", + "execution_count": 844, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "# allwheels = itertools.product(string.ascii_lowercase, repeat=3)\n", + "\n", + "# with multiprocessing.Pool() as pool:\n", + "# res = pool.map(Bombe(wheel_i_spec, wheel_v_spec, wheel_iii_spec, reflector_b_spec, \n", + "# menu=w_menu, start_signal=Signal('t', 'x')),\n", + "# allwheels)\n", + "# [r[0] for r in res if r[1]]" + ] + }, + { + "cell_type": "code", + "execution_count": 845, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "13" + ] + }, + "execution_count": 845, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len([r[0] for r in res if r[1]])" + ] + }, + { + "cell_type": "code", + "execution_count": 846, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [ + "w_bombe = Bombe(wheel_i_spec, wheel_v_spec, wheel_iii_spec, reflector_b_spec, \n", + " menu=w_menu)" + ] + }, + { + "cell_type": "code", + "execution_count": 847, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Signal(bank='e', wire='e')" + ] + }, + "execution_count": 847, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_bombe.test_start" + ] + }, + { + "cell_type": "code", + "execution_count": 848, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 848, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_bombe.test(start_positions=('e', 'l', 'f'))" + ] + }, + { + "cell_type": "code", + "execution_count": 849, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 849, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "w_bombe.test(Signal('t', 'x'), ('e', 'l', 'f'))" + ] + }, + { + "cell_type": "code", + "execution_count": 850, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "a : a.cdefghi..l.nopqrst.vwx..\n", + "b : ....efghi...mnop.......x..\n", + "c : a....fg.i..lmn.p.r.t...x..\n", + "d : a...efghij.lmn...rstu..xyz\n", + "e : ab.defghijklmnopqrstuvwxyz\n", + "f : abcde.ghijklmnopqrstuvwxyz\n", + "g : abcdef.hijklmnopqrstuvwxyz\n", + "h : ab.defghi...mnopq.stuvwx..\n", + "i : abcdefgh.jklmnopqrstuvwxyz\n", + "j : ...defg.i...mn.p...t...x..\n", + "k : ....efg.i..lmnop.r.t...x..\n", + "l : a.cdefg.i.k.mnopqr.t..wxyz\n", + "m : .bcdefghijklmnopqrstuvwxyz\n", + "n : abcdefghijklmnopqr.tuvwxyz\n", + "o : ab..efghi.klmnopqr.t...xyz\n", + "p : abc.efghijklmnopqrstuvwxyz\n", + "q : a...efghi..lmnop.r.t...x..\n", + "r : a.cdefg.i.klmnopqrst..w..z\n", + "s : a..defghi...m..p.rstuv.xyz\n", + "t : a.cdefghijklmnopqrstuvwxyz\n", + "u : ...defghi...mn.p..st...x..\n", + "v : a...efghi...mn.p..st...x..\n", + "w : a...efghi..lmn.p.r.t...x..\n", + "x : abcdefghijklmnopq.stuvwxyz\n", + "y : ...defg.i..lmnop..st...x..\n", + "z : ...defg.i..lmnop.rst...x..\n" + ] + } + ], + "source": [ + "r = w_bombe.test(start_positions=('c', 'p', 'v'))\n", + "print(r)\n", + "for b in sorted(w_bombe.banks):\n", + " print(b, ': ', end='')\n", + " for w in sorted(w_bombe.banks[b]):\n", + " if w_bombe.banks[b][w]:\n", + " print(w, end='')\n", + " else:\n", + " print('.', end='')\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": 851, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "a : abcdefghi..lmnop.rst.v.x.z\n", + "b : a...ef.hi..lmnop.r.t...x..\n", + "c : a..defg.i..lmn.p.rst...x..\n", + "d : a.c.efghi.klmnopqrstu.wxyz\n", + "e : abcdefghijklmnopqrstuvwx.z\n", + "f : abcdefghijklmno.qrstuvwxyz\n", + "g : a.cdefghijklmnopqrstuvwxyz\n", + "h : ab.defghi...mnopqrstuvwxyz\n", + "i : abcdefghijklm.opqrstuvwxyz\n", + "j : ....efg.i..lmnop...t...x..\n", + "k : ...defg.i..lmn.p..st...x..\n", + "l : abcdefg.ijklmnopqrstuv.x..\n", + "m : abcdefghijkl.nopqrstuvwxyz\n", + "n : abcdefgh.jklmnopqrstuvwxyz\n", + "o : ab.defghij.lmnop.r.tuvwxyz\n", + "p : abcde.ghijklmnopqrstuvwxyz\n", + "q : ...defghi..lmn.p..st...x..\n", + "r : abcdefghi..lmnop.rst.v.x.z\n", + "s : a.cdefghi.klmn.pqr.tuvwxyz\n", + "t : abcdefghijklmnopqrstuvw.yz\n", + "u : ...defghi..lmnop..st...x..\n", + "v : a...efghi..lmnop.rst...x..\n", + "w : ...defghi...mnop..st...x..\n", + "x : abcdefghijklmnopqrs.uvwxyz\n", + "y : ...d.fghi...mnop..st...x..\n", + "z : a..defghi...mnop.rst...x..\n" + ] + } + ], + "source": [ + "r = w_bombe.test(start_positions=('e', 'l', 'f'))\n", + "print(r)\n", + "for b in sorted(w_bombe.banks):\n", + " print(b, ': ', end='')\n", + " for w in sorted(w_bombe.banks[b]):\n", + " if w_bombe.banks[b][w]:\n", + " print(w, end='')\n", + " else:\n", + " print('.', end='')\n", + " print('')" + ] + }, + { + "cell_type": "code", + "execution_count": 852, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{frozenset({'e', 'y'}),\n", + " frozenset({'t', 'x'}),\n", + " frozenset({'i', 'n'}),\n", + " frozenset({'m'}),\n", + " frozenset({'b', 'g'}),\n", + " frozenset({'f', 'p'})}" + ] + }, + "execution_count": 852, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ps = w_bombe.possible_plugboards()\n", + "ps" + ] + }, + { + "cell_type": "code", + "execution_count": 853, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 853, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all(s0.isdisjoint(s1) for s0 in ps for s1 in ps if s0 != s1)" + ] + }, + { + "cell_type": "code", + "execution_count": 854, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "({frozenset({1, 2}), frozenset({2, 3}), frozenset({3, 4})},\n", + " frozenset({1, 2}),\n", + " frozenset({3, 4}),\n", + " frozenset({2, 3}))" + ] + }, + "execution_count": 854, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "s = set()\n", + "f1 = frozenset((1, 2))\n", + "f2 = frozenset((3, 4))\n", + "f3 = frozenset((2, 3))\n", + "s = s.union({f1})\n", + "s = s.union({f2})\n", + "s = s.union({f1})\n", + "s = s.union({f3})\n", + "s, f1, f2, f3" + ] + }, + { + "cell_type": "code", + "execution_count": 855, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 855, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all(s0.isdisjoint(s1) for s0 in s for s1 in s if s0 != s1)" + ] + }, + { + "cell_type": "code", + "execution_count": 856, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 856, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{1, 2}.isdisjoint({1, 6})" + ] + }, + { + "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.1+" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/enigma.py b/enigma.py index 1150763..72f4807 100644 --- a/enigma.py +++ b/enigma.py @@ -1,7 +1,9 @@ # coding: utf-8 +################################## # # Enigma machine +################################## # Specification from [Codes and Ciphers](http://www.codesandciphers.org.uk/enigma/rotorspec.htm) page. # # 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). @@ -12,6 +14,8 @@ import string import collections +import multiprocessing +import itertools # Some convenience functions @@ -278,7 +282,6 @@ class SimpleWheel(LetterTransformer): def advance(self): self.position = (self.position + 1) % 26 - # return self.position @@ -429,16 +432,11 @@ class Wheel(SimpleWheel): def set_position(self, position): self.position = (pos(position) - self.ring_setting + 1) % 26 - # self.position_l = position self.peg_positions = [(pos(p) - pos(position)) % 26 for p in self.ring_peg_letters] def advance(self): super(Wheel, self).advance() self.peg_positions = [(p - 1) % 26 for p in self.peg_positions] - # self.position_l = unpos(self.position + self.ring_setting - 1) - # return self.position - - class Enigma(object): @@ -912,20 +910,20 @@ class Enigma(object): 'wenpbqrouxlkychdfgzvitajms' - >>> enigma31.set_wheels('i', 'd', 'z') + >>> enigma31.set_wheels('i', 'z', 'd') >>> enigma31.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure') - 'gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz' + 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki' >>> enigma31.wheel_positions - (3, 12, 6) + (4, 9, 10) >>> cat(enigma31.wheel_positions_l) - 'ifd' + 'jch' >>> enigma31.peg_positions - ([8], [20], [18]) + ([7], [23], [14]) >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'urygzpdmxtwshqvfnbljaokice' + 'mopnigfuesqwadbcktjrhylzvx' - >>> enigma31.set_wheels('i', 'd', 'z') - >>> enigma31.decipher('gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz') + >>> enigma31.set_wheels('i', 'z', 'd') + >>> enigma31.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') 'verylongtestmessagewithanextrabitofmessageforgoodmeasure' """ def __init__(self, reflector_spec, @@ -1002,6 +1000,186 @@ class Enigma(object): # print() +################################## +# # Bombe +################################## + +Signal = collections.namedtuple('Signal', ['bank', 'wire']) +Connection = collections.namedtuple('Connection', ['banks', 'scrambler']) +MenuItem = collections.namedtuple('MenuIem', ['before', 'after', 'number']) + + +class Scrambler(object): + def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, + wheel1_pos='a', wheel2_pos='a', wheel3_pos='a'): + self.wheel1 = SimpleWheel(wheel1_spec, position=wheel1_pos) + self.wheel2 = SimpleWheel(wheel2_spec, position=wheel2_pos) + self.wheel3 = SimpleWheel(wheel3_spec, position=wheel3_pos) + self.reflector = Reflector(reflector_spec) + + def __getattribute__(self, name): + if name=='wheel_positions': + return self.wheel1.position, self.wheel2.position, self.wheel3.position + elif name=='wheel_positions_l': + return self.wheel1.position_l, self.wheel2.position_l, self.wheel3.position_l + else: + return object.__getattribute__(self, name) + + def advance(self, wheel1=False, wheel2=False, wheel3=True): + if wheel1: self.wheel1.advance() + if wheel2: self.wheel2.advance() + if wheel3: self.wheel3.advance() + + def lookup(self, letter): + a = self.wheel3.forward(letter) + b = self.wheel2.forward(a) + c = self.wheel1.forward(b) + d = self.reflector.forward(c) + e = self.wheel1.backward(d) + f = self.wheel2.backward(e) + g = self.wheel3.backward(f) + return g + + def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos): + self.wheel1.set_position(wheel1_pos) + self.wheel2.set_position(wheel2_pos) + self.wheel3.set_position(wheel3_pos) + + +class Bombe(object): + def __init__(self, wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, + menu=None, start_signal=None, use_diagonal_board=True, + verify_plugboard=True): + self.connections = [] + self.wheel1_spec = wheel1_spec + self.wheel2_spec = wheel2_spec + self.wheel3_spec = wheel3_spec + self.reflector_spec = reflector_spec + if menu: + self.read_menu(menu) + if start_signal: + self.test_start = start_signal + self.use_diagonal_board = use_diagonal_board + self.verify_plugboard = verify_plugboard + + def __getattribute__(self, name): + if name=='wheel_positions': + return self.connections[0].scrambler.wheel_positions + elif name=='wheel_positions_l': + return self.connections[0].scrambler.wheel_positions_l + else: + return object.__getattribute__(self, name) + + def __call__(self, start_positions): + return start_positions, self.test(initial_signal=self.test_start, + start_positions=start_positions, + use_diagonal_board=self.use_diagonal_board, + verify_plugboard=self.verify_plugboard) + + def add_connection(self, bank_before, bank_after, scrambler): + self.connections += [Connection([bank_before, bank_after], scrambler)] + + def read_menu(self, menu): + for item in menu: + scrambler = Scrambler(self.wheel1_spec, self.wheel2_spec, self.wheel3_spec, + self.reflector_spec, + wheel3_pos=unpos(item.number - 1)) + self.add_connection(item.before, item.after, scrambler) + most_common_letter = (collections.Counter(m.before for m in menu) + \ + collections.Counter(m.after for m in menu)).most_common(1)[0][0] + self.test_start = Signal(most_common_letter, most_common_letter) + + def set_positions(self, wheel1_pos, wheel2_pos, wheel3_pos): + for i, c in enumerate(self.connections): + c.scrambler.set_positions(wheel1_pos, wheel2_pos, unpos(pos(wheel3_pos) + i)) + + def test(self, initial_signal=None, start_positions=None, use_diagonal_board=True, + verify_plugboard=True): + self.banks = {label: + dict(zip(string.ascii_lowercase, [False]*len(string.ascii_lowercase))) + for label in string.ascii_lowercase} + if start_positions: + self.set_positions(*start_positions) + if not initial_signal: + initial_signal = self.test_start + self.pending = [initial_signal] + self.propagate(use_diagonal_board) + live_wire_count = len([self.banks[self.test_start.bank][w] + for w in self.banks[self.test_start.bank] + if self.banks[self.test_start.bank][w]]) + if live_wire_count < 26: + if verify_plugboard: + possibles = self.possible_plugboards() + return all(s0.isdisjoint(s1) for s0 in possibles for s1 in possibles if s0 != s1) + else: + return True + else: + return False + + def propagate(self, use_diagonal_board): + while self.pending: + current = self.pending[0] + # print("processing", current) + self.pending = self.pending[1:] + if not self.banks[current.bank][current.wire]: + self.banks[current.bank][current.wire] = True + if use_diagonal_board: + self.pending += [Signal(current.wire, current.bank)] + for c in self.connections: + if current.bank in c.banks: + other_bank = [b for b in c.banks if b != current.bank][0] + other_wire = c.scrambler.lookup(current.wire) + # print(" adding", other_bank, other_wire, "because", c.banks) + self.pending += [Signal(other_bank, other_wire)] + + def run(self, run_start=None, wheel1_pos='a', wheel2_pos='a', wheel3_pos='a', use_diagonal_board=True): + if not run_start: + run_start = self.test_start + self.solutions = [] + self.set_positions(wheel1_pos, wheel2_pos, wheel3_pos) + for run_index in range(26*26*26): + if self.test(initial_signal=run_start, use_diagonal_board=use_diagonal_board): + self.solutions += [self.connections[0].scrambler.wheel_positions_l] + advance3 = True + advance2 = False + advance1 = False + if (run_index + 1) % 26 == 0: advance2 = True + if (run_index + 1) % (26*26) == 0: advance1 = True + for c in self.connections: + c.scrambler.advance(advance1, advance2, advance3) + return self.solutions + + def possible_plugboards(self): + possibles = set() + for b in self.banks: + active = [w for w in self.banks[b] if self.banks[b][w]] + inactive = [w for w in self.banks[b] if not self.banks[b][w]] + if len(active) == 1: + possibles = possibles.union({frozenset((b, active[0]))}) + if len(inactive) == 1: + possibles = possibles.union({frozenset((b, inactive[0]))}) + return possibles + + +def make_menu(plaintext, ciphertext): + return [MenuItem(p, c, i+1) + for i, (p, c) in enumerate(zip(plaintext, ciphertext))] + + +def run_multi_bombe(wheel1_spec, wheel2_spec, wheel3_spec, reflector_spec, menu, + start_signal=None, use_diagonal_board=True, + verify_plugboard=True): + allwheels = itertools.product(string.ascii_lowercase, repeat=3) + + with multiprocessing.Pool() as pool: + res = pool.map(Bombe(wheel1_spec, wheel2_spec, wheel3_spec, + reflector_spec, menu=menu, start_signal=start_signal, + use_diagonal_board=use_diagonal_board, + verify_plugboard=verify_plugboard), + allwheels) + return [r[0] for r in res if r[1]] + + if __name__ == "__main__": import doctest # doctest.testmod(extraglobs={'lt': LetterTransformer(1, 'a')})