From: Neil Smith Date: Fri, 3 Jun 2016 20:00:12 +0000 (+0100) Subject: Added tests for enigma machine and bombe X-Git-Url: https://git.njae.me.uk/?a=commitdiff_plain;h=a718663ac5643f2ee292a6b5784aa4d4ea95ee17;p=cipher-tools.git Added tests for enigma machine and bombe --- diff --git a/Untitled.ipynb b/Untitled.ipynb new file mode 100644 index 0000000..0a8a3e0 --- /dev/null +++ b/Untitled.ipynb @@ -0,0 +1,181 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from enigma import *" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "e31 = 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')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def advance(e, n):\n", + " def print_state(e):\n", + " print(' self.assertEqual(self.enigma31.wheel_positions, {})'.format(e.wheel_positions))\n", + " print(\" self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(e.wheel_positions_l)))\n", + " print(' self.assertEqual(self.enigma31.peg_positions, {})'.format(e.peg_positions))\n", + " print()\n", + " \n", + " \n", + " \n", + " print_state(e)\n", + " for i in range(n):\n", + " print(' self.engima31.advance()')\n", + " e.advance()\n", + " print_state(e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": false + }, + "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.peg_positions, ([16], [1], [2]))\n", + "\n", + " self.engima31.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.peg_positions, ([16], [1], [1]))\n", + "\n", + " self.engima31.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.peg_positions, ([16], [1], [0]))\n", + "\n", + " self.engima31.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.peg_positions, ([16], [0], [25]))\n", + "\n", + " self.engima31.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.peg_positions, ([15], [25], [24]))\n", + "\n", + " self.engima31.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.peg_positions, ([15], [25], [23]))\n", + "\n" + ] + } + ], + "source": [ + "e31.set_wheels('a', 'y', 't')\n", + "advance(e31, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " self.assertEqual(self.enigma31.wheel_positions, (21, 6, 21))\n", + " self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azs')\n", + " self.assertEqual(self.enigma31.peg_positions, ([16], [0], [3]))\n", + "\n", + " self.engima31.advance()\n", + " self.assertEqual(self.enigma31.wheel_positions, (22, 7, 22))\n", + " self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bat')\n", + " self.assertEqual(self.enigma31.peg_positions, ([15], [25], [2]))\n", + "\n", + " self.engima31.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.peg_positions, ([15], [25], [1]))\n", + "\n", + " self.engima31.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.peg_positions, ([15], [25], [0]))\n", + "\n", + " self.engima31.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.peg_positions, ([15], [24], [25]))\n", + "\n", + " self.engima31.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.peg_positions, ([15], [24], [24]))\n", + "\n" + ] + } + ], + "source": [ + "e31.set_wheels('a', 'z', 's')\n", + "advance(e31, 5)" + ] + }, + { + "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/bombe.ipynb b/bombe.ipynb index c340539..99770da 100644 --- a/bombe.ipynb +++ b/bombe.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": { "collapsed": true }, @@ -22,38 +22,6 @@ "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": 4, - "metadata": { - "collapsed": true - }, - "outputs": [], "source": [ "Signal = collections.namedtuple('Signal', ['bank', 'wire'])\n", "Connection = collections.namedtuple('Connection', ['banks', 'scrambler'])\n", @@ -62,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": { "collapsed": true }, @@ -107,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": { "collapsed": false }, @@ -231,7 +199,7 @@ }, { "cell_type": "code", - "execution_count": 804, + "execution_count": 6, "metadata": { "collapsed": false }, @@ -243,7 +211,7 @@ }, { "cell_type": "code", - "execution_count": 805, + "execution_count": 7, "metadata": { "collapsed": true }, @@ -259,7 +227,7 @@ }, { "cell_type": "code", - "execution_count": 806, + "execution_count": 8, "metadata": { "collapsed": false }, @@ -270,7 +238,7 @@ "'opgndxcrwomnlnecjz'" ] }, - "execution_count": 806, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -284,7 +252,7 @@ }, { "cell_type": "code", - "execution_count": 807, + "execution_count": 9, "metadata": { "collapsed": false }, @@ -295,7 +263,7 @@ "'aas'" ] }, - "execution_count": 807, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -306,7 +274,7 @@ }, { "cell_type": "code", - "execution_count": 808, + "execution_count": 10, "metadata": { "collapsed": false }, @@ -334,7 +302,7 @@ " MenuIem(before='e', after='z', number=18)]" ] }, - "execution_count": 808, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -346,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 809, + "execution_count": 11, "metadata": { "collapsed": true }, @@ -359,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 810, + "execution_count": 12, "metadata": { "collapsed": false }, @@ -387,7 +355,7 @@ " MenuIem(before='e', after='z', number=18)]" ] }, - "execution_count": 810, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -398,7 +366,7 @@ }, { "cell_type": "code", - "execution_count": 811, + "execution_count": 13, "metadata": { "collapsed": false }, @@ -409,7 +377,7 @@ "'s'" ] }, - "execution_count": 811, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -420,7 +388,7 @@ }, { "cell_type": "code", - "execution_count": 812, + "execution_count": 14, "metadata": { "collapsed": false }, @@ -431,7 +399,7 @@ }, { "cell_type": "code", - "execution_count": 813, + "execution_count": 15, "metadata": { "collapsed": false }, @@ -442,7 +410,7 @@ "18" ] }, - "execution_count": 813, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -453,7 +421,7 @@ }, { "cell_type": "code", - "execution_count": 814, + "execution_count": 16, "metadata": { "collapsed": false }, @@ -490,7 +458,7 @@ }, { "cell_type": "code", - "execution_count": 815, + "execution_count": 17, "metadata": { "collapsed": false }, @@ -501,7 +469,7 @@ "False" ] }, - "execution_count": 815, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -512,7 +480,7 @@ }, { "cell_type": "code", - "execution_count": 816, + "execution_count": 18, "metadata": { "collapsed": false }, @@ -548,7 +516,7 @@ " 'z': True}" ] }, - "execution_count": 816, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -559,7 +527,7 @@ }, { "cell_type": "code", - "execution_count": 817, + "execution_count": 19, "metadata": { "collapsed": false }, @@ -610,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": 818, + "execution_count": 20, "metadata": { "collapsed": false }, @@ -621,7 +589,7 @@ "('a', 'a', 'a')" ] }, - "execution_count": 818, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -632,7 +600,7 @@ }, { "cell_type": "code", - "execution_count": 819, + "execution_count": 21, "metadata": { "collapsed": false }, @@ -670,7 +638,7 @@ }, { "cell_type": "code", - "execution_count": 820, + "execution_count": 22, "metadata": { "collapsed": false }, @@ -682,7 +650,7 @@ }, { "cell_type": "code", - "execution_count": 821, + "execution_count": 23, "metadata": { "collapsed": false }, @@ -736,7 +704,7 @@ }, { "cell_type": "code", - "execution_count": 822, + "execution_count": 24, "metadata": { "collapsed": false }, @@ -790,7 +758,7 @@ }, { "cell_type": "code", - "execution_count": 823, + "execution_count": 25, "metadata": { "collapsed": false }, @@ -801,7 +769,7 @@ "1" ] }, - "execution_count": 823, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -812,7 +780,7 @@ }, { "cell_type": "code", - "execution_count": 824, + "execution_count": 26, "metadata": { "collapsed": false }, @@ -825,7 +793,7 @@ }, { "cell_type": "code", - "execution_count": 825, + "execution_count": 27, "metadata": { "collapsed": false }, @@ -838,7 +806,7 @@ }, { "cell_type": "code", - "execution_count": 826, + "execution_count": 28, "metadata": { "collapsed": false }, @@ -849,7 +817,7 @@ "('a', 'a', 'b')" ] }, - "execution_count": 826, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -860,7 +828,7 @@ }, { "cell_type": "code", - "execution_count": 827, + "execution_count": 29, "metadata": { "collapsed": false }, @@ -871,7 +839,7 @@ "False" ] }, - "execution_count": 827, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -882,7 +850,7 @@ }, { "cell_type": "code", - "execution_count": 828, + "execution_count": 30, "metadata": { "collapsed": false }, @@ -893,7 +861,7 @@ "('p', 'p', 'p')" ] }, - "execution_count": 828, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -904,7 +872,7 @@ }, { "cell_type": "code", - "execution_count": 829, + "execution_count": 31, "metadata": { "collapsed": false }, @@ -955,7 +923,7 @@ }, { "cell_type": "code", - "execution_count": 830, + "execution_count": 32, "metadata": { "collapsed": false }, @@ -966,7 +934,7 @@ "17576" ] }, - "execution_count": 830, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -978,7 +946,7 @@ }, { "cell_type": "code", - "execution_count": 831, + "execution_count": 33, "metadata": { "collapsed": false }, @@ -989,7 +957,7 @@ "(('a', 'a', 'b'), True)" ] }, - "execution_count": 831, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -1001,7 +969,7 @@ }, { "cell_type": "code", - "execution_count": 832, + "execution_count": 34, "metadata": { "collapsed": false }, @@ -1012,7 +980,7 @@ }, { "cell_type": "code", - "execution_count": 833, + "execution_count": 35, "metadata": { "collapsed": false }, @@ -1023,7 +991,7 @@ "[('a', 'a', 'b')]" ] }, - "execution_count": 833, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1039,7 +1007,7 @@ }, { "cell_type": "code", - "execution_count": 857, + "execution_count": 36, "metadata": { "collapsed": true }, @@ -1061,7 +1029,7 @@ }, { "cell_type": "code", - "execution_count": 835, + "execution_count": 37, "metadata": { "collapsed": false }, @@ -1072,7 +1040,7 @@ "[('a', 'a', 'b')]" ] }, - "execution_count": 835, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1083,7 +1051,7 @@ }, { "cell_type": "code", - "execution_count": 836, + "execution_count": 38, "metadata": { "collapsed": false }, @@ -1102,7 +1070,7 @@ }, { "cell_type": "code", - "execution_count": 837, + "execution_count": 39, "metadata": { "collapsed": false }, @@ -1113,7 +1081,7 @@ "('e', 'l', 'e')" ] }, - "execution_count": 837, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1125,7 +1093,7 @@ }, { "cell_type": "code", - "execution_count": 838, + "execution_count": 40, "metadata": { "collapsed": false }, @@ -1136,7 +1104,7 @@ "'dhnpforeeimgg'" ] }, - "execution_count": 838, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -1150,7 +1118,7 @@ }, { "cell_type": "code", - "execution_count": 839, + "execution_count": 41, "metadata": { "collapsed": false }, @@ -1161,7 +1129,7 @@ "('j', 'e', 'o')" ] }, - "execution_count": 839, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -1172,7 +1140,7 @@ }, { "cell_type": "code", - "execution_count": 840, + "execution_count": 42, "metadata": { "collapsed": false }, @@ -1195,7 +1163,7 @@ " MenuIem(before='t', after='g', number=13)]" ] }, - "execution_count": 840, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -1207,7 +1175,7 @@ }, { "cell_type": "code", - "execution_count": 841, + "execution_count": 43, "metadata": { "collapsed": false, "scrolled": true @@ -1280,7 +1248,7 @@ " ('z', 'z', 'k')]" ] }, - "execution_count": 841, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -1297,7 +1265,7 @@ }, { "cell_type": "code", - "execution_count": 842, + "execution_count": 44, "metadata": { "collapsed": false }, @@ -1308,7 +1276,7 @@ "62" ] }, - "execution_count": 842, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -1319,7 +1287,7 @@ }, { "cell_type": "code", - "execution_count": 843, + "execution_count": 45, "metadata": { "collapsed": false, "scrolled": true @@ -1343,7 +1311,7 @@ " ('y', 'n', 'c')]" ] }, - "execution_count": 843, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -1360,7 +1328,7 @@ }, { "cell_type": "code", - "execution_count": 858, + "execution_count": 46, "metadata": { "collapsed": false }, @@ -1383,7 +1351,7 @@ " ('y', 'n', 'c')]" ] }, - "execution_count": 858, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1394,7 +1362,7 @@ }, { "cell_type": "code", - "execution_count": 844, + "execution_count": 47, "metadata": { "collapsed": false }, @@ -1411,7 +1379,7 @@ }, { "cell_type": "code", - "execution_count": 845, + "execution_count": 48, "metadata": { "collapsed": false }, @@ -1422,7 +1390,7 @@ "13" ] }, - "execution_count": 845, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -1433,7 +1401,7 @@ }, { "cell_type": "code", - "execution_count": 846, + "execution_count": 49, "metadata": { "collapsed": false }, @@ -1445,7 +1413,7 @@ }, { "cell_type": "code", - "execution_count": 847, + "execution_count": 50, "metadata": { "collapsed": false }, @@ -1456,7 +1424,7 @@ "Signal(bank='e', wire='e')" ] }, - "execution_count": 847, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -1467,7 +1435,7 @@ }, { "cell_type": "code", - "execution_count": 848, + "execution_count": 51, "metadata": { "collapsed": false }, @@ -1478,7 +1446,7 @@ "True" ] }, - "execution_count": 848, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -1489,7 +1457,7 @@ }, { "cell_type": "code", - "execution_count": 849, + "execution_count": 52, "metadata": { "collapsed": false }, @@ -1500,7 +1468,7 @@ "True" ] }, - "execution_count": 849, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -1511,7 +1479,7 @@ }, { "cell_type": "code", - "execution_count": 850, + "execution_count": 53, "metadata": { "collapsed": false }, @@ -1565,7 +1533,7 @@ }, { "cell_type": "code", - "execution_count": 851, + "execution_count": 54, "metadata": { "collapsed": false }, @@ -1619,7 +1587,7 @@ }, { "cell_type": "code", - "execution_count": 852, + "execution_count": 55, "metadata": { "collapsed": false }, @@ -1627,15 +1595,15 @@ { "data": { "text/plain": [ - "{frozenset({'e', 'y'}),\n", + "{frozenset({'b', 'g'}),\n", + " frozenset({'e', 'y'}),\n", " frozenset({'t', 'x'}),\n", - " frozenset({'i', 'n'}),\n", " frozenset({'m'}),\n", - " frozenset({'b', 'g'}),\n", + " frozenset({'i', 'n'}),\n", " frozenset({'f', 'p'})}" ] }, - "execution_count": 852, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -1647,7 +1615,7 @@ }, { "cell_type": "code", - "execution_count": 853, + "execution_count": 56, "metadata": { "collapsed": false }, @@ -1658,7 +1626,7 @@ "True" ] }, - "execution_count": 853, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -1669,7 +1637,7 @@ }, { "cell_type": "code", - "execution_count": 854, + "execution_count": 57, "metadata": { "collapsed": false }, @@ -1683,7 +1651,7 @@ " frozenset({2, 3}))" ] }, - "execution_count": 854, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -1702,7 +1670,7 @@ }, { "cell_type": "code", - "execution_count": 855, + "execution_count": 58, "metadata": { "collapsed": false }, @@ -1713,7 +1681,7 @@ "False" ] }, - "execution_count": 855, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -1724,7 +1692,7 @@ }, { "cell_type": "code", - "execution_count": 856, + "execution_count": 59, "metadata": { "collapsed": false }, @@ -1735,7 +1703,7 @@ "False" ] }, - "execution_count": 856, + "execution_count": 59, "metadata": {}, "output_type": "execute_result" } diff --git a/bombe.py b/bombe.py new file mode 100644 index 0000000..acaad0e --- /dev/null +++ b/bombe.py @@ -0,0 +1,163 @@ +import string +import collections +import multiprocessing +import itertools +from enigma import * + + +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 + diff --git a/enigma.py b/enigma.py index cb57f7a..8c72f22 100644 --- a/enigma.py +++ b/enigma.py @@ -63,24 +63,6 @@ class LetterTransformer(object): """A generic substitution cipher, that has different transforms in the forward and backward directions. It requires that the transforms for all letters by provided. - - >>> lt = LetterTransformer([('z', 'a')] + [(l, string.ascii_lowercase[i+1]) \ - for i, l in enumerate(string.ascii_lowercase[:-1])], \ - raw_transform = True) - >>> 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] - >>> 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] - - >>> lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))) - >>> 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] - >>> 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] - >>> cat(lt.forward(l) for l in string.ascii_lowercase) - 'zyxwcabdefghijklmnopqrstuv' - >>> cat(lt.backward(l) for l in string.ascii_lowercase) - 'fgehijklmnopqrstuvwxyzdcba' """ def __init__(self, specification, raw_transform=False): if raw_transform: @@ -136,16 +118,6 @@ class Plugboard(LetterTransformer): """A plugboard, a type of letter transformer where forward and backward transforms are the same. If a letter isn't explicitly transformed, it is kept as it is. - - >>> pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper()) - >>> 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] - >>> pb.forward_map == pb.backward_map - True - >>> cat(pb.forward(l) for l in string.ascii_lowercase) - 'ugcdypblnzkhmisfrqoxavwtej' - >>> cat(pb.backward(l) for l in string.ascii_lowercase) - 'ugcdypblnzkhmisfrqoxavwtej' """ def parse_specification(self, specification): return [tuple(clean(p)) for p in specification.split()] @@ -170,16 +142,6 @@ class Plugboard(LetterTransformer): class Reflector(Plugboard): """A reflector is a plugboard that requires 13 transforms. - - >>> reflector_b = Reflector(reflector_b_spec) - >>> reflector_b.forward_map == reflector_b.backward_map - True - >>> 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] - >>> cat(reflector_b.forward(l) for l in string.ascii_lowercase) - 'yruhqsldpxngokmiebfzcwvjat' - >>> cat(reflector_b.backward(l) for l in string.ascii_lowercase) - 'yruhqsldpxngokmiebfzcwvjat' """ def validate_transform(self, transform): if len(transform) != 13: @@ -208,52 +170,6 @@ class SimpleWheel(LetterTransformer): Letter inputs and outputs are given relative to the frame holding the wheel, so if the wheel is advanced three places, an input of 'p' will enter the wheel on the position under the wheel's 'q' label. - - >>> rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower())) - >>> wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True) - >>> cat(wheel_1.forward(l) for l in string.ascii_lowercase) - 'ekmflgdqvzntowyhxuspaibrcj' - >>> cat(wheel_1.backward(l) for l in string.ascii_lowercase) - 'uwygadfpvzbeckmthxslrinqoj' - - - >>> wheel_2 = SimpleWheel(wheel_ii_spec) - >>> cat(wheel_2.forward(l) for l in string.ascii_lowercase) - 'ajdksiruxblhwtmcqgznpyfvoe' - >>> cat(wheel_2.backward(l) for l in string.ascii_lowercase) - 'ajpczwrlfbdkotyuqgenhxmivs' - - >>> wheel_3 = SimpleWheel(wheel_iii_spec) - >>> wheel_3.set_position('a') - >>> wheel_3.advance() - >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase) - 'cegikboqswuymxdhvfzjltrpna' - >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase) - 'zfaobrcpdteumygxhwivkqjnls' - >>> wheel_3.position - 1 - >>> wheel_3.position_l - 'b' - - >>> for _ in range(24): wheel_3.advance() - >>> wheel_3.position - 25 - >>> wheel_3.position_l - 'z' - >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase) - 'pcegikmdqsuywaozfjxhblnvtr' - >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase) - 'nubhcqdterfvgwoaizjykxmslp' - - >>> wheel_3.advance() - >>> wheel_3.position - 0 - >>> wheel_3.position_l - 'a' - >>> cat(wheel_3.forward(l) for l in string.ascii_lowercase) - 'bdfhjlcprtxvznyeiwgakmusqo' - >>> cat(wheel_3.backward(l) for l in string.ascii_lowercase) - 'tagbpcsdqeufvnzhyixjwlrkom' """ def __init__(self, transform, position='a', raw_transform=False): super(SimpleWheel, self).__init__(transform, raw_transform) @@ -304,119 +220,6 @@ class Wheel(SimpleWheel): The peg_positions are the number of advances of this wheel before it will advance the next wheel. - >>> wheel_3 = Wheel(wheel_iii_spec, wheel_iii_pegs, position='b', ring_setting=1) - >>> wheel_3.position - 1 - >>> wheel_3.peg_positions - [20] - >>> wheel_3.position_l - 'b' - >>> wheel_3.advance() - >>> wheel_3.position - 2 - >>> wheel_3.peg_positions - [19] - >>> wheel_3.position_l - 'c' - - >>> wheel_6 = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', ring_setting=3) - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'xkqhwpvngzrcfoiaselbtymjdu' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'ptlyrmidoxbswhnfckquzgeavj' - >>> wheel_6.position - 25 - >>> 11 in wheel_6.peg_positions - True - >>> 24 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'b' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'jpgvoumfyqbenhzrdkasxlictw' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'skxqlhcnwarvgmebjptyfdzuio' - >>> wheel_6.position - 0 - >>> 10 in wheel_6.peg_positions - True - >>> 23 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'c' - - >>> for _ in range(22): wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'mgxantkzsyqjcufirldvhoewbp' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'dymswobuplgraevzkqifntxcjh' - >>> wheel_6.position - 22 - >>> 1 in wheel_6.peg_positions - True - >>> 14 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'y' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'fwzmsjyrxpibtehqkcugndvaol' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'xlrvnatokfqzduyjphemswbigc' - >>> wheel_6.position - 23 - >>> 0 in wheel_6.peg_positions - True - >>> 13 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'z' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'vylrixqwohasdgpjbtfmcuznke' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'kqumzsnjepyctxiogdlrvahfbw' - >>> wheel_6.position - 24 - >>> 25 in wheel_6.peg_positions - True - >>> 12 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'a' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'xkqhwpvngzrcfoiaselbtymjdu' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'ptlyrmidoxbswhnfckquzgeavj' - >>> wheel_6.position - 25 - >>> 24 in wheel_6.peg_positions - True - >>> 11 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'b' - - >>> wheel_6.advance() - >>> cat(wheel_6.forward(l) for l in string.ascii_lowercase) - 'jpgvoumfyqbenhzrdkasxlictw' - >>> cat(wheel_6.backward(l) for l in string.ascii_lowercase) - 'skxqlhcnwarvgmebjptyfdzuio' - >>> wheel_6.position - 0 - >>> 23 in wheel_6.peg_positions - True - >>> 10 in wheel_6.peg_positions - True - >>> wheel_6.position_l - 'c' - """ def __init__(self, transform, ring_peg_letters, ring_setting=1, position='a', raw_transform=False): self.ring_peg_letters = ring_peg_letters @@ -442,489 +245,7 @@ class Wheel(SimpleWheel): class Enigma(object): """An Enigma machine. - >>> enigma = Enigma(reflector_b_spec, \ - wheel_i_spec, wheel_i_pegs, \ - wheel_ii_spec, wheel_ii_pegs, \ - wheel_iii_spec, wheel_iii_pegs, \ - 1, 1, 1, \ - '') - >>> enigma.set_wheels('a', 'a', 't') - >>> enigma.wheel_positions - (0, 0, 19) - >>> cat(enigma.wheel_positions_l) - 'aat' - >>> enigma.peg_positions - ([16], [4], [2]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'puvioztjdhxmlyeawsrgbcqknf' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 0, 20) - >>> cat(enigma.wheel_positions_l) - 'aau' - >>> enigma.peg_positions - ([16], [4], [1]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'baigpldqcowfyzjehvtsxrkumn' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 0, 21) - >>> cat(enigma.wheel_positions_l) - 'aav' - >>> enigma.peg_positions - ([16], [4], [0]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'mnvfydiwgzsoablrxpkutchqej' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 1, 22) - >>> cat(enigma.wheel_positions_l) - 'abw' - >>> enigma.peg_positions - ([16], [3], [25]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'ulfopcykswhbzvderqixanjtgm' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 1, 23) - >>> cat(enigma.wheel_positions_l) - 'abx' - >>> enigma.peg_positions - ([16], [3], [24]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'qmwftdyovursbzhxaklejicpgn' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 1, 24) - >>> cat(enigma.wheel_positions_l) - 'aby' - >>> enigma.peg_positions - ([16], [3], [23]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'oljmzxrvucybdqasngpwihtfke' - - - - - >>> enigma.set_wheels('a', 'd', 't') - >>> enigma.wheel_positions - (0, 3, 19) - >>> cat(enigma.wheel_positions_l) - 'adt' - >>> enigma.peg_positions - ([16], [1], [2]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'zcbpqxwsjiuonmldethrkygfva' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 3, 20) - >>> cat(enigma.wheel_positions_l) - 'adu' - >>> enigma.peg_positions - ([16], [1], [1]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'ehprawjbngotxikcsdqlzyfmvu' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 3, 21) - >>> cat(enigma.wheel_positions_l) - 'adv' - >>> enigma.peg_positions - ([16], [1], [0]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'eqzxarpihmnvjkwgbfuyslodtc' - - >>> enigma.advance() - >>> enigma.wheel_positions - (0, 4, 22) - >>> cat(enigma.wheel_positions_l) - 'aew' - >>> enigma.peg_positions - ([16], [0], [25]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'qedcbtpluzmhkongavwfirsyxj' - - >>> enigma.advance() - >>> enigma.wheel_positions - (1, 5, 23) - >>> cat(enigma.wheel_positions_l) - 'bfx' - >>> enigma.peg_positions - ([15], [25], [24]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'iwuedhsfazqxytvrkpgncoblmj' - - >>> enigma.advance() - >>> enigma.wheel_positions - (1, 5, 24) - >>> cat(enigma.wheel_positions_l) - 'bfy' - >>> enigma.peg_positions - ([15], [25], [23]) - >>> cat(enigma.lookup(l) for l in string.ascii_lowercase) - 'baknstqzrmcxjdvygiefwoulph' - - - >>> enigma.set_wheels('a', 'a', 'a') - >>> ct = enigma.encipher('testmessage') - >>> ct - 'olpfhnvflyn' - - >>> enigma.set_wheels('a', 'd', 't') - >>> ct = enigma.encipher('testmessage') - >>> ct - 'lawnjgpwjik' - - - >>> enigma.set_wheels('b', 'd', 'q') - >>> ct = enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') - >>> ct - 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr' - >>> enigma.left_wheel.position_l - 'c' - >>> enigma.middle_wheel.position_l - 'h' - >>> enigma.right_wheel.position_l - 'a' - - # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm - # Enigma simulation settings are - # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJEU;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX - >>> enigma31 = Enigma(reflector_b_spec, \ - wheel_i_spec, wheel_i_pegs, \ - wheel_v_spec, wheel_v_pegs, \ - wheel_iii_spec, wheel_iii_pegs, \ - 6, 20, 24, \ - 'ua pf rq so ni ey bg hl tx zj') - - >>> enigma31.set_wheels('j', 'e', 'u') - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 11, 24) - >>> cat(enigma31.wheel_positions_l) - 'jev' - >>> enigma31.peg_positions - ([7], [21], [0]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'mvqjlyowkdieasgzcunxrbhtfp' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 25) - >>> cat(enigma31.wheel_positions_l) - 'jfw' - >>> enigma31.peg_positions - ([7], [20], [25]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'sjolzuyvrbwdpxcmtiaqfhknge' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 0) - >>> cat(enigma31.wheel_positions_l) - 'jfx' - >>> enigma31.peg_positions - ([7], [20], [24]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'qrxedkoywufmlvgsabpzjnicht' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 1) - >>> cat(enigma31.wheel_positions_l) - 'jfy' - >>> enigma31.peg_positions - ([7], [20], [23]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'hpsukliagqefwvtbjxcodnmrzy' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (4, 12, 2) - >>> cat(enigma31.wheel_positions_l) - 'jfz' - >>> enigma31.peg_positions - ([7], [20], [22]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'zevnbpyqowrtxdifhkulscjmga' - - - >>> enigma31.set_wheels('i', 'd', 'z') - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 3) - >>> cat(enigma31.wheel_positions_l) - 'ida' - >>> enigma31.peg_positions - ([8], [22], [21]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'ikhpqrvcambzjondefwyxgsutl' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 4) - >>> cat(enigma31.wheel_positions_l) - 'idb' - >>> enigma31.peg_positions - ([8], [22], [20]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'cdabskhgzwfmlqvunyexpojtri' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 5) - >>> cat(enigma31.wheel_positions_l) - 'idc' - >>> enigma31.peg_positions - ([8], [22], [19]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'pcbwiqhgemyvjsuaftnroldzkx' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 6) - >>> cat(enigma31.wheel_positions_l) - 'idd' - >>> enigma31.peg_positions - ([8], [22], [18]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'xcbfvdnouptmlghjzwykierasq' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 7) - >>> cat(enigma31.wheel_positions_l) - 'ide' - >>> enigma31.peg_positions - ([8], [22], [17]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'xfvglbdynuseriwqpmkzjcoaht' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 8) - >>> cat(enigma31.wheel_positions_l) - 'idf' - >>> enigma31.peg_positions - ([8], [22], [16]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'tfpqlbouynsewjgcdxkahzmriv' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 9) - >>> cat(enigma31.wheel_positions_l) - 'idg' - >>> enigma31.peg_positions - ([8], [22], [15]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'cjaunvlwtbygzexrspqidfhokm' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 10) - >>> cat(enigma31.wheel_positions_l) - 'idh' - >>> enigma31.peg_positions - ([8], [22], [14]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'yltxkrqvowebzpingfucshjdam' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 11) - >>> cat(enigma31.wheel_positions_l) - 'idi' - >>> enigma31.peg_positions - ([8], [22], [13]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'myktluzrnxceaiqsohpdfwvjbg' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 12) - >>> cat(enigma31.wheel_positions_l) - 'idj' - >>> enigma31.peg_positions - ([8], [22], [12]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'pynjrmiugdqxfcvakewzhoslbt' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 13) - >>> cat(enigma31.wheel_positions_l) - 'idk' - >>> enigma31.peg_positions - ([8], [22], [11]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'mwvedyplnoxhaijgrqtszcbkfu' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 14) - >>> cat(enigma31.wheel_positions_l) - 'idl' - >>> enigma31.peg_positions - ([8], [22], [10]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'qcbrfeutvoxpnmjladzhgiykws' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 15) - >>> cat(enigma31.wheel_positions_l) - 'idm' - >>> enigma31.peg_positions - ([8], [22], [9]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'dnoahryetsmukbcvwfjilpqzgx' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 16) - >>> cat(enigma31.wheel_positions_l) - 'idn' - >>> enigma31.peg_positions - ([8], [22], [8]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'nidcfehgbqsovalyjzkxwmutpr' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 17) - >>> cat(enigma31.wheel_positions_l) - 'ido' - >>> enigma31.peg_positions - ([8], [22], [7]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'joifxdulcarhzpbntkwqgysevm' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 18) - >>> cat(enigma31.wheel_positions_l) - 'idp' - >>> enigma31.peg_positions - ([8], [22], [6]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'ptnlsxvozmwdjchayuebrgkfqi' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 19) - >>> cat(enigma31.wheel_positions_l) - 'idq' - >>> enigma31.peg_positions - ([8], [22], [5]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'slwopzqnmxybihdeguavrtcjkf' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 20) - >>> cat(enigma31.wheel_positions_l) - 'idr' - >>> enigma31.peg_positions - ([8], [22], [4]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'hcbedwlamzogixkytsrqvufnpj' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 21) - >>> cat(enigma31.wheel_positions_l) - 'ids' - >>> enigma31.peg_positions - ([8], [22], [3]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'odxbjwzrmelkisavuhnyqpfctg' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 22) - >>> cat(enigma31.wheel_positions_l) - 'idt' - >>> enigma31.peg_positions - ([8], [22], [2]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'udgbfeclrwnhxksvtioqapjmzy' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 23) - >>> cat(enigma31.wheel_positions_l) - 'idu' - >>> enigma31.peg_positions - ([8], [22], [1]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'nrdczqxmowvshaiufblypkjgte' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 10, 24) - >>> cat(enigma31.wheel_positions_l) - 'idv' - >>> enigma31.peg_positions - ([8], [22], [0]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'hkifjdoacebqtzgulyvmpsxwrn' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 11, 25) - >>> cat(enigma31.wheel_positions_l) - 'iew' - >>> enigma31.peg_positions - ([8], [21], [25]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'yptzuhofqvnmlkgbixwcejsrad' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 11, 0) - >>> cat(enigma31.wheel_positions_l) - 'iex' - >>> enigma31.peg_positions - ([8], [21], [24]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'vkdcwhqfjibzsptngumoraeyxl' - - >>> enigma31.advance() - >>> enigma31.wheel_positions - (3, 11, 1) - >>> cat(enigma31.wheel_positions_l) - 'iey' - >>> enigma31.peg_positions - ([8], [21], [23]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'wenpbqrouxlkychdfgzvitajms' - - - >>> enigma31.set_wheels('i', 'z', 'd') - >>> enigma31.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure') - 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki' - >>> enigma31.wheel_positions - (4, 9, 10) - >>> cat(enigma31.wheel_positions_l) - 'jch' - >>> enigma31.peg_positions - ([7], [23], [14]) - >>> cat(enigma31.lookup(l) for l in string.ascii_lowercase) - 'mopnigfuesqwadbcktjrhylzvx' - >>> enigma31.set_wheels('i', 'z', 'd') - >>> enigma31.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') - 'verylongtestmessagewithanextrabitofmessageforgoodmeasure' """ def __init__(self, reflector_spec, left_wheel_spec, left_wheel_pegs, diff --git a/test_bombe.py b/test_bombe.py new file mode 100644 index 0000000..830bb2b --- /dev/null +++ b/test_bombe.py @@ -0,0 +1,686 @@ +import unittest +import collections + +from enigma import * + +class LetterTransformerTest(unittest.TestCase): + + def test_maps1(self): + lt = LetterTransformer([('z', 'a')] + \ + list(zip(string.ascii_lowercase, string.ascii_lowercase[1:])), + raw_transform = True) + self.assertEqual(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]) + self.assertEqual(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]) + + + def test_maps2(self): + lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))) + self.assertEqual(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]) + self.assertEqual(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]) + + def test_transform(self): + lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))) + self.assertEqual(cat(lt.forward(l) for l in string.ascii_lowercase), + 'zyxwcabdefghijklmnopqrstuv') + self.assertEqual(cat(lt.backward(l) for l in string.ascii_lowercase), + 'fgehijklmnopqrstuvwxyzdcba') + + +class PlugboardTest(unittest.TestCase): + def setUp(self): + self.pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper()) + + def test_maps(self): + self.assertEqual(self.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]) + self.assertEqual(self.pb.forward_map, self.pb.backward_map) + + def test_transform(self): + self.assertEqual(cat(self.pb.forward(l) + for l in string.ascii_lowercase), + 'ugcdypblnzkhmisfrqoxavwtej') + self.assertEqual(cat(self.pb.backward(l) + for l in string.ascii_lowercase), + 'ugcdypblnzkhmisfrqoxavwtej') + + +class ReflectorTest(unittest.TestCase): + def setUp(self): + self.ref = Reflector(reflector_b_spec) + + def test_maps(self): + self.assertEqual(self.ref.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]) + self.assertEqual(self.ref.forward_map, self.ref.backward_map) + + def test_transform(self): + self.assertEqual(cat(self.ref.forward(l) + for l in string.ascii_lowercase), + 'yruhqsldpxngokmiebfzcwvjat') + self.assertEqual(cat(self.ref.backward(l) + for l in string.ascii_lowercase), + 'yruhqsldpxngokmiebfzcwvjat') + + +class SimpleWheelTest(unittest.TestCase): + def test_init1(self): + rotor_1_transform = list(zip(string.ascii_lowercase, + 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower())) + wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True) + self.assertEqual(cat(wheel_1.forward(l) + for l in string.ascii_lowercase), + 'ekmflgdqvzntowyhxuspaibrcj') + self.assertEqual(cat(wheel_1.backward(l) + for l in string.ascii_lowercase), + 'uwygadfpvzbeckmthxslrinqoj') + + def test_init2(self): + wheel_2 = SimpleWheel(wheel_ii_spec) + self.assertEqual(cat(wheel_2.forward(l) + for l in string.ascii_lowercase), + 'ajdksiruxblhwtmcqgznpyfvoe') + self.assertEqual(cat(wheel_2.backward(l) + for l in string.ascii_lowercase), + 'ajpczwrlfbdkotyuqgenhxmivs') + + def test_advance(self): + wheel_3 = SimpleWheel(wheel_iii_spec) + wheel_3.set_position('a') + wheel_3.advance() + self.assertEqual(cat(wheel_3.forward(l) + for l in string.ascii_lowercase), + 'cegikboqswuymxdhvfzjltrpna') + self.assertEqual(cat(wheel_3.backward(l) + for l in string.ascii_lowercase), + 'zfaobrcpdteumygxhwivkqjnls') + self.assertEqual(wheel_3.position, 1) + self.assertEqual(wheel_3.position_l, 'b') + + for _ in range(24): wheel_3.advance() + + self.assertEqual(wheel_3.position, 25) + self.assertEqual(wheel_3.position_l, 'z') + + self.assertEqual(cat(wheel_3.forward(l) + for l in string.ascii_lowercase), + 'pcegikmdqsuywaozfjxhblnvtr') + self.assertEqual(cat(wheel_3.backward(l) + for l in string.ascii_lowercase), + 'nubhcqdterfvgwoaizjykxmslp') + + wheel_3.advance() + self.assertEqual(wheel_3.position, 0) + self.assertEqual(wheel_3.position_l, 'a') + + + self.assertEqual(cat(wheel_3.forward(l) + for l in string.ascii_lowercase), + 'bdfhjlcprtxvznyeiwgakmusqo') + self.assertEqual(cat(wheel_3.backward(l) + for l in string.ascii_lowercase), + 'tagbpcsdqeufvnzhyixjwlrkom') + + +class SimpleWheelTest(unittest.TestCase): + def test_init1(self): + wheel = Wheel(wheel_iii_spec, wheel_iii_pegs, position='b', + ring_setting=1) + self.assertEqual(wheel.position, 1) + self.assertEqual(wheel.peg_positions, [20]) + self.assertEqual(wheel.position_l, 'b') + + wheel.advance() + self.assertEqual(wheel.position, 2) + self.assertEqual(wheel.peg_positions, [19]) + self.assertEqual(wheel.position_l, 'c') + + def test_init2(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + self.assertEqual(wheel.position, 25) + self.assertIn(11, wheel.peg_positions) + self.assertIn(24, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'b') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'xkqhwpvngzrcfoiaselbtymjdu') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'ptlyrmidoxbswhnfckquzgeavj') + + + def test_advance(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + wheel.advance() + + self.assertEqual(wheel.position, 0) + self.assertIn(10, wheel.peg_positions) + self.assertIn(23, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'c') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'jpgvoumfyqbenhzrdkasxlictw') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'skxqlhcnwarvgmebjptyfdzuio') + + def test_advance_23(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(23): + wheel.advance() + + self.assertEqual(wheel.position, 22) + self.assertIn(1, wheel.peg_positions) + self.assertIn(14, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'y') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'mgxantkzsyqjcufirldvhoewbp') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'dymswobuplgraevzkqifntxcjh') + + def test_advance_24(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(24): + wheel.advance() + + self.assertEqual(wheel.position, 23) + self.assertIn(0, wheel.peg_positions) + self.assertIn(13, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'z') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'fwzmsjyrxpibtehqkcugndvaol') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'xlrvnatokfqzduyjphemswbigc') + + def test_advance_25(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(25): + wheel.advance() + + self.assertEqual(wheel.position, 24) + self.assertIn(25, wheel.peg_positions) + self.assertIn(12, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'a') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'vylrixqwohasdgpjbtfmcuznke') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'kqumzsnjepyctxiogdlrvahfbw') + + def test_advance_26(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(26): + wheel.advance() + + self.assertEqual(wheel.position, 25) + self.assertIn(24, wheel.peg_positions) + self.assertIn(11, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'b') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'xkqhwpvngzrcfoiaselbtymjdu') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'ptlyrmidoxbswhnfckquzgeavj') + + + def test_advance_27(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(27): + wheel.advance() + + self.assertEqual(wheel.position, 0) + self.assertIn(23, wheel.peg_positions) + self.assertIn(10, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'c') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'jpgvoumfyqbenhzrdkasxlictw') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'skxqlhcnwarvgmebjptyfdzuio') + +class EnigmaTest(unittest.TestCase): + + def setUp(self): + self.enigma = Enigma(reflector_b_spec, + wheel_i_spec, wheel_i_pegs, + wheel_ii_spec, wheel_ii_pegs, + wheel_iii_spec, wheel_iii_pegs, + 1, 1, 1, + '') + + # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm + # Enigma simulation settings are + # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJEU;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX + self.enigma31 = Enigma(reflector_b_spec, + wheel_i_spec, wheel_i_pegs, + wheel_v_spec, wheel_v_pegs, + wheel_iii_spec, wheel_iii_pegs, + 6, 20, 24, + 'ua pf rq so ni ey bg hl tx zj') + + + def test_middle_advance(self): + self.enigma.set_wheels('a', 'a', 't') + self.assertEqual(self.enigma.wheel_positions, (0, 0, 19)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aat') + self.assertEqual(self.enigma.peg_positions, ([16], [4], [2])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'puvioztjdhxmlyeawsrgbcqknf') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 0, 20)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aau') + self.assertEqual(self.enigma.peg_positions, ([16], [4], [1])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'baigpldqcowfyzjehvtsxrkumn') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 0, 21)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aav') + self.assertEqual(self.enigma.peg_positions, ([16], [4], [0])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'mnvfydiwgzsoablrxpkutchqej') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 1, 22)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'abw') + self.assertEqual(self.enigma.peg_positions, ([16], [3], [25])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'ulfopcykswhbzvderqixanjtgm') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 1, 23)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'abx') + self.assertEqual(self.enigma.peg_positions, ([16], [3], [24])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'qmwftdyovursbzhxaklejicpgn') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 1, 24)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aby') + self.assertEqual(self.enigma.peg_positions, ([16], [3], [23])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'oljmzxrvucybdqasngpwihtfke') + + + def test_double_advance(self): + self.enigma.set_wheels('a', 'd', 't') + self.assertEqual(self.enigma.wheel_positions, (0, 3, 19)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'adt') + self.assertEqual(self.enigma.peg_positions, ([16], [1], [2])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'zcbpqxwsjiuonmldethrkygfva') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 3, 20)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'adu') + self.assertEqual(self.enigma.peg_positions, ([16], [1], [1])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'ehprawjbngotxikcsdqlzyfmvu') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 3, 21)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'adv') + self.assertEqual(self.enigma.peg_positions, ([16], [1], [0])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'eqzxarpihmnvjkwgbfuyslodtc') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 4, 22)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aew') + self.assertEqual(self.enigma.peg_positions, ([16], [0], [25])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'qedcbtpluzmhkongavwfirsyxj') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (1, 5, 23)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'bfx') + self.assertEqual(self.enigma.peg_positions, ([15], [25], [24])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'iwuedhsfazqxytvrkpgncoblmj') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (1, 5, 24)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'bfy') + self.assertEqual(self.enigma.peg_positions, ([15], [25], [23])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'baknstqzrmcxjdvygiefwoulph') + + + def test_simple_encipher(self): + self.enigma.set_wheels('a', 'a', 'a') + ct = self.enigma.encipher('testmessage') + self.assertEqual(ct, 'olpfhnvflyn') + + self.enigma.set_wheels('a', 'd', 't') + ct = self.enigma.encipher('testmessage') + self.assertEqual(ct, 'lawnjgpwjik') + + self.enigma.set_wheels('b', 'd', 'q') + ct = self.enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') + self.assertEqual(ct, + 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr') + self.assertEqual(cat(self.enigma.wheel_positions_l), 'cha') + + + def test_advance_with_ring_settings(self): + self.enigma31.set_wheels('j', 'e', 'u') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 11, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jev') + self.assertEqual(self.enigma31.peg_positions, ([7], [21], [0])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'mvqjlyowkdieasgzcunxrbhtfp') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfw') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [25])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'sjolzuyvrbwdpxcmtiaqfhknge') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfx') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [24])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'qrxedkoywufmlvgsabpzjnicht') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfy') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [23])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'hpsukliagqefwvtbjxcodnmrzy') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 2)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfz') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [22])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'zevnbpyqowrtxdifhkulscjmga') + + + def test_advance_with_ring_settings_2(self): + self.enigma31.set_wheels('i', 'd', 'z') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 3)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ida') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [21])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'ikhpqrvcambzjondefwyxgsutl') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 4)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idb') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [20])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'cdabskhgzwfmlqvunyexpojtri') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 5)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idc') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [19])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'pcbwiqhgemyvjsuaftnroldzkx') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 6)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idd') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [18])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'xcbfvdnouptmlghjzwykierasq') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 7)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ide') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [17])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'xfvglbdynuseriwqpmkzjcoaht') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 8)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idf') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [16])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'tfpqlbouynsewjgcdxkahzmriv') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 9)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idg') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [15])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'cjaunvlwtbygzexrspqidfhokm') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 10)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idh') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [14])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'yltxkrqvowebzpingfucshjdam') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 11)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idi') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [13])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'myktluzrnxceaiqsohpdfwvjbg') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 12)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idj') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [12])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'pynjrmiugdqxfcvakewzhoslbt') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 13)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idk') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [11])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'mwvedyplnoxhaijgrqtszcbkfu') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 14)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idl') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [10])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'qcbrfeutvoxpnmjladzhgiykws') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 15)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idm') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [9])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'dnoahryetsmukbcvwfjilpqzgx') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 16)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idn') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [8])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'nidcfehgbqsovalyjzkxwmutpr') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 17)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ido') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [7])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'joifxdulcarhzpbntkwqgysevm') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 18)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idp') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [6])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'ptnlsxvozmwdjchayuebrgkfqi') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 19)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idq') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [5])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'slwopzqnmxybihdeguavrtcjkf') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 20)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idr') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [4])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'hcbedwlamzogixkytsrqvufnpj') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 21)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ids') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [3])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'odxbjwzrmelkisavuhnyqpfctg') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 22)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idt') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [2])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'udgbfeclrwnhxksvtioqapjmzy') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 23)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idu') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [1])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'nrdczqxmowvshaiufblypkjgte') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idv') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [0])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'hkifjdoacebqtzgulyvmpsxwrn') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 11, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'iew') + self.assertEqual(self.enigma31.peg_positions, ([8], [21], [25])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'yptzuhofqvnmlkgbixwcejsrad') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 11, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'iex') + self.assertEqual(self.enigma31.peg_positions, ([8], [21], [24])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'vkdcwhqfjibzsptngumoraeyxl') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 11, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'iey') + self.assertEqual(self.enigma31.peg_positions, ([8], [21], [23])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'wenpbqrouxlkychdfgzvitajms') + + def test_double_advance_with_ring_settings_2(self): + self.enigma31.set_wheels('a', 'y', 't') + self.assertEqual(self.enigma31.wheel_positions, (21, 5, 22)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayt') + self.assertEqual(self.enigma31.peg_positions, ([16], [1], [2])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (21, 5, 23)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayu') + self.assertEqual(self.enigma31.peg_positions, ([16], [1], [1])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (21, 5, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayv') + self.assertEqual(self.enigma31.peg_positions, ([16], [1], [0])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (21, 6, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azw') + self.assertEqual(self.enigma31.peg_positions, ([16], [0], [25])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bax') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [24])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bay') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [23])) + + self.enigma31.set_wheels('a', 'z', 't') + self.assertEqual(self.enigma31.wheel_positions, (21, 6, 22)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azt') + self.assertEqual(self.enigma31.peg_positions, ([16], [0], [2])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 23)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bau') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [1])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bav') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [0])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 8, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbw') + self.assertEqual(self.enigma31.peg_positions, ([15], [24], [25])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 8, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbx') + self.assertEqual(self.enigma31.peg_positions, ([15], [24], [24])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 8, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bby') + self.assertEqual(self.enigma31.peg_positions, ([15], [24], [23])) + + + def test_encipher_with_ring(self): + + self.enigma31.set_wheels('i', 'z', 'd') + ct = self.enigma31.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure') + self.assertEqual(ct, + 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') + self.assertEqual(self.enigma31.wheel_positions, (4, 9, 10)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jch') + self.assertEqual(self.enigma31.peg_positions, ([7], [23], [14])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'mopnigfuesqwadbcktjrhylzvx') + + self.enigma31.set_wheels('i', 'z', 'd') + pt = self.enigma31.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') + self.assertEqual(pt, + 'verylongtestmessagewithanextrabitofmessageforgoodmeasure') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test_enigma.py b/test_enigma.py new file mode 100644 index 0000000..830bb2b --- /dev/null +++ b/test_enigma.py @@ -0,0 +1,686 @@ +import unittest +import collections + +from enigma import * + +class LetterTransformerTest(unittest.TestCase): + + def test_maps1(self): + lt = LetterTransformer([('z', 'a')] + \ + list(zip(string.ascii_lowercase, string.ascii_lowercase[1:])), + raw_transform = True) + self.assertEqual(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]) + self.assertEqual(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]) + + + def test_maps2(self): + lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))) + self.assertEqual(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]) + self.assertEqual(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]) + + def test_transform(self): + lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))) + self.assertEqual(cat(lt.forward(l) for l in string.ascii_lowercase), + 'zyxwcabdefghijklmnopqrstuv') + self.assertEqual(cat(lt.backward(l) for l in string.ascii_lowercase), + 'fgehijklmnopqrstuvwxyzdcba') + + +class PlugboardTest(unittest.TestCase): + def setUp(self): + self.pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper()) + + def test_maps(self): + self.assertEqual(self.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]) + self.assertEqual(self.pb.forward_map, self.pb.backward_map) + + def test_transform(self): + self.assertEqual(cat(self.pb.forward(l) + for l in string.ascii_lowercase), + 'ugcdypblnzkhmisfrqoxavwtej') + self.assertEqual(cat(self.pb.backward(l) + for l in string.ascii_lowercase), + 'ugcdypblnzkhmisfrqoxavwtej') + + +class ReflectorTest(unittest.TestCase): + def setUp(self): + self.ref = Reflector(reflector_b_spec) + + def test_maps(self): + self.assertEqual(self.ref.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]) + self.assertEqual(self.ref.forward_map, self.ref.backward_map) + + def test_transform(self): + self.assertEqual(cat(self.ref.forward(l) + for l in string.ascii_lowercase), + 'yruhqsldpxngokmiebfzcwvjat') + self.assertEqual(cat(self.ref.backward(l) + for l in string.ascii_lowercase), + 'yruhqsldpxngokmiebfzcwvjat') + + +class SimpleWheelTest(unittest.TestCase): + def test_init1(self): + rotor_1_transform = list(zip(string.ascii_lowercase, + 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower())) + wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True) + self.assertEqual(cat(wheel_1.forward(l) + for l in string.ascii_lowercase), + 'ekmflgdqvzntowyhxuspaibrcj') + self.assertEqual(cat(wheel_1.backward(l) + for l in string.ascii_lowercase), + 'uwygadfpvzbeckmthxslrinqoj') + + def test_init2(self): + wheel_2 = SimpleWheel(wheel_ii_spec) + self.assertEqual(cat(wheel_2.forward(l) + for l in string.ascii_lowercase), + 'ajdksiruxblhwtmcqgznpyfvoe') + self.assertEqual(cat(wheel_2.backward(l) + for l in string.ascii_lowercase), + 'ajpczwrlfbdkotyuqgenhxmivs') + + def test_advance(self): + wheel_3 = SimpleWheel(wheel_iii_spec) + wheel_3.set_position('a') + wheel_3.advance() + self.assertEqual(cat(wheel_3.forward(l) + for l in string.ascii_lowercase), + 'cegikboqswuymxdhvfzjltrpna') + self.assertEqual(cat(wheel_3.backward(l) + for l in string.ascii_lowercase), + 'zfaobrcpdteumygxhwivkqjnls') + self.assertEqual(wheel_3.position, 1) + self.assertEqual(wheel_3.position_l, 'b') + + for _ in range(24): wheel_3.advance() + + self.assertEqual(wheel_3.position, 25) + self.assertEqual(wheel_3.position_l, 'z') + + self.assertEqual(cat(wheel_3.forward(l) + for l in string.ascii_lowercase), + 'pcegikmdqsuywaozfjxhblnvtr') + self.assertEqual(cat(wheel_3.backward(l) + for l in string.ascii_lowercase), + 'nubhcqdterfvgwoaizjykxmslp') + + wheel_3.advance() + self.assertEqual(wheel_3.position, 0) + self.assertEqual(wheel_3.position_l, 'a') + + + self.assertEqual(cat(wheel_3.forward(l) + for l in string.ascii_lowercase), + 'bdfhjlcprtxvznyeiwgakmusqo') + self.assertEqual(cat(wheel_3.backward(l) + for l in string.ascii_lowercase), + 'tagbpcsdqeufvnzhyixjwlrkom') + + +class SimpleWheelTest(unittest.TestCase): + def test_init1(self): + wheel = Wheel(wheel_iii_spec, wheel_iii_pegs, position='b', + ring_setting=1) + self.assertEqual(wheel.position, 1) + self.assertEqual(wheel.peg_positions, [20]) + self.assertEqual(wheel.position_l, 'b') + + wheel.advance() + self.assertEqual(wheel.position, 2) + self.assertEqual(wheel.peg_positions, [19]) + self.assertEqual(wheel.position_l, 'c') + + def test_init2(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + self.assertEqual(wheel.position, 25) + self.assertIn(11, wheel.peg_positions) + self.assertIn(24, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'b') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'xkqhwpvngzrcfoiaselbtymjdu') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'ptlyrmidoxbswhnfckquzgeavj') + + + def test_advance(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + wheel.advance() + + self.assertEqual(wheel.position, 0) + self.assertIn(10, wheel.peg_positions) + self.assertIn(23, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'c') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'jpgvoumfyqbenhzrdkasxlictw') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'skxqlhcnwarvgmebjptyfdzuio') + + def test_advance_23(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(23): + wheel.advance() + + self.assertEqual(wheel.position, 22) + self.assertIn(1, wheel.peg_positions) + self.assertIn(14, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'y') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'mgxantkzsyqjcufirldvhoewbp') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'dymswobuplgraevzkqifntxcjh') + + def test_advance_24(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(24): + wheel.advance() + + self.assertEqual(wheel.position, 23) + self.assertIn(0, wheel.peg_positions) + self.assertIn(13, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'z') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'fwzmsjyrxpibtehqkcugndvaol') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'xlrvnatokfqzduyjphemswbigc') + + def test_advance_25(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(25): + wheel.advance() + + self.assertEqual(wheel.position, 24) + self.assertIn(25, wheel.peg_positions) + self.assertIn(12, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'a') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'vylrixqwohasdgpjbtfmcuznke') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'kqumzsnjepyctxiogdlrvahfbw') + + def test_advance_26(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(26): + wheel.advance() + + self.assertEqual(wheel.position, 25) + self.assertIn(24, wheel.peg_positions) + self.assertIn(11, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'b') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'xkqhwpvngzrcfoiaselbtymjdu') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'ptlyrmidoxbswhnfckquzgeavj') + + + def test_advance_27(self): + wheel = Wheel(wheel_vi_spec, wheel_vi_pegs, position='b', + ring_setting=3) + for _ in range(27): + wheel.advance() + + self.assertEqual(wheel.position, 0) + self.assertIn(23, wheel.peg_positions) + self.assertIn(10, wheel.peg_positions) + self.assertEqual(wheel.position_l, 'c') + self.assertEqual(cat(wheel.forward(l) + for l in string.ascii_lowercase), + 'jpgvoumfyqbenhzrdkasxlictw') + self.assertEqual(cat(wheel.backward(l) + for l in string.ascii_lowercase), + 'skxqlhcnwarvgmebjptyfdzuio') + +class EnigmaTest(unittest.TestCase): + + def setUp(self): + self.enigma = Enigma(reflector_b_spec, + wheel_i_spec, wheel_i_pegs, + wheel_ii_spec, wheel_ii_pegs, + wheel_iii_spec, wheel_iii_pegs, + 1, 1, 1, + '') + + # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm + # Enigma simulation settings are + # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJEU;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX + self.enigma31 = Enigma(reflector_b_spec, + wheel_i_spec, wheel_i_pegs, + wheel_v_spec, wheel_v_pegs, + wheel_iii_spec, wheel_iii_pegs, + 6, 20, 24, + 'ua pf rq so ni ey bg hl tx zj') + + + def test_middle_advance(self): + self.enigma.set_wheels('a', 'a', 't') + self.assertEqual(self.enigma.wheel_positions, (0, 0, 19)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aat') + self.assertEqual(self.enigma.peg_positions, ([16], [4], [2])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'puvioztjdhxmlyeawsrgbcqknf') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 0, 20)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aau') + self.assertEqual(self.enigma.peg_positions, ([16], [4], [1])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'baigpldqcowfyzjehvtsxrkumn') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 0, 21)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aav') + self.assertEqual(self.enigma.peg_positions, ([16], [4], [0])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'mnvfydiwgzsoablrxpkutchqej') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 1, 22)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'abw') + self.assertEqual(self.enigma.peg_positions, ([16], [3], [25])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'ulfopcykswhbzvderqixanjtgm') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 1, 23)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'abx') + self.assertEqual(self.enigma.peg_positions, ([16], [3], [24])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'qmwftdyovursbzhxaklejicpgn') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 1, 24)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aby') + self.assertEqual(self.enigma.peg_positions, ([16], [3], [23])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'oljmzxrvucybdqasngpwihtfke') + + + def test_double_advance(self): + self.enigma.set_wheels('a', 'd', 't') + self.assertEqual(self.enigma.wheel_positions, (0, 3, 19)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'adt') + self.assertEqual(self.enigma.peg_positions, ([16], [1], [2])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'zcbpqxwsjiuonmldethrkygfva') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 3, 20)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'adu') + self.assertEqual(self.enigma.peg_positions, ([16], [1], [1])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'ehprawjbngotxikcsdqlzyfmvu') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 3, 21)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'adv') + self.assertEqual(self.enigma.peg_positions, ([16], [1], [0])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'eqzxarpihmnvjkwgbfuyslodtc') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (0, 4, 22)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'aew') + self.assertEqual(self.enigma.peg_positions, ([16], [0], [25])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'qedcbtpluzmhkongavwfirsyxj') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (1, 5, 23)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'bfx') + self.assertEqual(self.enigma.peg_positions, ([15], [25], [24])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'iwuedhsfazqxytvrkpgncoblmj') + + self.enigma.advance() + self.assertEqual(self.enigma.wheel_positions, (1, 5, 24)) + self.assertEqual(cat(self.enigma.wheel_positions_l), 'bfy') + self.assertEqual(self.enigma.peg_positions, ([15], [25], [23])) + self.assertEqual(cat(self.enigma.lookup(l) for l in string.ascii_lowercase), + 'baknstqzrmcxjdvygiefwoulph') + + + def test_simple_encipher(self): + self.enigma.set_wheels('a', 'a', 'a') + ct = self.enigma.encipher('testmessage') + self.assertEqual(ct, 'olpfhnvflyn') + + self.enigma.set_wheels('a', 'd', 't') + ct = self.enigma.encipher('testmessage') + self.assertEqual(ct, 'lawnjgpwjik') + + self.enigma.set_wheels('b', 'd', 'q') + ct = self.enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') + self.assertEqual(ct, + 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr') + self.assertEqual(cat(self.enigma.wheel_positions_l), 'cha') + + + def test_advance_with_ring_settings(self): + self.enigma31.set_wheels('j', 'e', 'u') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 11, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jev') + self.assertEqual(self.enigma31.peg_positions, ([7], [21], [0])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'mvqjlyowkdieasgzcunxrbhtfp') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfw') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [25])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'sjolzuyvrbwdpxcmtiaqfhknge') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfx') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [24])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'qrxedkoywufmlvgsabpzjnicht') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfy') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [23])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'hpsukliagqefwvtbjxcodnmrzy') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (4, 12, 2)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jfz') + self.assertEqual(self.enigma31.peg_positions, ([7], [20], [22])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'zevnbpyqowrtxdifhkulscjmga') + + + def test_advance_with_ring_settings_2(self): + self.enigma31.set_wheels('i', 'd', 'z') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 3)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ida') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [21])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'ikhpqrvcambzjondefwyxgsutl') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 4)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idb') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [20])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'cdabskhgzwfmlqvunyexpojtri') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 5)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idc') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [19])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'pcbwiqhgemyvjsuaftnroldzkx') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 6)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idd') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [18])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'xcbfvdnouptmlghjzwykierasq') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 7)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ide') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [17])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'xfvglbdynuseriwqpmkzjcoaht') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 8)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idf') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [16])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'tfpqlbouynsewjgcdxkahzmriv') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 9)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idg') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [15])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'cjaunvlwtbygzexrspqidfhokm') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 10)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idh') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [14])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'yltxkrqvowebzpingfucshjdam') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 11)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idi') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [13])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'myktluzrnxceaiqsohpdfwvjbg') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 12)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idj') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [12])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'pynjrmiugdqxfcvakewzhoslbt') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 13)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idk') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [11])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'mwvedyplnoxhaijgrqtszcbkfu') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 14)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idl') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [10])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'qcbrfeutvoxpnmjladzhgiykws') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 15)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idm') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [9])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'dnoahryetsmukbcvwfjilpqzgx') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 16)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idn') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [8])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'nidcfehgbqsovalyjzkxwmutpr') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 17)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ido') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [7])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'joifxdulcarhzpbntkwqgysevm') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 18)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idp') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [6])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'ptnlsxvozmwdjchayuebrgkfqi') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 19)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idq') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [5])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'slwopzqnmxybihdeguavrtcjkf') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 20)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idr') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [4])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'hcbedwlamzogixkytsrqvufnpj') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 21)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ids') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [3])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'odxbjwzrmelkisavuhnyqpfctg') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 22)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idt') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [2])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'udgbfeclrwnhxksvtioqapjmzy') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 23)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idu') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [1])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'nrdczqxmowvshaiufblypkjgte') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 10, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'idv') + self.assertEqual(self.enigma31.peg_positions, ([8], [22], [0])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'hkifjdoacebqtzgulyvmpsxwrn') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 11, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'iew') + self.assertEqual(self.enigma31.peg_positions, ([8], [21], [25])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'yptzuhofqvnmlkgbixwcejsrad') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 11, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'iex') + self.assertEqual(self.enigma31.peg_positions, ([8], [21], [24])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'vkdcwhqfjibzsptngumoraeyxl') + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (3, 11, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'iey') + self.assertEqual(self.enigma31.peg_positions, ([8], [21], [23])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'wenpbqrouxlkychdfgzvitajms') + + def test_double_advance_with_ring_settings_2(self): + self.enigma31.set_wheels('a', 'y', 't') + self.assertEqual(self.enigma31.wheel_positions, (21, 5, 22)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayt') + self.assertEqual(self.enigma31.peg_positions, ([16], [1], [2])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (21, 5, 23)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayu') + self.assertEqual(self.enigma31.peg_positions, ([16], [1], [1])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (21, 5, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayv') + self.assertEqual(self.enigma31.peg_positions, ([16], [1], [0])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (21, 6, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azw') + self.assertEqual(self.enigma31.peg_positions, ([16], [0], [25])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bax') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [24])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bay') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [23])) + + self.enigma31.set_wheels('a', 'z', 't') + self.assertEqual(self.enigma31.wheel_positions, (21, 6, 22)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azt') + self.assertEqual(self.enigma31.peg_positions, ([16], [0], [2])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 23)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bau') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [1])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 7, 24)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bav') + self.assertEqual(self.enigma31.peg_positions, ([15], [25], [0])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 8, 25)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbw') + self.assertEqual(self.enigma31.peg_positions, ([15], [24], [25])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 8, 0)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbx') + self.assertEqual(self.enigma31.peg_positions, ([15], [24], [24])) + + self.enigma31.advance() + self.assertEqual(self.enigma31.wheel_positions, (22, 8, 1)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bby') + self.assertEqual(self.enigma31.peg_positions, ([15], [24], [23])) + + + def test_encipher_with_ring(self): + + self.enigma31.set_wheels('i', 'z', 'd') + ct = self.enigma31.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure') + self.assertEqual(ct, + 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') + self.assertEqual(self.enigma31.wheel_positions, (4, 9, 10)) + self.assertEqual(cat(self.enigma31.wheel_positions_l), 'jch') + self.assertEqual(self.enigma31.peg_positions, ([7], [23], [14])) + self.assertEqual(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase), + 'mopnigfuesqwadbcktjrhylzvx') + + self.enigma31.set_wheels('i', 'z', 'd') + pt = self.enigma31.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki') + self.assertEqual(pt, + 'verylongtestmessagewithanextrabitofmessageforgoodmeasure') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file