Updated for challenge 9
[cipher-tools.git] / enigma.ipynb
1 {
2 "cells": [
3 {
4 "cell_type": "markdown",
5 "metadata": {},
6 "source": [
7 "<a name=\"top\"></a>\n",
8 "# Enigma machine\n",
9 "\n",
10 "This is an implementation of an Enigma machine in Python. See below for links that describe the Enigma machine and how it was used.\n",
11 "\n",
12 "The Enigma machine has a bunch of components which can be swapped or modified to change its behaviour. The components are:\n",
13 "\n",
14 "* The plugboard\n",
15 "* The wheels (three chosen from a set of five)\n",
16 "* The reflector (two available, though generally not swapped)\n",
17 "\n",
18 "The plugboard and wheel selection were changed every day. The wheel orientation was changed every message.\n",
19 "\n",
20 "## Design sketch\n",
21 "Each of the components can be thought of as a \"letter transformer\", which take a letter and input and give a different letter as output. From a given setup (plugboard setting or wheel orientation), these transformations are deterministic: if nothing moves, the same letter input will give the same letter output. Depending on the component, forward and backward transformations can be different. For instance, the wheel I converts `a` to `e` forward, and `a` to `u` backward.\n",
22 "\n",
23 "This means we can take an object oriented approach to building the Enigma machine. The machine itself is a collection (aggregation) of components. Each component keeps track of its current state. \n",
24 "\n",
25 "The components have an inheritance hierarchy.\n",
26 "\n",
27 "* [LetterTransformer](#lettertransformer)\n",
28 " * [Plugobard](#plugboard)\n",
29 " * [Reflector](#reflector)\n",
30 " * [SimpleWheel](#simplewheel)\n",
31 " * [Wheel](#wheel)\n",
32 "\n",
33 "The `LetterTransformer` is the base class and defines the basic operations all the transformers apply. \n",
34 "\n",
35 "A `Plugboard` is a type of `LetterTransformer` that swaps only some letters, and acts the same both forward and backward. The `Reflector` acts like a `Plugboard` but with 13 pairs of swaps.\n",
36 "\n",
37 "A `SimpleWheel` has different forward and backward transforms, and also rotates. A `Wheel` is the same, but the indicator \"ring\" around the outside can be rotated around the core. This ring of a `Wheel` has a notch that can control when other `SimpleWheel`s and `Wheel`s are rotated in the Enigma.\n",
38 "\n",
39 "Note that all the logic of when the wheels rotate is controlled by the Enigma machine.\n",
40 "\n",
41 "* [Engima](#enigma)\n",
42 "* [Testing Enigma](#testingenigma)\n",
43 "\n",
44 "### Implmentation note\n",
45 "The normal way to define a class in Python is to define all the methods, class variables, and instance variables at the same time. However, that makes it difficult to place the discussion of the various methods and what they do near the definitions. \n",
46 "\n",
47 "This notebook takes a variant approach of defining the base class, then defining methods in separate cells and adding them to the class with the `setattr()` procedure.\n",
48 "\n",
49 "\n",
50 "## See also\n",
51 "* Specification from [Codes and Ciphers](http://www.codesandciphers.org.uk/enigma/rotorspec.htm) page.\n",
52 "\n",
53 "* Example Enigma machines from [Louise Dale](http://enigma.louisedade.co.uk/enigma.html) (full simulation) and [EnigmaCo](http://enigmaco.de/enigma/enigma.html) (good animation of the wheels, but no ring settings).\n",
54 "\n",
55 "* There's also the nice Enigma simulator for Android by [Franklin Heath](https://franklinheath.co.uk/2012/02/04/our-first-app-published-enigma-simulator/), available on the [Google Play store](https://play.google.com/store/apps/details?id=uk.co.franklinheath.enigmasim&hl=en_GB).\n",
56 "\n",
57 "* Enigma wiring from the [Crypto Museum](http://www.cryptomuseum.com/crypto/enigma/wiring.htm)."
58 ]
59 },
60 {
61 "cell_type": "markdown",
62 "metadata": {},
63 "source": [
64 "First, some general-purpose and utility imports. \n",
65 "\n",
66 "`pos` and `unpos` convert between letters and numbers (in range 0-25 inclusive)."
67 ]
68 },
69 {
70 "cell_type": "code",
71 "execution_count": 108,
72 "metadata": {
73 "collapsed": true
74 },
75 "outputs": [],
76 "source": [
77 "import string\n",
78 "import collections\n",
79 "\n",
80 "cat = ''.join\n",
81 "\n",
82 "def clean(text): return cat(l.lower() for l in text if l in string.ascii_letters)\n",
83 "\n",
84 "def pos(letter): \n",
85 " if letter in string.ascii_lowercase:\n",
86 " return ord(letter) - ord('a')\n",
87 " elif letter in string.ascii_uppercase:\n",
88 " return ord(letter) - ord('A')\n",
89 " else:\n",
90 " return ''\n",
91 " \n",
92 "def unpos(number): return chr(number % 26 + ord('a'))"
93 ]
94 },
95 {
96 "cell_type": "markdown",
97 "metadata": {},
98 "source": [
99 "The wheel specifications show what positions `a` to `z` (ignoring the ring) go to. For instance, Wheel 1 converts `a` to `e` forward, and `a` to `u` backward. The notch positions show where the wheel advance notches are on the wheel rings. The reflector specifications show the reflected pairs."
100 ]
101 },
102 {
103 "cell_type": "code",
104 "execution_count": 109,
105 "metadata": {
106 "collapsed": true
107 },
108 "outputs": [],
109 "source": [
110 "wheel_i_spec = 'ekmflgdqvzntowyhxuspaibrcj'\n",
111 "wheel_ii_spec = 'ajdksiruxblhwtmcqgznpyfvoe'\n",
112 "wheel_iii_spec = 'bdfhjlcprtxvznyeiwgakmusqo'\n",
113 "wheel_iv_spec = 'esovpzjayquirhxlnftgkdcmwb'\n",
114 "wheel_v_spec = 'vzbrgityupsdnhlxawmjqofeck'\n",
115 "wheel_vi_spec = 'jpgvoumfyqbenhzrdkasxlictw'\n",
116 "wheel_vii_spec = 'nzjhgrcxmyswboufaivlpekqdt'\n",
117 "wheel_viii_spec = 'fkqhtlxocbjspdzramewniuygv'\n",
118 "beta_wheel_spec = 'leyjvcnixwpbqmdrtakzgfuhos'\n",
119 "gamma_wheel_spec = 'fsokanuerhmbtiycwlqpzxvgjd'\n",
120 "\n",
121 "wheel_i_notches = ['q']\n",
122 "wheel_ii_notches = ['e']\n",
123 "wheel_iii_notches = ['v']\n",
124 "wheel_iv_notches = ['j']\n",
125 "wheel_v_notches = ['z']\n",
126 "wheel_vi_notches = ['z', 'm']\n",
127 "wheel_vii_notches = ['z', 'm']\n",
128 "wheel_viii_notches = ['z', 'm']\n",
129 "\n",
130 "reflector_b_spec = 'ay br cu dh eq fs gl ip jx kn mo tz vw'\n",
131 "reflector_c_spec = 'af bv cp dj ei go hy kr lz mx nw tq su'"
132 ]
133 },
134 {
135 "cell_type": "code",
136 "execution_count": 110,
137 "metadata": {
138 "collapsed": true
139 },
140 "outputs": [],
141 "source": [
142 "# class LetterTransformer(object):\n",
143 "# def __init__(self, specification, raw_transform=False):\n",
144 "# if raw_transform:\n",
145 "# transform = specification\n",
146 "# else:\n",
147 "# transform = self.parse_specification(specification)\n",
148 "# self.validate_transform(transform)\n",
149 "# self.make_transform_map(transform)\n",
150 " \n",
151 "# def parse_specification(self, specification):\n",
152 "# return list(zip(string.ascii_lowercase, clean(specification)))\n",
153 "# # return specification\n",
154 " \n",
155 "# def validate_transform(self, transform):\n",
156 "# \"\"\"A set of pairs, of from-to\"\"\"\n",
157 "# if len(transform) != 26:\n",
158 "# raise ValueError(\"Transform specification has {} pairs, requires 26\".\n",
159 "# format(len(transform)))\n",
160 "# for p in transform:\n",
161 "# if len(p) != 2:\n",
162 "# raise ValueError(\"Not all mappings in transform \"\n",
163 "# \"have two elements\")\n",
164 "# if len(set([p[0] for p in transform])) != 26:\n",
165 "# raise ValueError(\"Transform specification must list 26 origin letters\") \n",
166 "# if len(set([p[1] for p in transform])) != 26:\n",
167 "# raise ValueError(\"Transform specification must list 26 destination letters\") \n",
168 "\n",
169 "# def make_empty_transform(self):\n",
170 "# self.forward_map = [0] * 26\n",
171 "# self.backward_map = [0] * 26\n",
172 " \n",
173 "# def make_transform_map(self, transform):\n",
174 "# self.make_empty_transform()\n",
175 "# for p in transform:\n",
176 "# self.forward_map[pos(p[0])] = pos(p[1])\n",
177 "# self.backward_map[pos(p[1])] = pos(p[0])\n",
178 "# return self.forward_map, self.backward_map\n",
179 " \n",
180 "# def forward(self, letter):\n",
181 "# if letter in string.ascii_lowercase:\n",
182 "# return unpos(self.forward_map[pos(letter)])\n",
183 "# else:\n",
184 "# return ''\n",
185 " \n",
186 "# def backward(self, letter):\n",
187 "# if letter in string.ascii_lowercase:\n",
188 "# return unpos(self.backward_map[pos(letter)])\n",
189 "# else:\n",
190 "# return ''"
191 ]
192 },
193 {
194 "cell_type": "markdown",
195 "metadata": {},
196 "source": [
197 "<a name=\"lettertransformer\"></a>\n",
198 "# Letter transformer\n",
199 "[Top](#top)\n",
200 "\n",
201 "A generic transformer of letters. All components in the Enigma are based on this. \n",
202 "\n",
203 "The transformer has two directions, `forward` and `backward`. In each direction, a given letter is transformed into a different letter. Both transformations are general permutations of the alphabet (i.e. each letter goes to one and only one new letter). There is no general requirement for the `forward` and `backward` transformations to have any particular relationship to each other (even though most do in the Enigma machine).\n",
204 "\n",
205 "When created, it must be given the transformation which should be applied. A raw transform is a sequence of letter pairs, such that `p[0]` is transformed to `p[1]` forwards, and `p[1]` goes to `p[0]` backwards.\n",
206 "\n",
207 "If the transform is not raw, it's assumed that the specification is a sequence of the `p[1]`s, and the standard alphabet gives the `p[0]`s."
208 ]
209 },
210 {
211 "cell_type": "code",
212 "execution_count": 111,
213 "metadata": {
214 "collapsed": true
215 },
216 "outputs": [],
217 "source": [
218 "class LetterTransformer(object):\n",
219 " def __init__(self, specification, raw_transform=False):\n",
220 " if raw_transform:\n",
221 " transform = specification\n",
222 " else:\n",
223 " transform = self.parse_specification(specification)\n",
224 " self.validate_transform(transform)\n",
225 " self.make_transform_map(transform)"
226 ]
227 },
228 {
229 "cell_type": "markdown",
230 "metadata": {},
231 "source": [
232 "Parse a specification: convert a string of destination letters into a list of pairs. "
233 ]
234 },
235 {
236 "cell_type": "code",
237 "execution_count": 112,
238 "metadata": {
239 "collapsed": true
240 },
241 "outputs": [],
242 "source": [
243 "def parse_specification(self, specification):\n",
244 " return list(zip(string.ascii_lowercase, clean(specification)))\n",
245 " # return specification\n",
246 "\n",
247 "setattr(LetterTransformer, \"parse_specification\", parse_specification)"
248 ]
249 },
250 {
251 "cell_type": "code",
252 "execution_count": 113,
253 "metadata": {},
254 "outputs": [
255 {
256 "data": {
257 "text/plain": [
258 "[('a', 'e'),\n",
259 " ('b', 'k'),\n",
260 " ('c', 'm'),\n",
261 " ('d', 'f'),\n",
262 " ('e', 'l'),\n",
263 " ('f', 'g'),\n",
264 " ('g', 'd'),\n",
265 " ('h', 'q'),\n",
266 " ('i', 'v'),\n",
267 " ('j', 'z'),\n",
268 " ('k', 'n'),\n",
269 " ('l', 't'),\n",
270 " ('m', 'o'),\n",
271 " ('n', 'w'),\n",
272 " ('o', 'y'),\n",
273 " ('p', 'h'),\n",
274 " ('q', 'x'),\n",
275 " ('r', 'u'),\n",
276 " ('s', 's'),\n",
277 " ('t', 'p'),\n",
278 " ('u', 'a'),\n",
279 " ('v', 'i'),\n",
280 " ('w', 'b'),\n",
281 " ('x', 'r'),\n",
282 " ('y', 'c'),\n",
283 " ('z', 'j')]"
284 ]
285 },
286 "execution_count": 113,
287 "metadata": {},
288 "output_type": "execute_result"
289 }
290 ],
291 "source": [
292 "parse_specification(None, wheel_i_spec)"
293 ]
294 },
295 {
296 "cell_type": "markdown",
297 "metadata": {},
298 "source": [
299 "Checks that a transform is valid."
300 ]
301 },
302 {
303 "cell_type": "code",
304 "execution_count": 114,
305 "metadata": {
306 "collapsed": true
307 },
308 "outputs": [],
309 "source": [
310 "def validate_transform(self, transform):\n",
311 " \"\"\"A set of pairs, of from-to\"\"\"\n",
312 " if len(transform) != 26:\n",
313 " raise ValueError(\"Transform specification has {} pairs, requires 26\".\n",
314 " format(len(transform)))\n",
315 " for p in transform:\n",
316 " if len(p) != 2:\n",
317 " raise ValueError(\"Not all mappings in transform \"\n",
318 " \"have two elements\")\n",
319 " if len(set([p[0] for p in transform])) != 26:\n",
320 " raise ValueError(\"Transform specification must list 26 origin letters\") \n",
321 " if len(set([p[1] for p in transform])) != 26:\n",
322 " raise ValueError(\"Transform specification must list 26 destination letters\") \n",
323 "\n",
324 "setattr(LetterTransformer, \"validate_transform\", validate_transform) "
325 ]
326 },
327 {
328 "cell_type": "markdown",
329 "metadata": {},
330 "source": [
331 "The empty transform maps each letter to itself, forward and backward. A useful starting point for creating the maps needed.\n",
332 "\n",
333 "The forward and backward maps are `list`s of numbers (rather than `dict`s of letters to letters) to make the calculations easier when it comes to the wheels, and wheels with turnable indicator rings."
334 ]
335 },
336 {
337 "cell_type": "code",
338 "execution_count": 115,
339 "metadata": {
340 "collapsed": true
341 },
342 "outputs": [],
343 "source": [
344 "# def make_empty_transform(self):\n",
345 "# self.forward_map = [0] * 26\n",
346 "# self.backward_map = [0] * 26\n",
347 "\n",
348 "# setattr(LetterTransformer, \"make_empty_transform\", make_empty_transform) "
349 ]
350 },
351 {
352 "cell_type": "code",
353 "execution_count": 116,
354 "metadata": {
355 "collapsed": true
356 },
357 "outputs": [],
358 "source": [
359 "def make_empty_transform(self):\n",
360 " self.forward_map = list(range(26))\n",
361 " self.backward_map = list(range(26))\n",
362 "\n",
363 "setattr(LetterTransformer, \"make_empty_transform\", make_empty_transform) "
364 ]
365 },
366 {
367 "cell_type": "markdown",
368 "metadata": {},
369 "source": [
370 "Make the transform. Starting from an empty transform, mutate it to include the swaps. Note that the forward and backward swaps are stored separately. "
371 ]
372 },
373 {
374 "cell_type": "code",
375 "execution_count": 117,
376 "metadata": {
377 "collapsed": true
378 },
379 "outputs": [],
380 "source": [
381 "def make_transform_map(self, transform):\n",
382 " self.make_empty_transform()\n",
383 " for p in transform:\n",
384 " self.forward_map[pos(p[0])] = pos(p[1])\n",
385 " self.backward_map[pos(p[1])] = pos(p[0])\n",
386 " return self.forward_map, self.backward_map\n",
387 "\n",
388 "setattr(LetterTransformer, \"make_transform_map\", make_transform_map)"
389 ]
390 },
391 {
392 "cell_type": "code",
393 "execution_count": 118,
394 "metadata": {
395 "collapsed": true
396 },
397 "outputs": [],
398 "source": [
399 "def forward(self, letter):\n",
400 " if letter in string.ascii_lowercase:\n",
401 " return unpos(self.forward_map[pos(letter)])\n",
402 " else:\n",
403 " return ''\n",
404 "\n",
405 "def backward(self, letter):\n",
406 " if letter in string.ascii_lowercase:\n",
407 " return unpos(self.backward_map[pos(letter)])\n",
408 " else:\n",
409 " return ''\n",
410 "\n",
411 "setattr(LetterTransformer, \"forward\", forward)\n",
412 "setattr(LetterTransformer, \"backward\", backward) "
413 ]
414 },
415 {
416 "cell_type": "code",
417 "execution_count": 119,
418 "metadata": {},
419 "outputs": [
420 {
421 "data": {
422 "text/plain": [
423 "[('z', 'a'),\n",
424 " ('a', 'b'),\n",
425 " ('b', 'c'),\n",
426 " ('c', 'd'),\n",
427 " ('d', 'e'),\n",
428 " ('e', 'f'),\n",
429 " ('f', 'g'),\n",
430 " ('g', 'h'),\n",
431 " ('h', 'i'),\n",
432 " ('i', 'j'),\n",
433 " ('j', 'k'),\n",
434 " ('k', 'l'),\n",
435 " ('l', 'm'),\n",
436 " ('m', 'n'),\n",
437 " ('n', 'o'),\n",
438 " ('o', 'p'),\n",
439 " ('p', 'q'),\n",
440 " ('q', 'r'),\n",
441 " ('r', 's'),\n",
442 " ('s', 't'),\n",
443 " ('t', 'u'),\n",
444 " ('u', 'v'),\n",
445 " ('v', 'w'),\n",
446 " ('w', 'x'),\n",
447 " ('x', 'y'),\n",
448 " ('y', 'z')]"
449 ]
450 },
451 "execution_count": 119,
452 "metadata": {},
453 "output_type": "execute_result"
454 }
455 ],
456 "source": [
457 "tmap = [('z', 'a')] + [(l, string.ascii_lowercase[i+1]) for i, l in enumerate(string.ascii_lowercase[:-1])]\n",
458 "tmap"
459 ]
460 },
461 {
462 "cell_type": "code",
463 "execution_count": 120,
464 "metadata": {},
465 "outputs": [
466 {
467 "data": {
468 "text/plain": [
469 "'zyxwcabdefghijklmnopqrstuv'"
470 ]
471 },
472 "execution_count": 120,
473 "metadata": {},
474 "output_type": "execute_result"
475 }
476 ],
477 "source": [
478 "cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))"
479 ]
480 },
481 {
482 "cell_type": "code",
483 "execution_count": 121,
484 "metadata": {
485 "collapsed": true,
486 "scrolled": true
487 },
488 "outputs": [],
489 "source": [
490 "tmap2 = list(zip(string.ascii_lowercase, cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase))))"
491 ]
492 },
493 {
494 "cell_type": "code",
495 "execution_count": 122,
496 "metadata": {
497 "scrolled": true
498 },
499 "outputs": [
500 {
501 "data": {
502 "text/plain": [
503 "([1,\n",
504 " 2,\n",
505 " 3,\n",
506 " 4,\n",
507 " 5,\n",
508 " 6,\n",
509 " 7,\n",
510 " 8,\n",
511 " 9,\n",
512 " 10,\n",
513 " 11,\n",
514 " 12,\n",
515 " 13,\n",
516 " 14,\n",
517 " 15,\n",
518 " 16,\n",
519 " 17,\n",
520 " 18,\n",
521 " 19,\n",
522 " 20,\n",
523 " 21,\n",
524 " 22,\n",
525 " 23,\n",
526 " 24,\n",
527 " 25,\n",
528 " 0],\n",
529 " [25,\n",
530 " 0,\n",
531 " 1,\n",
532 " 2,\n",
533 " 3,\n",
534 " 4,\n",
535 " 5,\n",
536 " 6,\n",
537 " 7,\n",
538 " 8,\n",
539 " 9,\n",
540 " 10,\n",
541 " 11,\n",
542 " 12,\n",
543 " 13,\n",
544 " 14,\n",
545 " 15,\n",
546 " 16,\n",
547 " 17,\n",
548 " 18,\n",
549 " 19,\n",
550 " 20,\n",
551 " 21,\n",
552 " 22,\n",
553 " 23,\n",
554 " 24])"
555 ]
556 },
557 "execution_count": 122,
558 "metadata": {},
559 "output_type": "execute_result"
560 }
561 ],
562 "source": [
563 "lt = LetterTransformer(tmap, raw_transform = True)\n",
564 "assert(lt.forward_map == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0])\n",
565 "assert(lt.backward_map == [25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24])\n",
566 "lt.forward_map, lt.backward_map"
567 ]
568 },
569 {
570 "cell_type": "code",
571 "execution_count": 123,
572 "metadata": {
573 "scrolled": true
574 },
575 "outputs": [
576 {
577 "data": {
578 "text/plain": [
579 "([25,\n",
580 " 24,\n",
581 " 23,\n",
582 " 22,\n",
583 " 2,\n",
584 " 0,\n",
585 " 1,\n",
586 " 3,\n",
587 " 4,\n",
588 " 5,\n",
589 " 6,\n",
590 " 7,\n",
591 " 8,\n",
592 " 9,\n",
593 " 10,\n",
594 " 11,\n",
595 " 12,\n",
596 " 13,\n",
597 " 14,\n",
598 " 15,\n",
599 " 16,\n",
600 " 17,\n",
601 " 18,\n",
602 " 19,\n",
603 " 20,\n",
604 " 21],\n",
605 " [5,\n",
606 " 6,\n",
607 " 4,\n",
608 " 7,\n",
609 " 8,\n",
610 " 9,\n",
611 " 10,\n",
612 " 11,\n",
613 " 12,\n",
614 " 13,\n",
615 " 14,\n",
616 " 15,\n",
617 " 16,\n",
618 " 17,\n",
619 " 18,\n",
620 " 19,\n",
621 " 20,\n",
622 " 21,\n",
623 " 22,\n",
624 " 23,\n",
625 " 24,\n",
626 " 25,\n",
627 " 3,\n",
628 " 2,\n",
629 " 1,\n",
630 " 0])"
631 ]
632 },
633 "execution_count": 123,
634 "metadata": {},
635 "output_type": "execute_result"
636 }
637 ],
638 "source": [
639 "lt = LetterTransformer(cat(collections.OrderedDict.fromkeys('zyxwc' + string.ascii_lowercase)))\n",
640 "assert(lt.forward_map == [25, 24, 23, 22, 2, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21])\n",
641 "assert(lt.backward_map == [5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 3, 2, 1, 0])\n",
642 "assert(cat(lt.forward(l) for l in string.ascii_lowercase) == 'zyxwcabdefghijklmnopqrstuv')\n",
643 "assert(cat(lt.backward(l) for l in string.ascii_lowercase) == 'fgehijklmnopqrstuvwxyzdcba')\n",
644 "lt.forward_map, lt.backward_map"
645 ]
646 },
647 {
648 "cell_type": "code",
649 "execution_count": 124,
650 "metadata": {},
651 "outputs": [
652 {
653 "data": {
654 "text/plain": [
655 "'zyxwcabdefghijklmnopqrstuv'"
656 ]
657 },
658 "execution_count": 124,
659 "metadata": {},
660 "output_type": "execute_result"
661 }
662 ],
663 "source": [
664 "cat(lt.forward(l) for l in string.ascii_lowercase)"
665 ]
666 },
667 {
668 "cell_type": "code",
669 "execution_count": 125,
670 "metadata": {},
671 "outputs": [
672 {
673 "data": {
674 "text/plain": [
675 "'fgehijklmnopqrstuvwxyzdcba'"
676 ]
677 },
678 "execution_count": 125,
679 "metadata": {},
680 "output_type": "execute_result"
681 }
682 ],
683 "source": [
684 "cat(lt.backward(l) for l in string.ascii_lowercase)"
685 ]
686 },
687 {
688 "cell_type": "markdown",
689 "metadata": {},
690 "source": [
691 "<a name=\"plugboard\"></a>\n",
692 "## Plugboard\n",
693 "[Top](#top)\n",
694 "\n",
695 "A `Plugboard` is a `LetterTransformer` that swaps some pairs of letters, and does the same swaps forward and backward."
696 ]
697 },
698 {
699 "cell_type": "code",
700 "execution_count": 126,
701 "metadata": {
702 "collapsed": true
703 },
704 "outputs": [],
705 "source": [
706 "class Plugboard(LetterTransformer):\n",
707 " def parse_specification(self, specification):\n",
708 " return [tuple(clean(p)) for p in specification.split()]\n",
709 " \n",
710 " def validate_transform(self, transform):\n",
711 " \"\"\"A set of pairs, of from-to\"\"\"\n",
712 " for p in transform:\n",
713 " if len(p) != 2:\n",
714 " raise ValueError(\"Not all mappings in transform\"\n",
715 " \"have two elements\")\n",
716 " \n",
717 "# def make_empty_transform(self):\n",
718 "# self.forward_map = list(range(26))\n",
719 "# self.backward_map = list(range(26))\n",
720 " \n",
721 " def make_transform_map(self, transform):\n",
722 " expanded_transform = transform + [tuple(reversed(p)) for p in transform]\n",
723 " return super(Plugboard, self).make_transform_map(expanded_transform)"
724 ]
725 },
726 {
727 "cell_type": "code",
728 "execution_count": 127,
729 "metadata": {
730 "collapsed": true
731 },
732 "outputs": [],
733 "source": [
734 "pb = Plugboard([('a', 'z'), ('b', 'y')], raw_transform=True)"
735 ]
736 },
737 {
738 "cell_type": "code",
739 "execution_count": 128,
740 "metadata": {},
741 "outputs": [
742 {
743 "data": {
744 "text/plain": [
745 "'zycdefghijklmnopqrstuvwxba'"
746 ]
747 },
748 "execution_count": 128,
749 "metadata": {},
750 "output_type": "execute_result"
751 }
752 ],
753 "source": [
754 "cat(pb.forward(l) for l in string.ascii_lowercase)"
755 ]
756 },
757 {
758 "cell_type": "code",
759 "execution_count": 129,
760 "metadata": {},
761 "outputs": [
762 {
763 "data": {
764 "text/plain": [
765 "'zycdefghijklmnopqrstuvwxba'"
766 ]
767 },
768 "execution_count": 129,
769 "metadata": {},
770 "output_type": "execute_result"
771 }
772 ],
773 "source": [
774 "cat(pb.backward(l) for l in string.ascii_lowercase)"
775 ]
776 },
777 {
778 "cell_type": "code",
779 "execution_count": 130,
780 "metadata": {
781 "collapsed": true
782 },
783 "outputs": [],
784 "source": [
785 "pb = Plugboard('az by')"
786 ]
787 },
788 {
789 "cell_type": "code",
790 "execution_count": 131,
791 "metadata": {},
792 "outputs": [
793 {
794 "data": {
795 "text/plain": [
796 "('zycdefghijklmnopqrstuvwxba', 'zycdefghijklmnopqrstuvwxba')"
797 ]
798 },
799 "execution_count": 131,
800 "metadata": {},
801 "output_type": "execute_result"
802 }
803 ],
804 "source": [
805 "cat(pb.forward(l) for l in string.ascii_lowercase), cat(pb.backward(l) for l in string.ascii_lowercase)"
806 ]
807 },
808 {
809 "cell_type": "code",
810 "execution_count": 132,
811 "metadata": {},
812 "outputs": [
813 {
814 "data": {
815 "text/plain": [
816 "('ugcdypblnzkhmisfrqoxavwtej', 'ugcdypblnzkhmisfrqoxavwtej')"
817 ]
818 },
819 "execution_count": 132,
820 "metadata": {},
821 "output_type": "execute_result"
822 }
823 ],
824 "source": [
825 "pb = Plugboard('ua pf rq so ni ey bg hl tx zj'.upper())\n",
826 "assert(pb.forward_map == pb.backward_map)\n",
827 "assert(pb.forward_map == [20, 6, 2, 3, 24, 15, 1, 11, 13, 25, 10, 7, 12, 8, 18, 5, 17, 16, 14, 23, 0, 21, 22, 19, 4, 9])\n",
828 "assert(cat(pb.forward(l) for l in string.ascii_lowercase) == 'ugcdypblnzkhmisfrqoxavwtej')\n",
829 "assert(cat(pb.backward(l) for l in string.ascii_lowercase) == 'ugcdypblnzkhmisfrqoxavwtej')\n",
830 "cat(pb.forward(l) for l in string.ascii_lowercase), cat(pb.backward(l) for l in string.ascii_lowercase)"
831 ]
832 },
833 {
834 "cell_type": "markdown",
835 "metadata": {},
836 "source": [
837 "<a name=\"reflector\"></a>\n",
838 "## Reflector\n",
839 "[Top](#top)\n",
840 "\n",
841 "A `Reflector` is a `Plugboard` that takes exactly 13 pairs of letters to swap."
842 ]
843 },
844 {
845 "cell_type": "code",
846 "execution_count": 133,
847 "metadata": {
848 "collapsed": true
849 },
850 "outputs": [],
851 "source": [
852 "class Reflector(Plugboard):\n",
853 " def validate_transform(self, transform):\n",
854 " if len(transform) != 13:\n",
855 " raise ValueError(\"Reflector specification has {} pairs, requires 13\".\n",
856 " format(len(transform)))\n",
857 " if len(set([p[0] for p in transform] + \n",
858 " [p[1] for p in transform])) != 26:\n",
859 " raise ValueError(\"Reflector specification does not contain 26 letters\")\n",
860 " try:\n",
861 " super(Reflector, self).validate_transform(transform)\n",
862 " except ValueError as v:\n",
863 " raise ValueError(\"Not all mappings in reflector have two elements\")"
864 ]
865 },
866 {
867 "cell_type": "code",
868 "execution_count": 134,
869 "metadata": {},
870 "outputs": [
871 {
872 "data": {
873 "text/plain": [
874 "[('a', 'y'),\n",
875 " ('b', 'r'),\n",
876 " ('c', 'u'),\n",
877 " ('d', 'h'),\n",
878 " ('e', 'q'),\n",
879 " ('f', 's'),\n",
880 " ('g', 'l'),\n",
881 " ('i', 'p'),\n",
882 " ('j', 'x'),\n",
883 " ('k', 'n'),\n",
884 " ('m', 'o'),\n",
885 " ('t', 'z'),\n",
886 " ('v', 'w')]"
887 ]
888 },
889 "execution_count": 134,
890 "metadata": {},
891 "output_type": "execute_result"
892 }
893 ],
894 "source": [
895 "# reflector_b_text = '(AY) (BR) (CU) (DH) (EQ) (FS) (GL) (IP) (JX) (KN) (MO) (TZ) (VW)'\n",
896 "reflector_b_l = [tuple(clean(p)) for p in reflector_b_spec.split()]\n",
897 "reflector_b_l"
898 ]
899 },
900 {
901 "cell_type": "code",
902 "execution_count": 135,
903 "metadata": {
904 "collapsed": true
905 },
906 "outputs": [],
907 "source": [
908 "reflector_b = Reflector(reflector_b_spec)\n",
909 "assert(reflector_b.forward_map == reflector_b.backward_map)\n",
910 "assert(reflector_b.forward_map == [24, 17, 20, 7, 16, 18, 11, 3, 15, 23, 13, 6, 14, 10, 12, 8, 4, 1, 5, 25, 2, 22, 21, 9, 0, 19])\n",
911 "assert(cat(reflector_b.forward(l) for l in string.ascii_lowercase) == 'yruhqsldpxngokmiebfzcwvjat')\n",
912 "assert(cat(reflector_b.backward(l) for l in string.ascii_lowercase) == 'yruhqsldpxngokmiebfzcwvjat')"
913 ]
914 },
915 {
916 "cell_type": "code",
917 "execution_count": 136,
918 "metadata": {},
919 "outputs": [
920 {
921 "data": {
922 "text/plain": [
923 "'yruhqsldpxngokmiebfzcwvjat'"
924 ]
925 },
926 "execution_count": 136,
927 "metadata": {},
928 "output_type": "execute_result"
929 }
930 ],
931 "source": [
932 "cat(reflector_b.forward(l) for l in string.ascii_lowercase)"
933 ]
934 },
935 {
936 "cell_type": "code",
937 "execution_count": 137,
938 "metadata": {
939 "collapsed": true
940 },
941 "outputs": [],
942 "source": [
943 "reflector_c = Reflector(reflector_c_spec)"
944 ]
945 },
946 {
947 "cell_type": "code",
948 "execution_count": 138,
949 "metadata": {},
950 "outputs": [
951 {
952 "data": {
953 "text/plain": [
954 "'fvpjiaoyedrzxwgctkuqsbnmhl'"
955 ]
956 },
957 "execution_count": 138,
958 "metadata": {},
959 "output_type": "execute_result"
960 }
961 ],
962 "source": [
963 "cat(reflector_c.forward(l) for l in string.ascii_lowercase)"
964 ]
965 },
966 {
967 "cell_type": "markdown",
968 "metadata": {},
969 "source": [
970 "<a name=\"simplewheel\"></a>\n",
971 "## SimpleWheel\n",
972 "[Top](#top)\n",
973 "\n",
974 "A `SimpleWheel` has different forward and backward maps, and also a position. The position is set with the `set_position` method (and initially in the creator), and the wheel can advance using the `advance` method. \n",
975 "\n",
976 "How the position is used is best explained with an example. The Enigma wheel 1, in the neutral position, transforms `a` to `e` (+4 letters) and `b` to `k` (+10 letters). When the wheel is in position `b` and an `a` in enciphered, it's the _second_ element of the map that's used, so `a` would be advanced 10 letters, to give `j`.\n",
977 "\n",
978 "This means that when using the letter transformation maps, you use the element in the map that's offset by the position of the wheel. When enciphering a `c`, you'd normally use transformation at position 2 in the map; if the wheel is in position 7, you'd instead use the transform at position 2 + 7 = 9 in the map.\n",
979 "\n",
980 "There are various modulus operators to keep the numbers in the requried range, meaning you can wrap around the map and around the wheel.\n",
981 "\n",
982 "Note the use of `__getattribute__` to give a more human-friendly version of the position without making it a method call. That allows you to write `wheel.position` and `wheel.position_l` and get the appropriate answers."
983 ]
984 },
985 {
986 "cell_type": "code",
987 "execution_count": 139,
988 "metadata": {
989 "collapsed": true
990 },
991 "outputs": [],
992 "source": [
993 "class SimpleWheel(LetterTransformer):\n",
994 " def __init__(self, transform, position='a', raw_transform=False):\n",
995 " super(SimpleWheel, self).__init__(transform, raw_transform)\n",
996 " self.set_position(position)\n",
997 " \n",
998 " def __getattribute__(self, name):\n",
999 " if name=='position_l':\n",
1000 " return unpos(self.position)\n",
1001 " else:\n",
1002 " return object.__getattribute__(self, name) "
1003 ]
1004 },
1005 {
1006 "cell_type": "markdown",
1007 "metadata": {},
1008 "source": [
1009 "Set the wheel to a new position. Note that it expects a letter, not a number."
1010 ]
1011 },
1012 {
1013 "cell_type": "code",
1014 "execution_count": 140,
1015 "metadata": {
1016 "collapsed": true
1017 },
1018 "outputs": [],
1019 "source": [
1020 "def set_position(self, position):\n",
1021 " self.position = ord(position) - ord('a')\n",
1022 " \n",
1023 "setattr(SimpleWheel, 'set_position', set_position) "
1024 ]
1025 },
1026 {
1027 "cell_type": "markdown",
1028 "metadata": {},
1029 "source": [
1030 "Advance the wheel one step. Note that advancing beyond position 25 moves back to 0."
1031 ]
1032 },
1033 {
1034 "cell_type": "code",
1035 "execution_count": 141,
1036 "metadata": {
1037 "collapsed": true
1038 },
1039 "outputs": [],
1040 "source": [
1041 "def advance(self):\n",
1042 " self.position = (self.position + 1) % 26\n",
1043 " return self.position\n",
1044 "\n",
1045 "setattr(SimpleWheel, 'advance', advance) "
1046 ]
1047 },
1048 {
1049 "cell_type": "markdown",
1050 "metadata": {},
1051 "source": [
1052 "Do the encipherment forward and backward. Note how the map element to use is affected by the wheel position, and how the modulus wraps that map element around the wheel if needed."
1053 ]
1054 },
1055 {
1056 "cell_type": "code",
1057 "execution_count": 142,
1058 "metadata": {
1059 "collapsed": true
1060 },
1061 "outputs": [],
1062 "source": [
1063 "def forward(self, letter):\n",
1064 " if letter in string.ascii_lowercase:\n",
1065 " return unpos((self.forward_map[(pos(letter) + self.position) % 26] - self.position))\n",
1066 " else:\n",
1067 " return ''\n",
1068 "\n",
1069 "def backward(self, letter):\n",
1070 " if letter in string.ascii_lowercase:\n",
1071 " return unpos((self.backward_map[(pos(letter) + self.position) % 26] - self.position))\n",
1072 " else:\n",
1073 " return ''\n",
1074 " \n",
1075 "setattr(SimpleWheel, 'forward', forward) \n",
1076 "setattr(SimpleWheel, 'backward', backward) "
1077 ]
1078 },
1079 {
1080 "cell_type": "code",
1081 "execution_count": 143,
1082 "metadata": {
1083 "scrolled": true
1084 },
1085 "outputs": [
1086 {
1087 "data": {
1088 "text/plain": [
1089 "[('a', 'e'),\n",
1090 " ('b', 'k'),\n",
1091 " ('c', 'm'),\n",
1092 " ('d', 'f'),\n",
1093 " ('e', 'l'),\n",
1094 " ('f', 'g'),\n",
1095 " ('g', 'd'),\n",
1096 " ('h', 'q'),\n",
1097 " ('i', 'v'),\n",
1098 " ('j', 'z'),\n",
1099 " ('k', 'n'),\n",
1100 " ('l', 't'),\n",
1101 " ('m', 'o'),\n",
1102 " ('n', 'w'),\n",
1103 " ('o', 'y'),\n",
1104 " ('p', 'h'),\n",
1105 " ('q', 'x'),\n",
1106 " ('r', 'u'),\n",
1107 " ('s', 's'),\n",
1108 " ('t', 'p'),\n",
1109 " ('u', 'a'),\n",
1110 " ('v', 'i'),\n",
1111 " ('w', 'b'),\n",
1112 " ('x', 'r'),\n",
1113 " ('y', 'c'),\n",
1114 " ('z', 'j')]"
1115 ]
1116 },
1117 "execution_count": 143,
1118 "metadata": {},
1119 "output_type": "execute_result"
1120 }
1121 ],
1122 "source": [
1123 "rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower()))\n",
1124 "rotor_1_transform"
1125 ]
1126 },
1127 {
1128 "cell_type": "code",
1129 "execution_count": 144,
1130 "metadata": {
1131 "collapsed": true
1132 },
1133 "outputs": [],
1134 "source": [
1135 "rotor_1_transform = list(zip(string.ascii_lowercase, 'EKMFLGDQVZNTOWYHXUSPAIBRCJ'.lower()))\n",
1136 "wheel_1 = SimpleWheel(rotor_1_transform, raw_transform=True)\n",
1137 "assert(cat(wheel_1.forward(l) for l in string.ascii_lowercase) == 'ekmflgdqvzntowyhxuspaibrcj')\n",
1138 "assert(cat(wheel_1.backward(l) for l in string.ascii_lowercase) == 'uwygadfpvzbeckmthxslrinqoj')"
1139 ]
1140 },
1141 {
1142 "cell_type": "code",
1143 "execution_count": 145,
1144 "metadata": {
1145 "scrolled": true
1146 },
1147 "outputs": [
1148 {
1149 "data": {
1150 "text/plain": [
1151 "[4,\n",
1152 " 10,\n",
1153 " 12,\n",
1154 " 5,\n",
1155 " 11,\n",
1156 " 6,\n",
1157 " 3,\n",
1158 " 16,\n",
1159 " 21,\n",
1160 " 25,\n",
1161 " 13,\n",
1162 " 19,\n",
1163 " 14,\n",
1164 " 22,\n",
1165 " 24,\n",
1166 " 7,\n",
1167 " 23,\n",
1168 " 20,\n",
1169 " 18,\n",
1170 " 15,\n",
1171 " 0,\n",
1172 " 8,\n",
1173 " 1,\n",
1174 " 17,\n",
1175 " 2,\n",
1176 " 9]"
1177 ]
1178 },
1179 "execution_count": 145,
1180 "metadata": {},
1181 "output_type": "execute_result"
1182 }
1183 ],
1184 "source": [
1185 "wheel_1.forward_map"
1186 ]
1187 },
1188 {
1189 "cell_type": "code",
1190 "execution_count": 146,
1191 "metadata": {},
1192 "outputs": [
1193 {
1194 "data": {
1195 "text/plain": [
1196 "'j'"
1197 ]
1198 },
1199 "execution_count": 146,
1200 "metadata": {},
1201 "output_type": "execute_result"
1202 }
1203 ],
1204 "source": [
1205 "wheel_1.advance()\n",
1206 "wheel_1.forward('a')"
1207 ]
1208 },
1209 {
1210 "cell_type": "code",
1211 "execution_count": 147,
1212 "metadata": {},
1213 "outputs": [
1214 {
1215 "data": {
1216 "text/plain": [
1217 "('jlekfcpuymsnvxgwtrozhaqbid', 'vxfzceouyadbjlsgwrkqhmpnit')"
1218 ]
1219 },
1220 "execution_count": 147,
1221 "metadata": {},
1222 "output_type": "execute_result"
1223 }
1224 ],
1225 "source": [
1226 "cat(wheel_1.forward(l) for l in string.ascii_lowercase), cat(wheel_1.backward(l) for l in string.ascii_lowercase)"
1227 ]
1228 },
1229 {
1230 "cell_type": "code",
1231 "execution_count": 148,
1232 "metadata": {},
1233 "outputs": [
1234 {
1235 "data": {
1236 "text/plain": [
1237 "'b'"
1238 ]
1239 },
1240 "execution_count": 148,
1241 "metadata": {},
1242 "output_type": "execute_result"
1243 }
1244 ],
1245 "source": [
1246 "wheel_1.position_l"
1247 ]
1248 },
1249 {
1250 "cell_type": "code",
1251 "execution_count": 149,
1252 "metadata": {
1253 "collapsed": true
1254 },
1255 "outputs": [],
1256 "source": [
1257 "wheel_2 = SimpleWheel(wheel_ii_spec)\n",
1258 "assert(cat(wheel_2.forward(l) for l in string.ascii_lowercase) == 'ajdksiruxblhwtmcqgznpyfvoe')\n",
1259 "assert(cat(wheel_2.backward(l) for l in string.ascii_lowercase) == 'ajpczwrlfbdkotyuqgenhxmivs')"
1260 ]
1261 },
1262 {
1263 "cell_type": "code",
1264 "execution_count": 150,
1265 "metadata": {},
1266 "outputs": [
1267 {
1268 "data": {
1269 "text/plain": [
1270 "('ajdksiruxblhwtmcqgznpyfvoe', 'ajpczwrlfbdkotyuqgenhxmivs')"
1271 ]
1272 },
1273 "execution_count": 150,
1274 "metadata": {},
1275 "output_type": "execute_result"
1276 }
1277 ],
1278 "source": [
1279 "cat(wheel_2.forward(l) for l in string.ascii_lowercase), cat(wheel_2.backward(l) for l in string.ascii_lowercase)"
1280 ]
1281 },
1282 {
1283 "cell_type": "code",
1284 "execution_count": 151,
1285 "metadata": {},
1286 "outputs": [
1287 {
1288 "data": {
1289 "text/plain": [
1290 "('bdfhjlcprtxvznyeiwgakmusqo', 'tagbpcsdqeufvnzhyixjwlrkom')"
1291 ]
1292 },
1293 "execution_count": 151,
1294 "metadata": {},
1295 "output_type": "execute_result"
1296 }
1297 ],
1298 "source": [
1299 "wheel_3 = SimpleWheel(wheel_iii_spec)\n",
1300 "wheel_3.set_position('a')\n",
1301 "wheel_3.advance()\n",
1302 "assert(cat(wheel_3.forward(l) for l in string.ascii_lowercase) == 'cegikboqswuymxdhvfzjltrpna')\n",
1303 "assert(cat(wheel_3.backward(l) for l in string.ascii_lowercase) == 'zfaobrcpdteumygxhwivkqjnls')\n",
1304 "assert(wheel_3.position == 1)\n",
1305 "assert(wheel_3.position_l == 'b')\n",
1306 "\n",
1307 "for _ in range(24): wheel_3.advance()\n",
1308 "assert(wheel_3.position == 25)\n",
1309 "assert(wheel_3.position_l == 'z')\n",
1310 "assert(cat(wheel_3.forward(l) for l in string.ascii_lowercase) == 'pcegikmdqsuywaozfjxhblnvtr')\n",
1311 "assert(cat(wheel_3.backward(l) for l in string.ascii_lowercase) == 'nubhcqdterfvgwoaizjykxmslp')\n",
1312 "\n",
1313 "wheel_3.advance()\n",
1314 "assert(wheel_3.position == 0)\n",
1315 "assert(wheel_3.position_l == 'a')\n",
1316 "assert(cat(wheel_3.forward(l) for l in string.ascii_lowercase) == 'bdfhjlcprtxvznyeiwgakmusqo')\n",
1317 "assert(cat(wheel_3.backward(l) for l in string.ascii_lowercase) == 'tagbpcsdqeufvnzhyixjwlrkom')\n",
1318 "\n",
1319 "cat(wheel_3.forward(l) for l in string.ascii_lowercase), cat(wheel_3.backward(l) for l in string.ascii_lowercase)"
1320 ]
1321 },
1322 {
1323 "cell_type": "markdown",
1324 "metadata": {},
1325 "source": [
1326 "<a name=\"wheel\"></a>\n",
1327 "## Wheel\n",
1328 "[Top](#top)\n",
1329 "\n",
1330 "This is the same as a `SimpleWheel`, but with the addition of a ring.\n",
1331 "\n",
1332 "The ring is moveable around the core of the wheel (with the wiring). This means that moving the ring changes the orientation of the core and wiring for the same setting.\n",
1333 "\n",
1334 "| Wheel with notch | Notch showing peg to hold it in place |\n",
1335 "| ---------------- | ------------------------------------- |\n",
1336 "| <img src=\"enigma-notch.jpg\" alt=\"Enigma wheel with notch\" width=300> | <img src=\"dtu_notch_big.jpg\" alt=\"Enigma wheel with notch\" width=300> |\n",
1337 "| (From [Crypto museum](http://www.cryptomuseum.com/crypto/enigma/img/300879/035/full.jpg)) | From [Matematik Sider](http://www.matematiksider.dk/enigma/dtu_notch_big.jpg) |\n",
1338 "\n",
1339 "Though it's not very visible in the right hand image, the extra metal below the ring shows a spring-loaded peg on the wheel core which drops into the ring, with one hole per letter. The ring setting is where the peg drops into the ring.\n",
1340 "\n",
1341 "The notch setting is used in the full Enigma to control when the wheels step forward.\n",
1342 "\n",
1343 "Note that the constructor calls the superclass's constructor, then sets the positions properly with a call to `set_position`."
1344 ]
1345 },
1346 {
1347 "cell_type": "code",
1348 "execution_count": 152,
1349 "metadata": {
1350 "collapsed": true
1351 },
1352 "outputs": [],
1353 "source": [
1354 "class Wheel(SimpleWheel):\n",
1355 " def __init__(self, transform, ring_notch_letters, ring_setting=1, position='a', raw_transform=False):\n",
1356 " self.ring_notch_letters = ring_notch_letters\n",
1357 " self.ring_setting = ring_setting\n",
1358 " super(Wheel, self).__init__(transform, position=position, raw_transform=raw_transform)\n",
1359 " self.set_position(position)"
1360 ]
1361 },
1362 {
1363 "cell_type": "markdown",
1364 "metadata": {},
1365 "source": [
1366 "The `position` of the wheel is the orientation of the core. It's the same as the ring if the ring setting is 1. The `position_l` attribute is used to report the position of the ring letter, which is what the Enigma operator would see. "
1367 ]
1368 },
1369 {
1370 "cell_type": "code",
1371 "execution_count": 153,
1372 "metadata": {},
1373 "outputs": [],
1374 "source": [
1375 "def __getattribute__(self,name):\n",
1376 " if name=='position_l':\n",
1377 " return unpos(self.position + self.ring_setting - 1)\n",
1378 " else:\n",
1379 " return object.__getattribute__(self, name)\n",
1380 "\n",
1381 "def set_position(self, position):\n",
1382 "# self.position = (pos(position) - self.ring_setting + 1) % 26\n",
1383 " if isinstance(position, str):\n",
1384 " self.position = (pos(position) - self.ring_setting + 1) % 26\n",
1385 " else:\n",
1386 " self.position = (position - self.ring_setting) % 26\n",
1387 "# self.notch_positions = [(pos(position) - pos(p)) % 26 for p in self.ring_notch_letters]\n",
1388 " self.notch_positions = [(self.position + self.ring_setting - 1 - pos(p)) % 26 for p in self.ring_notch_letters]\n",
1389 " \n",
1390 "setattr(Wheel, '__getattribute__', __getattribute__) \n",
1391 "setattr(Wheel, 'set_position', set_position) "
1392 ]
1393 },
1394 {
1395 "cell_type": "markdown",
1396 "metadata": {},
1397 "source": [
1398 "Advance the wheel. Again, note the superclass call, followed by the update of the notch positions."
1399 ]
1400 },
1401 {
1402 "cell_type": "code",
1403 "execution_count": 154,
1404 "metadata": {
1405 "collapsed": true
1406 },
1407 "outputs": [],
1408 "source": [
1409 "def advance(self):\n",
1410 " super(Wheel, self).advance()\n",
1411 " self.notch_positions = [(p + 1) % 26 for p in self.notch_positions]\n",
1412 " # self.position_l = unpos(self.position + self.ring_setting - 1)\n",
1413 " return self.position\n",
1414 " \n",
1415 "setattr(Wheel, 'advance', advance) "
1416 ]
1417 },
1418 {
1419 "cell_type": "code",
1420 "execution_count": 155,
1421 "metadata": {
1422 "collapsed": true
1423 },
1424 "outputs": [],
1425 "source": [
1426 "wheel_3 = Wheel(wheel_iii_spec, wheel_iii_notches, position='b', ring_setting=1)"
1427 ]
1428 },
1429 {
1430 "cell_type": "code",
1431 "execution_count": 156,
1432 "metadata": {},
1433 "outputs": [
1434 {
1435 "data": {
1436 "text/plain": [
1437 "(1, [6])"
1438 ]
1439 },
1440 "execution_count": 156,
1441 "metadata": {},
1442 "output_type": "execute_result"
1443 }
1444 ],
1445 "source": [
1446 "wheel_3.position, wheel_3.notch_positions"
1447 ]
1448 },
1449 {
1450 "cell_type": "code",
1451 "execution_count": 157,
1452 "metadata": {},
1453 "outputs": [
1454 {
1455 "data": {
1456 "text/plain": [
1457 "(25, [2, 15])"
1458 ]
1459 },
1460 "execution_count": 157,
1461 "metadata": {},
1462 "output_type": "execute_result"
1463 }
1464 ],
1465 "source": [
1466 "wheel_6 = Wheel(wheel_vi_spec, wheel_vi_notches, position='b', ring_setting=3)\n",
1467 "wheel_6.position, wheel_6.notch_positions"
1468 ]
1469 },
1470 {
1471 "cell_type": "code",
1472 "execution_count": 158,
1473 "metadata": {
1474 "scrolled": true
1475 },
1476 "outputs": [
1477 {
1478 "name": "stdout",
1479 "output_type": "stream",
1480 "text": [
1481 "0 [3, 16]\n",
1482 "1 [4, 17]\n",
1483 "2 [5, 18]\n",
1484 "3 [6, 19]\n",
1485 "4 [7, 20]\n",
1486 "5 [8, 21]\n",
1487 "6 [9, 22]\n",
1488 "7 [10, 23]\n",
1489 "8 [11, 24]\n",
1490 "9 [12, 25]\n",
1491 "10 [13, 0]\n",
1492 "11 [14, 1]\n",
1493 "12 [15, 2]\n",
1494 "13 [16, 3]\n",
1495 "14 [17, 4]\n",
1496 "15 [18, 5]\n",
1497 "16 [19, 6]\n",
1498 "17 [20, 7]\n",
1499 "18 [21, 8]\n",
1500 "19 [22, 9]\n",
1501 "20 [23, 10]\n",
1502 "21 [24, 11]\n",
1503 "22 [25, 12]\n",
1504 "23 [0, 13]\n",
1505 "24 [1, 14]\n",
1506 "25 [2, 15]\n",
1507 "0 [3, 16]\n"
1508 ]
1509 }
1510 ],
1511 "source": [
1512 "for _ in range(27):\n",
1513 " wheel_6.advance()\n",
1514 " print(wheel_6.position, wheel_6.notch_positions)"
1515 ]
1516 },
1517 {
1518 "cell_type": "code",
1519 "execution_count": 159,
1520 "metadata": {},
1521 "outputs": [],
1522 "source": [
1523 "wheel = Wheel(wheel_iii_spec, wheel_iii_notches, position='b', \n",
1524 " ring_setting=3)\n",
1525 "wheel.set_position(12)\n",
1526 "assert(wheel.position == 9)\n",
1527 "assert(16 in wheel.notch_positions)\n",
1528 "assert(wheel.position_l =='l')"
1529 ]
1530 },
1531 {
1532 "cell_type": "code",
1533 "execution_count": 160,
1534 "metadata": {},
1535 "outputs": [
1536 {
1537 "data": {
1538 "text/plain": [
1539 "[16]"
1540 ]
1541 },
1542 "execution_count": 160,
1543 "metadata": {},
1544 "output_type": "execute_result"
1545 }
1546 ],
1547 "source": [
1548 "wheel.notch_positions"
1549 ]
1550 },
1551 {
1552 "cell_type": "code",
1553 "execution_count": 161,
1554 "metadata": {},
1555 "outputs": [
1556 {
1557 "data": {
1558 "text/plain": [
1559 "'l'"
1560 ]
1561 },
1562 "execution_count": 161,
1563 "metadata": {},
1564 "output_type": "execute_result"
1565 }
1566 ],
1567 "source": [
1568 "wheel.position_l"
1569 ]
1570 },
1571 {
1572 "cell_type": "code",
1573 "execution_count": 162,
1574 "metadata": {
1575 "collapsed": true
1576 },
1577 "outputs": [],
1578 "source": [
1579 "wheel_3 = Wheel(wheel_iii_spec, wheel_iii_notches, position='b', ring_setting=1)\n",
1580 "assert(wheel_3.position == 1)\n",
1581 "assert(wheel_3.notch_positions == [6])\n",
1582 "assert(wheel_3.position_l == 'b')\n",
1583 "wheel_3.advance()\n",
1584 "assert(wheel_3.position == 2)\n",
1585 "assert(wheel_3.notch_positions == [7])\n",
1586 "assert(wheel_3.position_l == 'c')"
1587 ]
1588 },
1589 {
1590 "cell_type": "code",
1591 "execution_count": 163,
1592 "metadata": {},
1593 "outputs": [
1594 {
1595 "data": {
1596 "text/plain": [
1597 "[2, 15]"
1598 ]
1599 },
1600 "execution_count": 163,
1601 "metadata": {},
1602 "output_type": "execute_result"
1603 }
1604 ],
1605 "source": [
1606 "wheel_6 = Wheel(wheel_vi_spec, wheel_vi_notches, position='b', ring_setting=3)\n",
1607 "wheel_6.notch_positions"
1608 ]
1609 },
1610 {
1611 "cell_type": "code",
1612 "execution_count": 164,
1613 "metadata": {
1614 "collapsed": true
1615 },
1616 "outputs": [],
1617 "source": [
1618 "wheel_6 = Wheel(wheel_vi_spec, wheel_vi_notches, position='b', ring_setting=3)\n",
1619 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'xkqhwpvngzrcfoiaselbtymjdu')\n",
1620 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'ptlyrmidoxbswhnfckquzgeavj')\n",
1621 "assert(wheel_6.position == 25)\n",
1622 "assert(2 in wheel_6.notch_positions)\n",
1623 "assert(15 in wheel_6.notch_positions)\n",
1624 "assert(wheel_6.position_l == 'b')\n",
1625 "\n",
1626 "wheel_6.advance()\n",
1627 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'jpgvoumfyqbenhzrdkasxlictw')\n",
1628 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'skxqlhcnwarvgmebjptyfdzuio')\n",
1629 "assert(wheel_6.position == 0)\n",
1630 "assert(3 in wheel_6.notch_positions)\n",
1631 "assert(16 in wheel_6.notch_positions)\n",
1632 "assert(wheel_6.position_l == 'c')\n",
1633 "\n",
1634 "for _ in range(22): wheel_6.advance()\n",
1635 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'mgxantkzsyqjcufirldvhoewbp')\n",
1636 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'dymswobuplgraevzkqifntxcjh')\n",
1637 "assert(wheel_6.position == 22)\n",
1638 "assert(25 in wheel_6.notch_positions)\n",
1639 "assert(12 in wheel_6.notch_positions)\n",
1640 "assert(wheel_6.position_l == 'y')\n",
1641 "\n",
1642 "wheel_6.advance()\n",
1643 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'fwzmsjyrxpibtehqkcugndvaol')\n",
1644 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'xlrvnatokfqzduyjphemswbigc')\n",
1645 "assert(wheel_6.position == 23)\n",
1646 "assert(0 in wheel_6.notch_positions)\n",
1647 "assert(13 in wheel_6.notch_positions)\n",
1648 "assert(wheel_6.position_l == 'z')\n",
1649 "\n",
1650 "wheel_6.advance()\n",
1651 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'vylrixqwohasdgpjbtfmcuznke')\n",
1652 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'kqumzsnjepyctxiogdlrvahfbw')\n",
1653 "assert(wheel_6.position == 24)\n",
1654 "assert(1 in wheel_6.notch_positions)\n",
1655 "assert(14 in wheel_6.notch_positions)\n",
1656 "assert(wheel_6.position_l == 'a')\n",
1657 "\n",
1658 "wheel_6.advance()\n",
1659 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'xkqhwpvngzrcfoiaselbtymjdu')\n",
1660 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'ptlyrmidoxbswhnfckquzgeavj')\n",
1661 "assert(wheel_6.position == 25)\n",
1662 "assert(2 in wheel_6.notch_positions)\n",
1663 "assert(15 in wheel_6.notch_positions)\n",
1664 "assert(wheel_6.position_l == 'b')\n",
1665 "\n",
1666 "wheel_6.advance()\n",
1667 "assert(cat(wheel_6.forward(l) for l in string.ascii_lowercase) == 'jpgvoumfyqbenhzrdkasxlictw')\n",
1668 "assert(cat(wheel_6.backward(l) for l in string.ascii_lowercase) == 'skxqlhcnwarvgmebjptyfdzuio')\n",
1669 "assert(wheel_6.position == 0)\n",
1670 "assert(3 in wheel_6.notch_positions)\n",
1671 "assert(16 in wheel_6.notch_positions)\n",
1672 "assert(wheel_6.position_l == 'c')"
1673 ]
1674 },
1675 {
1676 "cell_type": "code",
1677 "execution_count": 165,
1678 "metadata": {},
1679 "outputs": [
1680 {
1681 "data": {
1682 "text/plain": [
1683 "(0, 'c', [3, 16])"
1684 ]
1685 },
1686 "execution_count": 165,
1687 "metadata": {},
1688 "output_type": "execute_result"
1689 }
1690 ],
1691 "source": [
1692 "wheel_6.position, wheel_6.position_l, wheel_6.notch_positions"
1693 ]
1694 },
1695 {
1696 "cell_type": "markdown",
1697 "metadata": {},
1698 "source": [
1699 "<a name=\"enigma\"></a>\n",
1700 "## Enigma\n",
1701 "[Top](#top)\n",
1702 "\n",
1703 "This is the full Enigma machine.\n",
1704 "\n",
1705 "It's a collection of the various components defined above. There are three wheels (left, middle, and right), a plugboard, and a reflector.\n",
1706 "\n",
1707 "The `__getattribute__` method returns the state of the machine in friendly form, generally by asking the components to return the relevant attributes."
1708 ]
1709 },
1710 {
1711 "cell_type": "code",
1712 "execution_count": 167,
1713 "metadata": {},
1714 "outputs": [],
1715 "source": [
1716 "class Enigma(object):\n",
1717 " def __init__(self, reflector_spec,\n",
1718 " left_wheel_spec, left_wheel_notches,\n",
1719 " middle_wheel_spec, middle_wheel_notches,\n",
1720 " right_wheel_spec, right_wheel_notches,\n",
1721 " left_ring_setting, middle_ring_setting, right_ring_setting,\n",
1722 " plugboard_setting):\n",
1723 " self.reflector = Reflector(reflector_spec)\n",
1724 " self.left_wheel = Wheel(left_wheel_spec, left_wheel_notches, ring_setting=left_ring_setting)\n",
1725 " self.middle_wheel = Wheel(middle_wheel_spec, middle_wheel_notches, ring_setting=middle_ring_setting)\n",
1726 " self.right_wheel = Wheel(right_wheel_spec, right_wheel_notches, ring_setting=right_ring_setting)\n",
1727 " self.plugboard = Plugboard(plugboard_setting)\n",
1728 " \n",
1729 " def __getattribute__(self,name):\n",
1730 " if name=='wheel_positions':\n",
1731 " return self.left_wheel.position, self.middle_wheel.position, self.right_wheel.position \n",
1732 " elif name=='wheel_positions_l':\n",
1733 " return self.left_wheel.position_l, self.middle_wheel.position_l, self.right_wheel.position_l \n",
1734 " elif name=='notch_positions':\n",
1735 " return (self.left_wheel.notch_positions, \n",
1736 " self.middle_wheel.notch_positions, \n",
1737 " self.right_wheel.notch_positions)\n",
1738 " else:\n",
1739 " return object.__getattribute__(self, name)"
1740 ]
1741 },
1742 {
1743 "cell_type": "markdown",
1744 "metadata": {},
1745 "source": [
1746 "Set the wheels to the initial positions. "
1747 ]
1748 },
1749 {
1750 "cell_type": "code",
1751 "execution_count": 168,
1752 "metadata": {
1753 "collapsed": true
1754 },
1755 "outputs": [],
1756 "source": [
1757 "def set_wheels(self, left_wheel_position, middle_wheel_position, right_wheel_position):\n",
1758 " self.left_wheel.set_position(left_wheel_position)\n",
1759 " self.middle_wheel.set_position(middle_wheel_position)\n",
1760 " self.right_wheel.set_position(right_wheel_position)\n",
1761 "\n",
1762 "setattr(Enigma, 'set_wheels', set_wheels)"
1763 ]
1764 },
1765 {
1766 "cell_type": "markdown",
1767 "metadata": {},
1768 "source": [
1769 "`lookup` just follows a path through the machine without changing the positions of any parts. It just follows a signal from the input, thorough all the components, to the reflector, back through all the components, to the output."
1770 ]
1771 },
1772 {
1773 "cell_type": "code",
1774 "execution_count": 169,
1775 "metadata": {
1776 "collapsed": true
1777 },
1778 "outputs": [],
1779 "source": [
1780 "def lookup(self, letter):\n",
1781 " a = self.plugboard.forward(letter)\n",
1782 " b = self.right_wheel.forward(a)\n",
1783 " c = self.middle_wheel.forward(b)\n",
1784 " d = self.left_wheel.forward(c)\n",
1785 " e = self.reflector.forward(d)\n",
1786 " f = self.left_wheel.backward(e)\n",
1787 " g = self.middle_wheel.backward(f)\n",
1788 " h = self.right_wheel.backward(g)\n",
1789 " i = self.plugboard.backward(h)\n",
1790 " return i\n",
1791 "\n",
1792 "setattr(Enigma, 'lookup', lookup)"
1793 ]
1794 },
1795 {
1796 "cell_type": "markdown",
1797 "metadata": {},
1798 "source": [
1799 "`advance` moves the wheels on one step. The right wheel always advances. If the notch is in the zero position, the wheel also advances the wheel to the left. \n",
1800 "\n",
1801 "It follows the 'double stepping' behaviour of the engima machines."
1802 ]
1803 },
1804 {
1805 "cell_type": "code",
1806 "execution_count": 170,
1807 "metadata": {
1808 "collapsed": true
1809 },
1810 "outputs": [],
1811 "source": [
1812 "def advance(self):\n",
1813 " advance_middle = False\n",
1814 " advance_left = False\n",
1815 " if 0 in self.right_wheel.notch_positions:\n",
1816 " advance_middle = True\n",
1817 " if 0 in self.middle_wheel.notch_positions:\n",
1818 " advance_left = True\n",
1819 " advance_middle = True\n",
1820 " self.right_wheel.advance()\n",
1821 " if advance_middle: self.middle_wheel.advance()\n",
1822 " if advance_left: self.left_wheel.advance()\n",
1823 "\n",
1824 "setattr(Enigma, 'advance', advance) "
1825 ]
1826 },
1827 {
1828 "cell_type": "markdown",
1829 "metadata": {},
1830 "source": [
1831 "Finally, encipher letters and messages. \n",
1832 "\n",
1833 "Note that the wheels advance _before_ the letter signal is sent through the machine: in the physical machine, the advancing is done by pressing the key on the keyboard. \n",
1834 "\n",
1835 "Also note that the messages are cleaned before use, so letters are converted to lower case and non-letters are removed."
1836 ]
1837 },
1838 {
1839 "cell_type": "code",
1840 "execution_count": 220,
1841 "metadata": {
1842 "collapsed": true
1843 },
1844 "outputs": [],
1845 "source": [
1846 "def encipher_letter(self, letter):\n",
1847 " self.advance()\n",
1848 " return self.lookup(letter)\n",
1849 "\n",
1850 "def encipher(self, message, debug=False):\n",
1851 " enciphered = ''\n",
1852 " for letter in clean(message):\n",
1853 " enciphered += self.encipher_letter(letter)\n",
1854 " if debug:\n",
1855 " print('Wheels now', list(self.wheel_positions_l), 'enciphering {} -> {}'.format(letter, self.lookup(letter)))\n",
1856 " return enciphered\n",
1857 "\n",
1858 "setattr(Enigma, 'encipher_letter', encipher_letter)\n",
1859 "setattr(Enigma, 'encipher', encipher)\n",
1860 "setattr(Enigma, 'decipher', encipher)"
1861 ]
1862 },
1863 {
1864 "cell_type": "markdown",
1865 "metadata": {},
1866 "source": [
1867 "<a name=\"testingenigma\"></a>\n",
1868 "## Testing Enigma\n",
1869 "[Top](#top)\n",
1870 "\n",
1871 "Some tests of the Enigma machine."
1872 ]
1873 },
1874 {
1875 "cell_type": "code",
1876 "execution_count": 172,
1877 "metadata": {
1878 "collapsed": true
1879 },
1880 "outputs": [],
1881 "source": [
1882 "enigma = Enigma(reflector_b_spec, \n",
1883 " wheel_i_spec, wheel_i_notches,\n",
1884 " wheel_ii_spec, wheel_ii_notches,\n",
1885 " wheel_iii_spec, wheel_iii_notches,\n",
1886 " 1, 1, 1,\n",
1887 " '')"
1888 ]
1889 },
1890 {
1891 "cell_type": "code",
1892 "execution_count": 173,
1893 "metadata": {},
1894 "outputs": [
1895 {
1896 "data": {
1897 "text/plain": [
1898 "'u'"
1899 ]
1900 },
1901 "execution_count": 173,
1902 "metadata": {},
1903 "output_type": "execute_result"
1904 }
1905 ],
1906 "source": [
1907 "enigma.lookup('a')"
1908 ]
1909 },
1910 {
1911 "cell_type": "code",
1912 "execution_count": 174,
1913 "metadata": {},
1914 "outputs": [
1915 {
1916 "data": {
1917 "text/plain": [
1918 "'a'"
1919 ]
1920 },
1921 "execution_count": 174,
1922 "metadata": {},
1923 "output_type": "execute_result"
1924 }
1925 ],
1926 "source": [
1927 "enigma.lookup('u')"
1928 ]
1929 },
1930 {
1931 "cell_type": "code",
1932 "execution_count": 175,
1933 "metadata": {},
1934 "outputs": [
1935 {
1936 "data": {
1937 "text/plain": [
1938 "'uejobtpzwcnsrkdgvmlfaqiyxh'"
1939 ]
1940 },
1941 "execution_count": 175,
1942 "metadata": {},
1943 "output_type": "execute_result"
1944 }
1945 ],
1946 "source": [
1947 "cat(enigma.lookup(l) for l in string.ascii_lowercase)"
1948 ]
1949 },
1950 {
1951 "cell_type": "code",
1952 "execution_count": 176,
1953 "metadata": {},
1954 "outputs": [
1955 {
1956 "data": {
1957 "text/plain": [
1958 "'abcdefghijklmnopqrstuvwxyz'"
1959 ]
1960 },
1961 "execution_count": 176,
1962 "metadata": {},
1963 "output_type": "execute_result"
1964 }
1965 ],
1966 "source": [
1967 "cat(enigma.lookup(enigma.lookup(l)) for l in string.ascii_lowercase)"
1968 ]
1969 },
1970 {
1971 "cell_type": "code",
1972 "execution_count": 177,
1973 "metadata": {
1974 "collapsed": true
1975 },
1976 "outputs": [],
1977 "source": [
1978 "assert(cat(enigma.lookup(enigma.lookup(l)) for l in string.ascii_lowercase) == string.ascii_lowercase)"
1979 ]
1980 },
1981 {
1982 "cell_type": "code",
1983 "execution_count": 178,
1984 "metadata": {
1985 "scrolled": true
1986 },
1987 "outputs": [
1988 {
1989 "name": "stdout",
1990 "output_type": "stream",
1991 "text": [
1992 "0 :: a a a ; [10] [22] [5] uejobtpzwcnsrkdgvmlfaqiyxh\n",
1993 "1 :: a a b ; [10] [22] [6] baqmfexihswpdytlcvjozrkgnu\n",
1994 "2 :: a a c ; [10] [22] [7] djralkwpobfeyqihncxzvugsmt\n",
1995 "3 :: a a d ; [10] [22] [8] zlejcuitgdmbkonsvxphfqyrwa\n",
1996 "4 :: a a e ; [10] [22] [9] gcblwtakqzhdosmxiunfryepvj\n",
1997 "5 :: a a f ; [10] [22] [10] osnirgfmdpvuhcajwebxlkqtzy\n",
1998 "6 :: a a g ; [10] [22] [11] wymvnqzjlhoicekuftxrpdasbg\n",
1999 "7 :: a a h ; [10] [22] [12] cjafkdztpbeuormiwnvhlsqyxg\n",
2000 "8 :: a a i ; [10] [22] [13] xijuyslvbczgnmqwotfrdhpaek\n",
2001 "9 :: a a j ; [10] [22] [14] lfzrwbytjisaovmuxdkhpneqgc\n",
2002 "10 :: a a k ; [10] [22] [15] tkezcqynuwbpvhslfxoaimjrgd\n",
2003 "11 :: a a l ; [10] [22] [16] kiwfnduxbsaotelqpvjmgrchzy\n",
2004 "12 :: a a m ; [10] [22] [17] sfkutbpoxycrnmhgwlaedzqijv\n",
2005 "13 :: a a n ; [10] [22] [18] baqwlkhgrsfextpocijnvudmzy\n",
2006 "14 :: a a o ; [10] [22] [19] teofbdzxqkjyrscvimnawpuhlg\n",
2007 "15 :: a a p ; [10] [22] [20] mhypswrbzxqvaondkgeutlfjci\n",
2008 "16 :: a a q ; [10] [22] [21] cpasnrhgkuixzevbyfdwjotlqm\n",
2009 "17 :: a a r ; [10] [22] [22] dlfatcjwygvbnmzrxpueskhqio\n",
2010 "18 :: a a s ; [10] [22] [23] lxymzjuqtfpadsrkhonigwvbce\n",
2011 "19 :: a a t ; [10] [22] [24] puvioztjdhxmlyeawsrgbcqknf\n",
2012 "20 :: a a u ; [10] [22] [25] baigpldqcowfyzjehvtsxrkumn\n",
2013 "21 :: a a v ; [10] [22] [0] mnvfydiwgzsoablrxpkutchqej\n",
2014 "22 :: a b w ; [10] [23] [1] ulfopcykswhbzvderqixanjtgm\n",
2015 "23 :: a b x ; [10] [23] [2] qmwftdyovursbzhxaklejicpgn\n",
2016 "24 :: a b y ; [10] [23] [3] oljmzxrvucybdqasngpwihtfke\n",
2017 "25 :: a b z ; [10] [23] [4] fwevcalzxutgysrqponkjdbimh\n"
2018 ]
2019 }
2020 ],
2021 "source": [
2022 "# check the middle wheel turns over\n",
2023 "enigma.set_wheels('a', 'a', 'a')\n",
2024 "for i in range(26):\n",
2025 " print(i, '::', \n",
2026 " enigma.left_wheel.position_l, enigma.middle_wheel.position_l, enigma.right_wheel.position_l, ';',\n",
2027 " enigma.left_wheel.notch_positions, enigma.middle_wheel.notch_positions, enigma.right_wheel.notch_positions, \n",
2028 " cat(enigma.lookup(l) for l in string.ascii_lowercase))\n",
2029 " enigma.advance()"
2030 ]
2031 },
2032 {
2033 "cell_type": "markdown",
2034 "metadata": {},
2035 "source": [
2036 "Formal test of middle wheel turnover"
2037 ]
2038 },
2039 {
2040 "cell_type": "code",
2041 "execution_count": 179,
2042 "metadata": {
2043 "collapsed": true,
2044 "scrolled": true
2045 },
2046 "outputs": [],
2047 "source": [
2048 "enigma.set_wheels('a', 'a', 't')\n",
2049 "assert(enigma.wheel_positions == (0, 0, 19))\n",
2050 "assert(cat(enigma.wheel_positions_l) == 'aat')\n",
2051 "assert(enigma.notch_positions == ([10], [22], [24]))\n",
2052 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'puvioztjdhxmlyeawsrgbcqknf')\n",
2053 "\n",
2054 "enigma.advance()\n",
2055 "assert(enigma.wheel_positions == (0, 0, 20))\n",
2056 "assert(cat(enigma.wheel_positions_l) == 'aau')\n",
2057 "assert(enigma.notch_positions == ([10], [22], [25]))\n",
2058 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baigpldqcowfyzjehvtsxrkumn')\n",
2059 "\n",
2060 "enigma.advance()\n",
2061 "assert(enigma.wheel_positions == (0, 0, 21))\n",
2062 "assert(cat(enigma.wheel_positions_l) == 'aav')\n",
2063 "assert(enigma.notch_positions == ([10], [22], [0]))\n",
2064 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'mnvfydiwgzsoablrxpkutchqej')\n",
2065 "\n",
2066 "enigma.advance()\n",
2067 "assert(enigma.wheel_positions == (0, 1, 22))\n",
2068 "assert(cat(enigma.wheel_positions_l) == 'abw')\n",
2069 "assert(enigma.notch_positions == ([10], [23], [1]))\n",
2070 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ulfopcykswhbzvderqixanjtgm')\n",
2071 "\n",
2072 "enigma.advance()\n",
2073 "assert(enigma.wheel_positions == (0, 1, 23))\n",
2074 "assert(cat(enigma.wheel_positions_l) == 'abx')\n",
2075 "assert(enigma.notch_positions == ([10], [23], [2]))\n",
2076 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qmwftdyovursbzhxaklejicpgn')\n",
2077 "\n",
2078 "enigma.advance()\n",
2079 "assert(enigma.wheel_positions == (0, 1, 24))\n",
2080 "assert(cat(enigma.wheel_positions_l) == 'aby')\n",
2081 "assert(enigma.notch_positions == ([10], [23 ], [3]))\n",
2082 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'oljmzxrvucybdqasngpwihtfke')\n"
2083 ]
2084 },
2085 {
2086 "cell_type": "markdown",
2087 "metadata": {},
2088 "source": [
2089 "Test of middle wheel advancing the left wheel, exhibiting the \"double step\" behaviour."
2090 ]
2091 },
2092 {
2093 "cell_type": "code",
2094 "execution_count": 180,
2095 "metadata": {
2096 "scrolled": true
2097 },
2098 "outputs": [
2099 {
2100 "name": "stdout",
2101 "output_type": "stream",
2102 "text": [
2103 "(1, 5, 24) ('b', 'f', 'y') ([11], [1], [3]) baknstqzrmcxjdvygiefwoulph\n"
2104 ]
2105 }
2106 ],
2107 "source": [
2108 "enigma.set_wheels('a', 'd', 't')\n",
2109 "assert(enigma.wheel_positions == (0, 3, 19))\n",
2110 "assert(cat(enigma.wheel_positions_l) == 'adt')\n",
2111 "assert(enigma.notch_positions == ([10], [25], [24]))\n",
2112 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'zcbpqxwsjiuonmldethrkygfva')\n",
2113 "\n",
2114 "enigma.advance()\n",
2115 "assert(enigma.wheel_positions == (0, 3, 20))\n",
2116 "assert(cat(enigma.wheel_positions_l) == 'adu')\n",
2117 "assert(enigma.notch_positions == ([10], [25], [25]))\n",
2118 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ehprawjbngotxikcsdqlzyfmvu')\n",
2119 "\n",
2120 "enigma.advance()\n",
2121 "assert(enigma.wheel_positions == (0, 3, 21))\n",
2122 "assert(cat(enigma.wheel_positions_l) == 'adv')\n",
2123 "assert(enigma.notch_positions == ([10], [25], [0]))\n",
2124 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'eqzxarpihmnvjkwgbfuyslodtc')\n",
2125 "\n",
2126 "enigma.advance()\n",
2127 "assert(enigma.wheel_positions == (0, 4, 22))\n",
2128 "assert(cat(enigma.wheel_positions_l) == 'aew')\n",
2129 "assert(enigma.notch_positions == ([10], [0], [1]))\n",
2130 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qedcbtpluzmhkongavwfirsyxj')\n",
2131 "\n",
2132 "enigma.advance()\n",
2133 "assert(enigma.wheel_positions == (1, 5, 23))\n",
2134 "assert(cat(enigma.wheel_positions_l) == 'bfx')\n",
2135 "assert(enigma.notch_positions == ([11], [1], [2]))\n",
2136 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'iwuedhsfazqxytvrkpgncoblmj')\n",
2137 "\n",
2138 "enigma.advance()\n",
2139 "assert(enigma.wheel_positions == (1, 5, 24))\n",
2140 "assert(cat(enigma.wheel_positions_l) == 'bfy')\n",
2141 "assert(enigma.notch_positions == ([11], [1], [3]))\n",
2142 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baknstqzrmcxjdvygiefwoulph')\n",
2143 "\n",
2144 "print(enigma.wheel_positions, enigma.wheel_positions_l, enigma.notch_positions, \n",
2145 " cat(enigma.lookup(l) for l in string.ascii_lowercase))\n"
2146 ]
2147 },
2148 {
2149 "cell_type": "code",
2150 "execution_count": 181,
2151 "metadata": {},
2152 "outputs": [
2153 {
2154 "data": {
2155 "text/plain": [
2156 "'olpfhnvflyn'"
2157 ]
2158 },
2159 "execution_count": 181,
2160 "metadata": {},
2161 "output_type": "execute_result"
2162 }
2163 ],
2164 "source": [
2165 "enigma.set_wheels('a', 'a', 'a')\n",
2166 "ct = enigma.encipher('testmessage')\n",
2167 "assert(ct == 'olpfhnvflyn')\n",
2168 "ct"
2169 ]
2170 },
2171 {
2172 "cell_type": "code",
2173 "execution_count": 182,
2174 "metadata": {},
2175 "outputs": [
2176 {
2177 "data": {
2178 "text/plain": [
2179 "'lawnjgpwjik'"
2180 ]
2181 },
2182 "execution_count": 182,
2183 "metadata": {},
2184 "output_type": "execute_result"
2185 }
2186 ],
2187 "source": [
2188 "enigma.set_wheels('a', 'd', 't')\n",
2189 "ct = enigma.encipher('testmessage')\n",
2190 "assert(ct == 'lawnjgpwjik')\n",
2191 "ct"
2192 ]
2193 },
2194 {
2195 "cell_type": "code",
2196 "execution_count": 183,
2197 "metadata": {},
2198 "outputs": [
2199 {
2200 "name": "stdout",
2201 "output_type": "stream",
2202 "text": [
2203 "enigma.advance()\n",
2204 "assert(enigma.wheel_positions == (0, 3, 20))\n",
2205 "assert(cat(enigma.wheel_positions_l) == 'adu')\n",
2206 "assert(enigma.notch_positions == ([10], [25], [25]))\n",
2207 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ehprawjbngotxikcsdqlzyfmvu')\n",
2208 "\n",
2209 "enigma.advance()\n",
2210 "assert(enigma.wheel_positions == (0, 3, 21))\n",
2211 "assert(cat(enigma.wheel_positions_l) == 'adv')\n",
2212 "assert(enigma.notch_positions == ([10], [25], [0]))\n",
2213 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'eqzxarpihmnvjkwgbfuyslodtc')\n",
2214 "\n",
2215 "enigma.advance()\n",
2216 "assert(enigma.wheel_positions == (0, 4, 22))\n",
2217 "assert(cat(enigma.wheel_positions_l) == 'aew')\n",
2218 "assert(enigma.notch_positions == ([10], [0], [1]))\n",
2219 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qedcbtpluzmhkongavwfirsyxj')\n",
2220 "\n",
2221 "enigma.advance()\n",
2222 "assert(enigma.wheel_positions == (1, 5, 23))\n",
2223 "assert(cat(enigma.wheel_positions_l) == 'bfx')\n",
2224 "assert(enigma.notch_positions == ([11], [1], [2]))\n",
2225 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'iwuedhsfazqxytvrkpgncoblmj')\n",
2226 "\n",
2227 "enigma.advance()\n",
2228 "assert(enigma.wheel_positions == (1, 5, 24))\n",
2229 "assert(cat(enigma.wheel_positions_l) == 'bfy')\n",
2230 "assert(enigma.notch_positions == ([11], [1], [3]))\n",
2231 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baknstqzrmcxjdvygiefwoulph')\n",
2232 "\n",
2233 "enigma.advance()\n",
2234 "assert(enigma.wheel_positions == (1, 5, 25))\n",
2235 "assert(cat(enigma.wheel_positions_l) == 'bfz')\n",
2236 "assert(enigma.notch_positions == ([11], [1], [4]))\n",
2237 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'mudcgteyjiwravxzslqfbnkohp')\n",
2238 "\n",
2239 "enigma.advance()\n",
2240 "assert(enigma.wheel_positions == (1, 5, 0))\n",
2241 "assert(cat(enigma.wheel_positions_l) == 'bfa')\n",
2242 "assert(enigma.notch_positions == ([11], [1], [5]))\n",
2243 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'gjkuotarmbcziwesvhpfdqnyxl')\n",
2244 "\n",
2245 "enigma.advance()\n",
2246 "assert(enigma.wheel_positions == (1, 5, 1))\n",
2247 "assert(cat(enigma.wheel_positions_l) == 'bfb')\n",
2248 "assert(enigma.notch_positions == ([11], [1], [6]))\n",
2249 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'fcbmpaqihxytdrvegnwlzosjku')\n",
2250 "\n",
2251 "enigma.advance()\n",
2252 "assert(enigma.wheel_positions == (1, 5, 2))\n",
2253 "assert(cat(enigma.wheel_positions_l) == 'bfc')\n",
2254 "assert(enigma.notch_positions == ([11], [1], [7]))\n",
2255 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'jktrlpmywabegzqfodxcvuishn')\n",
2256 "\n",
2257 "enigma.advance()\n",
2258 "assert(enigma.wheel_positions == (1, 5, 3))\n",
2259 "assert(cat(enigma.wheel_positions_l) == 'bfd')\n",
2260 "assert(enigma.notch_positions == ([11], [1], [8]))\n",
2261 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'baetclivgurfzqponkxdjhyswm')\n",
2262 "\n",
2263 "enigma.advance()\n",
2264 "assert(enigma.wheel_positions == (1, 5, 4))\n",
2265 "assert(cat(enigma.wheel_positions_l) == 'bfe')\n",
2266 "assert(enigma.notch_positions == ([11], [1], [9]))\n",
2267 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'wvjlkpzxtcedqsyfmunirbahog')\n",
2268 "\n",
2269 "enigma.advance()\n",
2270 "assert(enigma.wheel_positions == (1, 5, 5))\n",
2271 "assert(cat(enigma.wheel_positions_l) == 'bff')\n",
2272 "assert(enigma.notch_positions == ([11], [1], [10]))\n",
2273 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'zpqiogfsdlmjkyebcvhxwrutna')\n",
2274 "\n",
2275 "enigma.advance()\n",
2276 "assert(enigma.wheel_positions == (1, 5, 6))\n",
2277 "assert(cat(enigma.wheel_positions_l) == 'bfg')\n",
2278 "assert(enigma.notch_positions == ([11], [1], [11]))\n",
2279 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'fjmnwayslbxicdpouthrqzekgv')\n",
2280 "\n",
2281 "enigma.advance()\n",
2282 "assert(enigma.wheel_positions == (1, 5, 7))\n",
2283 "assert(cat(enigma.wheel_positions_l) == 'bfh')\n",
2284 "assert(enigma.notch_positions == ([11], [1], [12]))\n",
2285 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'csafzdyloxuhnmitwvbpkrqjge')\n",
2286 "\n",
2287 "enigma.advance()\n",
2288 "assert(enigma.wheel_positions == (1, 5, 8))\n",
2289 "assert(cat(enigma.wheel_positions_l) == 'bfi')\n",
2290 "assert(enigma.notch_positions == ([11], [1], [13]))\n",
2291 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'kihyvulcbtagwrqzonxjfemsdp')\n",
2292 "\n",
2293 "enigma.advance()\n",
2294 "assert(enigma.wheel_positions == (1, 5, 9))\n",
2295 "assert(cat(enigma.wheel_positions_l) == 'bfj')\n",
2296 "assert(enigma.notch_positions == ([11], [1], [14]))\n",
2297 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'pgzrytbksqhwxvuajdifonlmec')\n",
2298 "\n",
2299 "enigma.advance()\n",
2300 "assert(enigma.wheel_positions == (1, 5, 10))\n",
2301 "assert(cat(enigma.wheel_positions_l) == 'bfk')\n",
2302 "assert(enigma.notch_positions == ([11], [1], [15]))\n",
2303 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'fkirsazncwbvyhpoudexqljtmg')\n",
2304 "\n",
2305 "enigma.advance()\n",
2306 "assert(enigma.wheel_positions == (1, 5, 11))\n",
2307 "assert(cat(enigma.wheel_positions_l) == 'bfl')\n",
2308 "assert(enigma.notch_positions == ([11], [1], [16]))\n",
2309 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'mhkronubsvctafeqpdilgjxwzy')\n",
2310 "\n",
2311 "enigma.advance()\n",
2312 "assert(enigma.wheel_positions == (1, 5, 12))\n",
2313 "assert(cat(enigma.wheel_positions_l) == 'bfm')\n",
2314 "assert(enigma.notch_positions == ([11], [1], [17]))\n",
2315 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'gnkuoxarzycmlbetvhwpdqsfji')\n",
2316 "\n",
2317 "enigma.advance()\n",
2318 "assert(enigma.wheel_positions == (1, 5, 13))\n",
2319 "assert(cat(enigma.wheel_positions_l) == 'bfn')\n",
2320 "assert(enigma.notch_positions == ([11], [1], [18]))\n",
2321 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'bainslqkcxhfudpogtermwvjzy')\n",
2322 "\n",
2323 "enigma.advance()\n",
2324 "assert(enigma.wheel_positions == (1, 5, 14))\n",
2325 "assert(cat(enigma.wheel_positions_l) == 'bfo')\n",
2326 "assert(enigma.notch_positions == ([11], [1], [19]))\n",
2327 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'xemfbdnwjitycgzusvqkprhalo')\n",
2328 "\n",
2329 "enigma.advance()\n",
2330 "assert(enigma.wheel_positions == (1, 5, 15))\n",
2331 "assert(cat(enigma.wheel_positions_l) == 'bfp')\n",
2332 "assert(enigma.notch_positions == ([11], [1], [20]))\n",
2333 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'qixksmhgbtdvfonrapejwluczy')\n",
2334 "\n",
2335 "enigma.advance()\n",
2336 "assert(enigma.wheel_positions == (1, 5, 16))\n",
2337 "assert(cat(enigma.wheel_positions_l) == 'bfq')\n",
2338 "assert(enigma.notch_positions == ([11], [1], [21]))\n",
2339 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'cgaulmbskwiefrtzynhodxjvqp')\n",
2340 "\n",
2341 "enigma.advance()\n",
2342 "assert(enigma.wheel_positions == (1, 5, 17))\n",
2343 "assert(cat(enigma.wheel_positions_l) == 'bfr')\n",
2344 "assert(enigma.notch_positions == ([11], [1], [22]))\n",
2345 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'iwqfldszaxvenmyrcpgutkbjoh')\n",
2346 "\n",
2347 "enigma.advance()\n",
2348 "assert(enigma.wheel_positions == (1, 5, 18))\n",
2349 "assert(cat(enigma.wheel_positions_l) == 'bfs')\n",
2350 "assert(enigma.notch_positions == ([11], [1], [23]))\n",
2351 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'vxykrjilgfdhqtusmepnoazbcw')\n",
2352 "\n",
2353 "enigma.advance()\n",
2354 "assert(enigma.wheel_positions == (1, 5, 19))\n",
2355 "assert(cat(enigma.wheel_positions_l) == 'bft')\n",
2356 "assert(enigma.notch_positions == ([11], [1], [24]))\n",
2357 "assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == 'ieysbvkjahgmlpxnwtdrzfqocu')\n",
2358 "\n"
2359 ]
2360 }
2361 ],
2362 "source": [
2363 "enigma.set_wheels('a', 'd', 't')\n",
2364 "for i in range(26):\n",
2365 " enigma.advance()\n",
2366 " print('enigma.advance()')\n",
2367 " print(\"assert(enigma.wheel_positions == {})\".format(enigma.wheel_positions))\n",
2368 " print(\"assert(cat(enigma.wheel_positions_l) == '{}')\".format(cat(enigma.wheel_positions_l)))\n",
2369 " print(\"assert(enigma.notch_positions == {})\".format(enigma.notch_positions))\n",
2370 " print(\"assert(cat(enigma.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(enigma.lookup(l) for l in string.ascii_lowercase)))\n",
2371 " print()"
2372 ]
2373 },
2374 {
2375 "cell_type": "code",
2376 "execution_count": 184,
2377 "metadata": {},
2378 "outputs": [
2379 {
2380 "data": {
2381 "text/plain": [
2382 "'bahxvfrpdc'"
2383 ]
2384 },
2385 "execution_count": 184,
2386 "metadata": {},
2387 "output_type": "execute_result"
2388 }
2389 ],
2390 "source": [
2391 "enigma.set_wheels('a', 'd', 't')\n",
2392 "ct = enigma.encipher('hellothere')\n",
2393 "assert(ct == 'bahxvfrpdc')\n",
2394 "ct"
2395 ]
2396 },
2397 {
2398 "cell_type": "code",
2399 "execution_count": 185,
2400 "metadata": {},
2401 "outputs": [
2402 {
2403 "data": {
2404 "text/plain": [
2405 "'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr'"
2406 ]
2407 },
2408 "execution_count": 185,
2409 "metadata": {},
2410 "output_type": "execute_result"
2411 }
2412 ],
2413 "source": [
2414 "enigma.set_wheels('b', 'd', 'q')\n",
2415 "ct = enigma.encipher('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')\n",
2416 "assert(ct == 'kvmmwrlqlqsqpeugjrcxzwpfyiyybwloewrouvkpoztceuwtfjzqwpbqldttsr')\n",
2417 "assert(enigma.left_wheel.position_l == 'c')\n",
2418 "assert(enigma.middle_wheel.position_l == 'h')\n",
2419 "assert(enigma.right_wheel.position_l == 'a')\n",
2420 "ct"
2421 ]
2422 },
2423 {
2424 "cell_type": "code",
2425 "execution_count": 186,
2426 "metadata": {},
2427 "outputs": [
2428 {
2429 "data": {
2430 "text/plain": [
2431 "'c'"
2432 ]
2433 },
2434 "execution_count": 186,
2435 "metadata": {},
2436 "output_type": "execute_result"
2437 }
2438 ],
2439 "source": [
2440 "enigma.left_wheel.position_l"
2441 ]
2442 },
2443 {
2444 "cell_type": "code",
2445 "execution_count": 187,
2446 "metadata": {
2447 "collapsed": true
2448 },
2449 "outputs": [],
2450 "source": [
2451 "# Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm\n",
2452 "# Enigma simulation settings are \n",
2453 "# http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJFE;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX\n",
2454 "w_enigma = Enigma(reflector_b_spec, \n",
2455 " wheel_i_spec, wheel_i_notches,\n",
2456 " wheel_v_spec, wheel_v_notches,\n",
2457 " wheel_iii_spec, wheel_iii_notches,\n",
2458 " 6, 20, 24,\n",
2459 " 'ua pf rq so ni ey bg hl tx zj')"
2460 ]
2461 },
2462 {
2463 "cell_type": "code",
2464 "execution_count": 188,
2465 "metadata": {
2466 "collapsed": true
2467 },
2468 "outputs": [],
2469 "source": [
2470 "# # Setting sheet line 31 from http://www.codesandciphers.org.uk/enigma/enigma3.htm\n",
2471 "# # Enigma simulation settings are \n",
2472 "# # http://enigma.louisedade.co.uk/enigma.html?m3;b;b153;AFTX;AJFE;AU-BG-EY-FP-HL-IN-JZ-OS-QR-TX\n",
2473 "# enigma = Enigma(reflector_b_spec, \n",
2474 "# wheel_i_spec, wheel_i_notches,\n",
2475 "# wheel_v_spec, wheel_v_notches,\n",
2476 "# wheel_iii_spec, wheel_iii_notches,\n",
2477 "# 6, 20, 24,\n",
2478 "# 'ua pf rq so ni ey bg hl tx zj')"
2479 ]
2480 },
2481 {
2482 "cell_type": "code",
2483 "execution_count": 189,
2484 "metadata": {
2485 "collapsed": true
2486 },
2487 "outputs": [],
2488 "source": [
2489 "w_enigma.set_wheels('j', 'e', 'u')\n",
2490 "\n",
2491 "w_enigma.advance()\n",
2492 "assert(w_enigma.wheel_positions == (4, 11, 24))\n",
2493 "assert(cat(w_enigma.wheel_positions_l) == 'jev')\n",
2494 "assert(w_enigma.notch_positions == ([19], [5], [0]))\n",
2495 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mvqjlyowkdieasgzcunxrbhtfp')\n",
2496 "\n",
2497 "w_enigma.advance()\n",
2498 "assert(w_enigma.wheel_positions == (4, 12, 25))\n",
2499 "assert(cat(w_enigma.wheel_positions_l) == 'jfw')\n",
2500 "assert(w_enigma.notch_positions == ([19], [6], [1]))\n",
2501 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'sjolzuyvrbwdpxcmtiaqfhknge')\n",
2502 "\n",
2503 "w_enigma.advance()\n",
2504 "assert(w_enigma.wheel_positions == (4, 12, 0))\n",
2505 "assert(cat(w_enigma.wheel_positions_l) == 'jfx')\n",
2506 "assert(w_enigma.notch_positions == ([19], [6], [2]))\n",
2507 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'qrxedkoywufmlvgsabpzjnicht')\n",
2508 "\n",
2509 "w_enigma.advance()\n",
2510 "assert(w_enigma.wheel_positions == (4, 12, 1))\n",
2511 "assert(cat(w_enigma.wheel_positions_l) == 'jfy')\n",
2512 "assert(w_enigma.notch_positions == ([19], [6], [3]))\n",
2513 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hpsukliagqefwvtbjxcodnmrzy')\n",
2514 "\n",
2515 "w_enigma.advance()\n",
2516 "assert(w_enigma.wheel_positions == (4, 12, 2))\n",
2517 "assert(cat(w_enigma.wheel_positions_l) == 'jfz')\n",
2518 "assert(w_enigma.notch_positions == ([19], [6], [4]))\n",
2519 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'zevnbpyqowrtxdifhkulscjmga')\n"
2520 ]
2521 },
2522 {
2523 "cell_type": "code",
2524 "execution_count": 190,
2525 "metadata": {
2526 "collapsed": true
2527 },
2528 "outputs": [],
2529 "source": [
2530 "w_enigma.set_wheels('i', 'd', 'z')\n",
2531 "\n",
2532 "w_enigma.advance()\n",
2533 "assert(w_enigma.wheel_positions == (3, 10, 3))\n",
2534 "assert(cat(w_enigma.wheel_positions_l) == 'ida')\n",
2535 "assert(w_enigma.notch_positions == ([18], [4], [5]))\n",
2536 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ikhpqrvcambzjondefwyxgsutl')\n",
2537 "\n",
2538 "w_enigma.advance()\n",
2539 "assert(w_enigma.wheel_positions == (3, 10, 4))\n",
2540 "assert(cat(w_enigma.wheel_positions_l) == 'idb')\n",
2541 "assert(w_enigma.notch_positions == ([18], [4], [6]))\n",
2542 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cdabskhgzwfmlqvunyexpojtri')\n",
2543 "\n",
2544 "w_enigma.advance()\n",
2545 "assert(w_enigma.wheel_positions == (3, 10, 5))\n",
2546 "assert(cat(w_enigma.wheel_positions_l) == 'idc')\n",
2547 "assert(w_enigma.notch_positions == ([18], [4], [7]))\n",
2548 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pcbwiqhgemyvjsuaftnroldzkx')\n",
2549 "\n",
2550 "w_enigma.advance()\n",
2551 "assert(w_enigma.wheel_positions == (3, 10, 6))\n",
2552 "assert(cat(w_enigma.wheel_positions_l) == 'idd')\n",
2553 "assert(w_enigma.notch_positions == ([18], [4], [8]))\n",
2554 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xcbfvdnouptmlghjzwykierasq')\n",
2555 "\n",
2556 "w_enigma.advance()\n",
2557 "assert(w_enigma.wheel_positions == (3, 10, 7))\n",
2558 "assert(cat(w_enigma.wheel_positions_l) == 'ide')\n",
2559 "assert(w_enigma.notch_positions == ([18], [4], [9]))\n",
2560 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xfvglbdynuseriwqpmkzjcoaht')\n",
2561 "\n",
2562 "w_enigma.advance()\n",
2563 "assert(w_enigma.wheel_positions == (3, 10, 8))\n",
2564 "assert(cat(w_enigma.wheel_positions_l) == 'idf')\n",
2565 "assert(w_enigma.notch_positions == ([18], [4], [10]))\n",
2566 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'tfpqlbouynsewjgcdxkahzmriv')\n",
2567 "\n",
2568 "w_enigma.advance()\n",
2569 "assert(w_enigma.wheel_positions == (3, 10, 9))\n",
2570 "assert(cat(w_enigma.wheel_positions_l) == 'idg')\n",
2571 "assert(w_enigma.notch_positions == ([18], [4], [11]))\n",
2572 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cjaunvlwtbygzexrspqidfhokm')\n",
2573 "\n",
2574 "w_enigma.advance()\n",
2575 "assert(w_enigma.wheel_positions == (3, 10, 10))\n",
2576 "assert(cat(w_enigma.wheel_positions_l) == 'idh')\n",
2577 "assert(w_enigma.notch_positions == ([18], [4], [12]))\n",
2578 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yltxkrqvowebzpingfucshjdam')\n",
2579 "\n",
2580 "w_enigma.advance()\n",
2581 "assert(w_enigma.wheel_positions == (3, 10, 11))\n",
2582 "assert(cat(w_enigma.wheel_positions_l) == 'idi')\n",
2583 "assert(w_enigma.notch_positions == ([18], [4], [13]))\n",
2584 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'myktluzrnxceaiqsohpdfwvjbg')\n",
2585 "\n",
2586 "w_enigma.advance()\n",
2587 "assert(w_enigma.wheel_positions == (3, 10, 12))\n",
2588 "assert(cat(w_enigma.wheel_positions_l) == 'idj')\n",
2589 "assert(w_enigma.notch_positions == ([18], [4], [14]))\n",
2590 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pynjrmiugdqxfcvakewzhoslbt')\n",
2591 "\n",
2592 "w_enigma.advance()\n",
2593 "assert(w_enigma.wheel_positions == (3, 10, 13))\n",
2594 "assert(cat(w_enigma.wheel_positions_l) == 'idk')\n",
2595 "assert(w_enigma.notch_positions == ([18], [4], [15]))\n",
2596 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mwvedyplnoxhaijgrqtszcbkfu')\n",
2597 "\n",
2598 "w_enigma.advance()\n",
2599 "assert(w_enigma.wheel_positions == (3, 10, 14))\n",
2600 "assert(cat(w_enigma.wheel_positions_l) == 'idl')\n",
2601 "assert(w_enigma.notch_positions == ([18], [4], [16]))\n",
2602 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'qcbrfeutvoxpnmjladzhgiykws')\n",
2603 "\n",
2604 "w_enigma.advance()\n",
2605 "assert(w_enigma.wheel_positions == (3, 10, 15))\n",
2606 "assert(cat(w_enigma.wheel_positions_l) == 'idm')\n",
2607 "assert(w_enigma.notch_positions == ([18], [4], [17]))\n",
2608 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'dnoahryetsmukbcvwfjilpqzgx')\n",
2609 "\n",
2610 "w_enigma.advance()\n",
2611 "assert(w_enigma.wheel_positions == (3, 10, 16))\n",
2612 "assert(cat(w_enigma.wheel_positions_l) == 'idn')\n",
2613 "assert(w_enigma.notch_positions == ([18], [4], [18]))\n",
2614 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nidcfehgbqsovalyjzkxwmutpr')\n",
2615 "\n",
2616 "w_enigma.advance()\n",
2617 "assert(w_enigma.wheel_positions == (3, 10, 17))\n",
2618 "assert(cat(w_enigma.wheel_positions_l) == 'ido')\n",
2619 "assert(w_enigma.notch_positions == ([18], [4], [19]))\n",
2620 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'joifxdulcarhzpbntkwqgysevm')\n",
2621 "\n",
2622 "w_enigma.advance()\n",
2623 "assert(w_enigma.wheel_positions == (3, 10, 18))\n",
2624 "assert(cat(w_enigma.wheel_positions_l) == 'idp')\n",
2625 "assert(w_enigma.notch_positions == ([18], [4], [20]))\n",
2626 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ptnlsxvozmwdjchayuebrgkfqi')\n",
2627 "\n",
2628 "w_enigma.advance()\n",
2629 "assert(w_enigma.wheel_positions == (3, 10, 19))\n",
2630 "assert(cat(w_enigma.wheel_positions_l) == 'idq')\n",
2631 "assert(w_enigma.notch_positions == ([18], [4], [21]))\n",
2632 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'slwopzqnmxybihdeguavrtcjkf')\n",
2633 "\n",
2634 "w_enigma.advance()\n",
2635 "assert(w_enigma.wheel_positions == (3, 10, 20))\n",
2636 "assert(cat(w_enigma.wheel_positions_l) == 'idr')\n",
2637 "assert(w_enigma.notch_positions == ([18], [4], [22]))\n",
2638 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hcbedwlamzogixkytsrqvufnpj')\n",
2639 "\n",
2640 "w_enigma.advance()\n",
2641 "assert(w_enigma.wheel_positions == (3, 10, 21))\n",
2642 "assert(cat(w_enigma.wheel_positions_l) == 'ids')\n",
2643 "assert(w_enigma.notch_positions == ([18], [4], [23]))\n",
2644 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'odxbjwzrmelkisavuhnyqpfctg')\n",
2645 "\n",
2646 "w_enigma.advance()\n",
2647 "assert(w_enigma.wheel_positions == (3, 10, 22))\n",
2648 "assert(cat(w_enigma.wheel_positions_l) == 'idt')\n",
2649 "assert(w_enigma.notch_positions == ([18], [4], [24]))\n",
2650 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'udgbfeclrwnhxksvtioqapjmzy')\n",
2651 "\n",
2652 "w_enigma.advance()\n",
2653 "assert(w_enigma.wheel_positions == (3, 10, 23))\n",
2654 "assert(cat(w_enigma.wheel_positions_l) == 'idu')\n",
2655 "assert(w_enigma.notch_positions == ([18], [4], [25]))\n",
2656 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nrdczqxmowvshaiufblypkjgte')\n",
2657 "\n",
2658 "w_enigma.advance()\n",
2659 "assert(w_enigma.wheel_positions == (3, 10, 24))\n",
2660 "assert(cat(w_enigma.wheel_positions_l) == 'idv')\n",
2661 "assert(w_enigma.notch_positions == ([18], [4], [0]))\n",
2662 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hkifjdoacebqtzgulyvmpsxwrn')\n",
2663 "\n",
2664 "w_enigma.advance()\n",
2665 "assert(w_enigma.wheel_positions == (3, 11, 25))\n",
2666 "assert(cat(w_enigma.wheel_positions_l) == 'iew')\n",
2667 "assert(w_enigma.notch_positions == ([18], [5], [1]))\n",
2668 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yptzuhofqvnmlkgbixwcejsrad')\n",
2669 "\n",
2670 "w_enigma.advance()\n",
2671 "assert(w_enigma.wheel_positions == (3, 11, 0))\n",
2672 "assert(cat(w_enigma.wheel_positions_l) == 'iex')\n",
2673 "assert(w_enigma.notch_positions == ([18], [5], [2]))\n",
2674 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'vkdcwhqfjibzsptngumoraeyxl')\n",
2675 "\n",
2676 "w_enigma.advance()\n",
2677 "assert(w_enigma.wheel_positions == (3, 11, 1))\n",
2678 "assert(cat(w_enigma.wheel_positions_l) == 'iey')\n",
2679 "assert(w_enigma.notch_positions == ([18], [5], [3]))\n",
2680 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'wenpbqrouxlkychdfgzvitajms')\n"
2681 ]
2682 },
2683 {
2684 "cell_type": "code",
2685 "execution_count": 191,
2686 "metadata": {},
2687 "outputs": [
2688 {
2689 "data": {
2690 "text/plain": [
2691 "([18], [5], [3])"
2692 ]
2693 },
2694 "execution_count": 191,
2695 "metadata": {},
2696 "output_type": "execute_result"
2697 }
2698 ],
2699 "source": [
2700 "w_enigma.notch_positions"
2701 ]
2702 },
2703 {
2704 "cell_type": "code",
2705 "execution_count": 192,
2706 "metadata": {},
2707 "outputs": [
2708 {
2709 "data": {
2710 "text/plain": [
2711 "('verylongtestmessagewithanextrabitofmessageforgoodmeasure',\n",
2712 " (3, 12, 6),\n",
2713 " ('i', 'f', 'd'),\n",
2714 " ([18], [6], [8]),\n",
2715 " 'urygzpdmxtwshqvfnbljaokice')"
2716 ]
2717 },
2718 "execution_count": 192,
2719 "metadata": {},
2720 "output_type": "execute_result"
2721 }
2722 ],
2723 "source": [
2724 "w_enigma.set_wheels('i', 'd', 'z')\n",
2725 "ct = w_enigma.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure')\n",
2726 "assert(ct == 'gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz')\n",
2727 "assert(w_enigma.wheel_positions == (3, 12, 6))\n",
2728 "assert(cat(w_enigma.wheel_positions_l) == 'ifd')\n",
2729 "assert(w_enigma.notch_positions == ([18], [6], [8]))\n",
2730 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'urygzpdmxtwshqvfnbljaokice')\n",
2731 "\n",
2732 "w_enigma.set_wheels('i', 'd', 'z')\n",
2733 "pt = w_enigma.encipher('gstsegeqdrthkfwesljjomfvcqwcfspxpfqqmewvddybarzwubxtpejz')\n",
2734 "assert(pt == 'verylongtestmessagewithanextrabitofmessageforgoodmeasure')\n",
2735 "\n",
2736 "pt, w_enigma.wheel_positions, w_enigma.wheel_positions_l, w_enigma.notch_positions, cat(w_enigma.lookup(l) for l in string.ascii_lowercase)"
2737 ]
2738 },
2739 {
2740 "cell_type": "code",
2741 "execution_count": 193,
2742 "metadata": {},
2743 "outputs": [
2744 {
2745 "name": "stdout",
2746 "output_type": "stream",
2747 "text": [
2748 "w_enigma.advance()\n",
2749 "assert(w_enigma.wheel_positions == (3, 10, 3))\n",
2750 "assert(cat(w_enigma.wheel_positions_l) == 'ida')\n",
2751 "assert(w_enigma.notch_positions == ([18], [4], [5]))\n",
2752 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ikhpqrvcambzjondefwyxgsutl')\n",
2753 "\n",
2754 "w_enigma.advance()\n",
2755 "assert(w_enigma.wheel_positions == (3, 10, 4))\n",
2756 "assert(cat(w_enigma.wheel_positions_l) == 'idb')\n",
2757 "assert(w_enigma.notch_positions == ([18], [4], [6]))\n",
2758 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cdabskhgzwfmlqvunyexpojtri')\n",
2759 "\n",
2760 "w_enigma.advance()\n",
2761 "assert(w_enigma.wheel_positions == (3, 10, 5))\n",
2762 "assert(cat(w_enigma.wheel_positions_l) == 'idc')\n",
2763 "assert(w_enigma.notch_positions == ([18], [4], [7]))\n",
2764 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pcbwiqhgemyvjsuaftnroldzkx')\n",
2765 "\n",
2766 "w_enigma.advance()\n",
2767 "assert(w_enigma.wheel_positions == (3, 10, 6))\n",
2768 "assert(cat(w_enigma.wheel_positions_l) == 'idd')\n",
2769 "assert(w_enigma.notch_positions == ([18], [4], [8]))\n",
2770 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xcbfvdnouptmlghjzwykierasq')\n",
2771 "\n",
2772 "w_enigma.advance()\n",
2773 "assert(w_enigma.wheel_positions == (3, 10, 7))\n",
2774 "assert(cat(w_enigma.wheel_positions_l) == 'ide')\n",
2775 "assert(w_enigma.notch_positions == ([18], [4], [9]))\n",
2776 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'xfvglbdynuseriwqpmkzjcoaht')\n",
2777 "\n",
2778 "w_enigma.advance()\n",
2779 "assert(w_enigma.wheel_positions == (3, 10, 8))\n",
2780 "assert(cat(w_enigma.wheel_positions_l) == 'idf')\n",
2781 "assert(w_enigma.notch_positions == ([18], [4], [10]))\n",
2782 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'tfpqlbouynsewjgcdxkahzmriv')\n",
2783 "\n",
2784 "w_enigma.advance()\n",
2785 "assert(w_enigma.wheel_positions == (3, 10, 9))\n",
2786 "assert(cat(w_enigma.wheel_positions_l) == 'idg')\n",
2787 "assert(w_enigma.notch_positions == ([18], [4], [11]))\n",
2788 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'cjaunvlwtbygzexrspqidfhokm')\n",
2789 "\n",
2790 "w_enigma.advance()\n",
2791 "assert(w_enigma.wheel_positions == (3, 10, 10))\n",
2792 "assert(cat(w_enigma.wheel_positions_l) == 'idh')\n",
2793 "assert(w_enigma.notch_positions == ([18], [4], [12]))\n",
2794 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yltxkrqvowebzpingfucshjdam')\n",
2795 "\n",
2796 "w_enigma.advance()\n",
2797 "assert(w_enigma.wheel_positions == (3, 10, 11))\n",
2798 "assert(cat(w_enigma.wheel_positions_l) == 'idi')\n",
2799 "assert(w_enigma.notch_positions == ([18], [4], [13]))\n",
2800 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'myktluzrnxceaiqsohpdfwvjbg')\n",
2801 "\n",
2802 "w_enigma.advance()\n",
2803 "assert(w_enigma.wheel_positions == (3, 10, 12))\n",
2804 "assert(cat(w_enigma.wheel_positions_l) == 'idj')\n",
2805 "assert(w_enigma.notch_positions == ([18], [4], [14]))\n",
2806 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'pynjrmiugdqxfcvakewzhoslbt')\n",
2807 "\n",
2808 "w_enigma.advance()\n",
2809 "assert(w_enigma.wheel_positions == (3, 10, 13))\n",
2810 "assert(cat(w_enigma.wheel_positions_l) == 'idk')\n",
2811 "assert(w_enigma.notch_positions == ([18], [4], [15]))\n",
2812 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mwvedyplnoxhaijgrqtszcbkfu')\n",
2813 "\n",
2814 "w_enigma.advance()\n",
2815 "assert(w_enigma.wheel_positions == (3, 10, 14))\n",
2816 "assert(cat(w_enigma.wheel_positions_l) == 'idl')\n",
2817 "assert(w_enigma.notch_positions == ([18], [4], [16]))\n",
2818 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'qcbrfeutvoxpnmjladzhgiykws')\n",
2819 "\n",
2820 "w_enigma.advance()\n",
2821 "assert(w_enigma.wheel_positions == (3, 10, 15))\n",
2822 "assert(cat(w_enigma.wheel_positions_l) == 'idm')\n",
2823 "assert(w_enigma.notch_positions == ([18], [4], [17]))\n",
2824 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'dnoahryetsmukbcvwfjilpqzgx')\n",
2825 "\n",
2826 "w_enigma.advance()\n",
2827 "assert(w_enigma.wheel_positions == (3, 10, 16))\n",
2828 "assert(cat(w_enigma.wheel_positions_l) == 'idn')\n",
2829 "assert(w_enigma.notch_positions == ([18], [4], [18]))\n",
2830 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nidcfehgbqsovalyjzkxwmutpr')\n",
2831 "\n",
2832 "w_enigma.advance()\n",
2833 "assert(w_enigma.wheel_positions == (3, 10, 17))\n",
2834 "assert(cat(w_enigma.wheel_positions_l) == 'ido')\n",
2835 "assert(w_enigma.notch_positions == ([18], [4], [19]))\n",
2836 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'joifxdulcarhzpbntkwqgysevm')\n",
2837 "\n",
2838 "w_enigma.advance()\n",
2839 "assert(w_enigma.wheel_positions == (3, 10, 18))\n",
2840 "assert(cat(w_enigma.wheel_positions_l) == 'idp')\n",
2841 "assert(w_enigma.notch_positions == ([18], [4], [20]))\n",
2842 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'ptnlsxvozmwdjchayuebrgkfqi')\n",
2843 "\n",
2844 "w_enigma.advance()\n",
2845 "assert(w_enigma.wheel_positions == (3, 10, 19))\n",
2846 "assert(cat(w_enigma.wheel_positions_l) == 'idq')\n",
2847 "assert(w_enigma.notch_positions == ([18], [4], [21]))\n",
2848 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'slwopzqnmxybihdeguavrtcjkf')\n",
2849 "\n",
2850 "w_enigma.advance()\n",
2851 "assert(w_enigma.wheel_positions == (3, 10, 20))\n",
2852 "assert(cat(w_enigma.wheel_positions_l) == 'idr')\n",
2853 "assert(w_enigma.notch_positions == ([18], [4], [22]))\n",
2854 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hcbedwlamzogixkytsrqvufnpj')\n",
2855 "\n",
2856 "w_enigma.advance()\n",
2857 "assert(w_enigma.wheel_positions == (3, 10, 21))\n",
2858 "assert(cat(w_enigma.wheel_positions_l) == 'ids')\n",
2859 "assert(w_enigma.notch_positions == ([18], [4], [23]))\n",
2860 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'odxbjwzrmelkisavuhnyqpfctg')\n",
2861 "\n",
2862 "w_enigma.advance()\n",
2863 "assert(w_enigma.wheel_positions == (3, 10, 22))\n",
2864 "assert(cat(w_enigma.wheel_positions_l) == 'idt')\n",
2865 "assert(w_enigma.notch_positions == ([18], [4], [24]))\n",
2866 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'udgbfeclrwnhxksvtioqapjmzy')\n",
2867 "\n",
2868 "w_enigma.advance()\n",
2869 "assert(w_enigma.wheel_positions == (3, 10, 23))\n",
2870 "assert(cat(w_enigma.wheel_positions_l) == 'idu')\n",
2871 "assert(w_enigma.notch_positions == ([18], [4], [25]))\n",
2872 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'nrdczqxmowvshaiufblypkjgte')\n",
2873 "\n",
2874 "w_enigma.advance()\n",
2875 "assert(w_enigma.wheel_positions == (3, 10, 24))\n",
2876 "assert(cat(w_enigma.wheel_positions_l) == 'idv')\n",
2877 "assert(w_enigma.notch_positions == ([18], [4], [0]))\n",
2878 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'hkifjdoacebqtzgulyvmpsxwrn')\n",
2879 "\n",
2880 "w_enigma.advance()\n",
2881 "assert(w_enigma.wheel_positions == (3, 11, 25))\n",
2882 "assert(cat(w_enigma.wheel_positions_l) == 'iew')\n",
2883 "assert(w_enigma.notch_positions == ([18], [5], [1]))\n",
2884 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'yptzuhofqvnmlkgbixwcejsrad')\n",
2885 "\n",
2886 "w_enigma.advance()\n",
2887 "assert(w_enigma.wheel_positions == (3, 11, 0))\n",
2888 "assert(cat(w_enigma.wheel_positions_l) == 'iex')\n",
2889 "assert(w_enigma.notch_positions == ([18], [5], [2]))\n",
2890 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'vkdcwhqfjibzsptngumoraeyxl')\n",
2891 "\n",
2892 "w_enigma.advance()\n",
2893 "assert(w_enigma.wheel_positions == (3, 11, 1))\n",
2894 "assert(cat(w_enigma.wheel_positions_l) == 'iey')\n",
2895 "assert(w_enigma.notch_positions == ([18], [5], [3]))\n",
2896 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'wenpbqrouxlkychdfgzvitajms')\n",
2897 "\n",
2898 "w_enigma.advance()\n",
2899 "assert(w_enigma.wheel_positions == (3, 11, 2))\n",
2900 "assert(cat(w_enigma.wheel_positions_l) == 'iez')\n",
2901 "assert(w_enigma.notch_positions == ([18], [5], [4]))\n",
2902 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'szgyqvclkoihurjwenaxmfptdb')\n",
2903 "\n"
2904 ]
2905 }
2906 ],
2907 "source": [
2908 "w_enigma.set_wheels('i', 'd', 'z')\n",
2909 "\n",
2910 "for i in range(26):\n",
2911 " w_enigma.advance()\n",
2912 " print('w_enigma.advance()')\n",
2913 " print(\"assert(w_enigma.wheel_positions == {})\".format(w_enigma.wheel_positions))\n",
2914 " print(\"assert(cat(w_enigma.wheel_positions_l) == '{}')\".format(cat(w_enigma.wheel_positions_l)))\n",
2915 " print(\"assert(w_enigma.notch_positions == {})\".format(w_enigma.notch_positions))\n",
2916 " print(\"assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n",
2917 " print()"
2918 ]
2919 },
2920 {
2921 "cell_type": "code",
2922 "execution_count": 215,
2923 "metadata": {},
2924 "outputs": [
2925 {
2926 "name": "stdout",
2927 "output_type": "stream",
2928 "text": [
2929 "self.assertEqual(self.enigma31.wheel_positions, (21, 5, 22))\n",
2930 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayt')\n",
2931 "self.assertEqual(self.enigma31.notch_positions, ([10], [25], [24]))\n",
2932 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'izhrgtecaslkywvqpdjfxonumb')\n",
2933 "\n",
2934 "self.enigma31.advance()\n",
2935 "self.assertEqual(self.enigma31.wheel_positions, (21, 5, 23))\n",
2936 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayu')\n",
2937 "self.assertEqual(self.enigma31.notch_positions, ([10], [25], [25]))\n",
2938 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'dtoavihgflwjnmcsrqpbzekyxu')\n",
2939 "\n",
2940 "self.enigma31.advance()\n",
2941 "self.assertEqual(self.enigma31.wheel_positions, (21, 5, 24))\n",
2942 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'ayv')\n",
2943 "self.assertEqual(self.enigma31.notch_positions, ([10], [25], [0]))\n",
2944 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'xquhtpsdwkjonmlfbvgecriazy')\n",
2945 "\n",
2946 "self.enigma31.advance()\n",
2947 "self.assertEqual(self.enigma31.wheel_positions, (21, 6, 25))\n",
2948 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azw')\n",
2949 "self.assertEqual(self.enigma31.notch_positions, ([10], [0], [1]))\n",
2950 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'dofapcluvmngjkbezyxwhitsrq')\n",
2951 "\n",
2952 "self.enigma31.advance()\n",
2953 "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 0))\n",
2954 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bax')\n",
2955 "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [2]))\n",
2956 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'jdlbmswztapcexrkuofiqygnvh')\n",
2957 "\n",
2958 "self.enigma31.advance()\n",
2959 "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 1))\n",
2960 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bay')\n",
2961 "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [3]))\n",
2962 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'iydcnuhgawsoxelztvkqfrjmbp')\n",
2963 "\n"
2964 ]
2965 }
2966 ],
2967 "source": [
2968 "w_enigma.set_wheels('a', 'y', 't')\n",
2969 "\n",
2970 "print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n",
2971 "print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n",
2972 "print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n",
2973 "print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n",
2974 "print()\n",
2975 "\n",
2976 "for i in range(5):\n",
2977 " w_enigma.advance()\n",
2978 " print('self.enigma31.advance()')\n",
2979 " print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n",
2980 " print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n",
2981 " print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n",
2982 " print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n",
2983 " print()"
2984 ]
2985 },
2986 {
2987 "cell_type": "code",
2988 "execution_count": 216,
2989 "metadata": {},
2990 "outputs": [
2991 {
2992 "name": "stdout",
2993 "output_type": "stream",
2994 "text": [
2995 "self.assertEqual(self.enigma31.wheel_positions, (21, 6, 22))\n",
2996 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'azt')\n",
2997 "self.assertEqual(self.enigma31.notch_positions, ([10], [0], [24]))\n",
2998 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'idjbptqwacsvnmregokfzlhyxu')\n",
2999 "\n",
3000 "self.enigma31.advance()\n",
3001 "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 23))\n",
3002 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bau')\n",
3003 "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [25]))\n",
3004 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'rniszouwcxtvqbfymadkglhjpe')\n",
3005 "\n",
3006 "self.enigma31.advance()\n",
3007 "self.assertEqual(self.enigma31.wheel_positions, (22, 7, 24))\n",
3008 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bav')\n",
3009 "self.assertEqual(self.enigma31.notch_positions, ([11], [1], [0]))\n",
3010 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'qijfsdmkbchugxtwazeolypnvr')\n",
3011 "\n",
3012 "self.enigma31.advance()\n",
3013 "self.assertEqual(self.enigma31.wheel_positions, (22, 8, 25))\n",
3014 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbw')\n",
3015 "self.assertEqual(self.enigma31.notch_positions, ([11], [2], [1]))\n",
3016 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'xprtlozyskjewqfbncidvumahg')\n",
3017 "\n",
3018 "self.enigma31.advance()\n",
3019 "self.assertEqual(self.enigma31.wheel_positions, (22, 8, 0))\n",
3020 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bbx')\n",
3021 "self.assertEqual(self.enigma31.notch_positions, ([11], [2], [2]))\n",
3022 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'vtfuyczoqxmpkwhlisrbdanjeg')\n",
3023 "\n",
3024 "self.enigma31.advance()\n",
3025 "self.assertEqual(self.enigma31.wheel_positions, (22, 8, 1))\n",
3026 "self.assertEqual(cat(self.enigma31.wheel_positions_l), 'bby')\n",
3027 "self.assertEqual(self.enigma31.notch_positions, ([11], [2], [3]))\n",
3028 "assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == 'tjrhzpqdobwxyuifgcvansklme')\n",
3029 "\n"
3030 ]
3031 }
3032 ],
3033 "source": [
3034 "w_enigma.set_wheels('a', 'z', 't')\n",
3035 "\n",
3036 "print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n",
3037 "print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n",
3038 "print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n",
3039 "print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n",
3040 "print()\n",
3041 "\n",
3042 "for i in range(5):\n",
3043 " w_enigma.advance()\n",
3044 " print('self.enigma31.advance()')\n",
3045 " print(\"self.assertEqual(self.enigma31.wheel_positions, {})\".format(w_enigma.wheel_positions))\n",
3046 " print(\"self.assertEqual(cat(self.enigma31.wheel_positions_l), '{}')\".format(cat(w_enigma.wheel_positions_l)))\n",
3047 " print(\"self.assertEqual(self.enigma31.notch_positions, {})\".format(w_enigma.notch_positions))\n",
3048 " print(\"assert(cat(self.enigma31.lookup(l) for l in string.ascii_lowercase) == '{}')\".format(cat(w_enigma.lookup(l) for l in string.ascii_lowercase)))\n",
3049 " print()"
3050 ]
3051 },
3052 {
3053 "cell_type": "code",
3054 "execution_count": 221,
3055 "metadata": {},
3056 "outputs": [],
3057 "source": [
3058 "w_enigma.set_wheels('i', 'z', 'd')\n",
3059 "ct = w_enigma.encipher('verylongtestmessagewithanextrabitofmessageforgoodmeasure')\n",
3060 "assert(ct == 'apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki')\n",
3061 "assert(w_enigma.wheel_positions == (4, 9, 10))\n",
3062 "assert(cat(w_enigma.wheel_positions_l) == 'jch')\n",
3063 "assert(w_enigma.notch_positions == ([19], [3], [12]))\n",
3064 "assert(cat(w_enigma.lookup(l) for l in string.ascii_lowercase) == 'mopnigfuesqwadbcktjrhylzvx')\n",
3065 "\n",
3066 "w_enigma.set_wheels('i', 'z', 'd')\n",
3067 "pt = w_enigma.decipher('apocwtjuikurcfivlozvhffkoacxufcekthcvodfqpxdjqyckdozlqki')\n",
3068 "assert(pt == 'verylongtestmessagewithanextrabitofmessageforgoodmeasure')"
3069 ]
3070 },
3071 {
3072 "cell_type": "code",
3073 "execution_count": 218,
3074 "metadata": {},
3075 "outputs": [
3076 {
3077 "data": {
3078 "text/plain": [
3079 "([19], [3], [12])"
3080 ]
3081 },
3082 "execution_count": 218,
3083 "metadata": {},
3084 "output_type": "execute_result"
3085 }
3086 ],
3087 "source": [
3088 "w_enigma.notch_positions"
3089 ]
3090 },
3091 {
3092 "cell_type": "code",
3093 "execution_count": 194,
3094 "metadata": {
3095 "collapsed": true
3096 },
3097 "outputs": [],
3098 "source": [
3099 "# Reflector B\n",
3100 "# Rotors III, I, II with rings 17, 11, 19\n",
3101 "# Plugboard pairs GU FZ BD LK TC PS HV WN JE AM\n",
3102 "\n",
3103 "tbt_enigma = Enigma(reflector_b_spec, \n",
3104 " wheel_iii_spec, wheel_iii_notches,\n",
3105 " wheel_i_spec, wheel_i_notches,\n",
3106 " wheel_ii_spec, wheel_ii_notches,\n",
3107 " 17, 11, 19,\n",
3108 " 'GU FZ BD LK TC PS HV WN JE AM'.lower())"
3109 ]
3110 },
3111 {
3112 "cell_type": "code",
3113 "execution_count": 195,
3114 "metadata": {},
3115 "outputs": [
3116 {
3117 "data": {
3118 "text/plain": [
3119 "'jbvbwwzfslhxnhzzccsngebmrnswgjonwbjnzcfgadeuoyameylmpvny'"
3120 ]
3121 },
3122 "execution_count": 195,
3123 "metadata": {},
3124 "output_type": "execute_result"
3125 }
3126 ],
3127 "source": [
3128 "tbt_enigma.set_wheels('a', 'q', 'v')\n",
3129 "ct = tbt_enigma.encipher('very long test message with an extra bit of message for good measure')\n",
3130 "ct"
3131 ]
3132 },
3133 {
3134 "cell_type": "code",
3135 "execution_count": 196,
3136 "metadata": {},
3137 "outputs": [
3138 {
3139 "data": {
3140 "text/plain": [
3141 "'SLGNC SZXLT KZEBG HSTGY WDMPR'"
3142 ]
3143 },
3144 "execution_count": 196,
3145 "metadata": {},
3146 "output_type": "execute_result"
3147 }
3148 ],
3149 "source": [
3150 "target_ct = 'SLGNC SZXLT KZEBG HSTGY WDMPR'\n",
3151 "target_ct"
3152 ]
3153 },
3154 {
3155 "cell_type": "code",
3156 "execution_count": 197,
3157 "metadata": {},
3158 "outputs": [
3159 {
3160 "data": {
3161 "text/plain": [
3162 "'Theyw erede tecte d byBri tishs hipsi nclud'"
3163 ]
3164 },
3165 "execution_count": 197,
3166 "metadata": {},
3167 "output_type": "execute_result"
3168 }
3169 ],
3170 "source": [
3171 "target_pt = 'Theyw erede tecte d byBri tishs hipsi nclud'\n",
3172 "target_pt"
3173 ]
3174 },
3175 {
3176 "cell_type": "code",
3177 "execution_count": 198,
3178 "metadata": {},
3179 "outputs": [
3180 {
3181 "data": {
3182 "text/plain": [
3183 "(29, 43)"
3184 ]
3185 },
3186 "execution_count": 198,
3187 "metadata": {},
3188 "output_type": "execute_result"
3189 }
3190 ],
3191 "source": [
3192 "len(target_ct), len(target_pt)"
3193 ]
3194 },
3195 {
3196 "cell_type": "code",
3197 "execution_count": 199,
3198 "metadata": {},
3199 "outputs": [
3200 {
3201 "name": "stdout",
3202 "output_type": "stream",
3203 "text": [
3204 "SLGNC SZXLT KZEBG HSTGY WDMPR\n",
3205 "Theyw erede tecte d byBri tishs hipsi nclud\n"
3206 ]
3207 }
3208 ],
3209 "source": [
3210 "print('{}\\n{}'.format(target_ct, target_pt))"
3211 ]
3212 },
3213 {
3214 "cell_type": "code",
3215 "execution_count": 200,
3216 "metadata": {},
3217 "outputs": [
3218 {
3219 "name": "stdout",
3220 "output_type": "stream",
3221 "text": [
3222 "Theyw erede tecte d byBri tishs hipsi nclud\n",
3223 "SLGNC SZXLT KZEBG HSTGY WDMPR\n",
3224 "slgncszxltkzebghstgywdmprucuzqdqzpve\n",
3225 "theyweredetectedbybritish\n"
3226 ]
3227 }
3228 ],
3229 "source": [
3230 "tbt_enigma.set_wheels('a', 'a', 'a')\n",
3231 "this_pt = tbt_enigma.encipher(target_ct)\n",
3232 "\n",
3233 "tbt_enigma.set_wheels('a', 'a', 'a')\n",
3234 "this_ct = tbt_enigma.encipher(target_pt)\n",
3235 "\n",
3236 "\n",
3237 "print('{}\\n{}\\n{}\\n{}'.format(target_pt, target_ct, this_ct, this_pt))"
3238 ]
3239 },
3240 {
3241 "cell_type": "code",
3242 "execution_count": 201,
3243 "metadata": {
3244 "collapsed": true
3245 },
3246 "outputs": [],
3247 "source": [
3248 "import itertools"
3249 ]
3250 },
3251 {
3252 "cell_type": "code",
3253 "execution_count": 202,
3254 "metadata": {
3255 "collapsed": true
3256 },
3257 "outputs": [],
3258 "source": [
3259 "def str_ham(s1, s2):\n",
3260 " \"\"\"Hamming distance for strings\"\"\"\n",
3261 " return sum(1 for c1, c2 in zip(s1, s2) if c1 != c2)"
3262 ]
3263 },
3264 {
3265 "cell_type": "code",
3266 "execution_count": 203,
3267 "metadata": {},
3268 "outputs": [
3269 {
3270 "data": {
3271 "text/plain": [
3272 "0"
3273 ]
3274 },
3275 "execution_count": 203,
3276 "metadata": {},
3277 "output_type": "execute_result"
3278 }
3279 ],
3280 "source": [
3281 "str_ham('hello', 'hello')"
3282 ]
3283 },
3284 {
3285 "cell_type": "markdown",
3286 "metadata": {},
3287 "source": [
3288 "A brute-force check of all message settings, looking for the one that generates the target text."
3289 ]
3290 },
3291 {
3292 "cell_type": "code",
3293 "execution_count": 204,
3294 "metadata": {},
3295 "outputs": [
3296 {
3297 "name": "stdout",
3298 "output_type": "stream",
3299 "text": [
3300 "best ('a', 'a', 'a') 29\n",
3301 "best ('a', 'a', 'a') 29\n",
3302 "best ('a', 'a', 'a') 29\n",
3303 "best ('a', 'a', 'a') 29\n",
3304 "1 loop, best of 3: 20.6 s per loop\n"
3305 ]
3306 }
3307 ],
3308 "source": [
3309 "%%timeit\n",
3310 "best = ('a', 'a', 'a')\n",
3311 "best_hd = 10000\n",
3312 "for w1, w2, w3 in itertools.product(string.ascii_lowercase, repeat=3):\n",
3313 " tbt_enigma.set_wheels(w1, w2, w3)\n",
3314 " this_ct = tbt_enigma.encipher(target_pt)\n",
3315 " if this_ct == target_ct:\n",
3316 " print(w1, w2, w3)\n",
3317 " if str_ham(this_ct, target_ct) < best_hd:\n",
3318 " best = (w1, w2, w3)\n",
3319 " best_hd = str_ham(this_ct, target_ct)\n",
3320 "print('best', best, best_hd)"
3321 ]
3322 },
3323 {
3324 "cell_type": "code",
3325 "execution_count": 205,
3326 "metadata": {},
3327 "outputs": [
3328 {
3329 "name": "stdout",
3330 "output_type": "stream",
3331 "text": [
3332 "Wheels now ['a', 'a', 'b'] enciphering t -> s\n",
3333 "Wheels now ['a', 'a', 'c'] enciphering h -> l\n",
3334 "Wheels now ['a', 'a', 'd'] enciphering e -> g\n",
3335 "Wheels now ['a', 'a', 'e'] enciphering y -> n\n",
3336 "Wheels now ['a', 'b', 'f'] enciphering w -> c\n",
3337 "Wheels now ['a', 'b', 'g'] enciphering e -> s\n",
3338 "Wheels now ['a', 'b', 'h'] enciphering r -> z\n",
3339 "Wheels now ['a', 'b', 'i'] enciphering e -> x\n",
3340 "Wheels now ['a', 'b', 'j'] enciphering d -> l\n",
3341 "Wheels now ['a', 'b', 'k'] enciphering e -> t\n",
3342 "Wheels now ['a', 'b', 'l'] enciphering t -> k\n",
3343 "Wheels now ['a', 'b', 'm'] enciphering e -> z\n",
3344 "Wheels now ['a', 'b', 'n'] enciphering c -> e\n",
3345 "Wheels now ['a', 'b', 'o'] enciphering t -> b\n",
3346 "Wheels now ['a', 'b', 'p'] enciphering e -> g\n",
3347 "Wheels now ['a', 'b', 'q'] enciphering d -> h\n",
3348 "Wheels now ['a', 'b', 'r'] enciphering b -> s\n",
3349 "Wheels now ['a', 'b', 's'] enciphering y -> t\n",
3350 "Wheels now ['a', 'b', 't'] enciphering b -> g\n",
3351 "Wheels now ['a', 'b', 'u'] enciphering r -> y\n",
3352 "Wheels now ['a', 'b', 'v'] enciphering i -> w\n",
3353 "Wheels now ['a', 'b', 'w'] enciphering t -> d\n",
3354 "Wheels now ['a', 'b', 'x'] enciphering i -> m\n",
3355 "Wheels now ['a', 'b', 'y'] enciphering s -> p\n",
3356 "Wheels now ['a', 'b', 'z'] enciphering h -> r\n",
3357 "Wheels now ['a', 'b', 'a'] enciphering s -> u\n",
3358 "Wheels now ['a', 'b', 'b'] enciphering h -> c\n",
3359 "Wheels now ['a', 'b', 'c'] enciphering i -> u\n",
3360 "Wheels now ['a', 'b', 'd'] enciphering p -> z\n",
3361 "Wheels now ['a', 'b', 'e'] enciphering s -> q\n",
3362 "Wheels now ['a', 'c', 'f'] enciphering i -> d\n",
3363 "Wheels now ['a', 'c', 'g'] enciphering n -> q\n",
3364 "Wheels now ['a', 'c', 'h'] enciphering c -> z\n",
3365 "Wheels now ['a', 'c', 'i'] enciphering l -> p\n",
3366 "Wheels now ['a', 'c', 'j'] enciphering u -> v\n",
3367 "Wheels now ['a', 'c', 'k'] enciphering d -> e\n"
3368 ]
3369 },
3370 {
3371 "data": {
3372 "text/plain": [
3373 "('slgncszxltkzebghstgywdmprucuzqdqzpve', 'SLGNC SZXLT KZEBG HSTGY WDMPR')"
3374 ]
3375 },
3376 "execution_count": 205,
3377 "metadata": {},
3378 "output_type": "execute_result"
3379 }
3380 ],
3381 "source": [
3382 "tbt_enigma.set_wheels('a', 'a', 'a')\n",
3383 "tbt_enigma.encipher(target_pt, debug=True), target_ct"
3384 ]
3385 },
3386 {
3387 "cell_type": "code",
3388 "execution_count": 206,
3389 "metadata": {},
3390 "outputs": [
3391 {
3392 "name": "stdout",
3393 "output_type": "stream",
3394 "text": [
3395 "Wheels now ['a', 'a', 'b'] enciphering s -> t\n",
3396 "Wheels now ['a', 'a', 'c'] enciphering l -> h\n",
3397 "Wheels now ['a', 'a', 'd'] enciphering g -> e\n",
3398 "Wheels now ['a', 'a', 'e'] enciphering n -> y\n",
3399 "Wheels now ['a', 'b', 'f'] enciphering c -> w\n",
3400 "Wheels now ['a', 'b', 'g'] enciphering s -> e\n",
3401 "Wheels now ['a', 'b', 'h'] enciphering z -> r\n",
3402 "Wheels now ['a', 'b', 'i'] enciphering x -> e\n",
3403 "Wheels now ['a', 'b', 'j'] enciphering l -> d\n",
3404 "Wheels now ['a', 'b', 'k'] enciphering t -> e\n",
3405 "Wheels now ['a', 'b', 'l'] enciphering k -> t\n",
3406 "Wheels now ['a', 'b', 'm'] enciphering z -> e\n",
3407 "Wheels now ['a', 'b', 'n'] enciphering e -> c\n",
3408 "Wheels now ['a', 'b', 'o'] enciphering b -> t\n",
3409 "Wheels now ['a', 'b', 'p'] enciphering g -> e\n",
3410 "Wheels now ['a', 'b', 'q'] enciphering h -> d\n",
3411 "Wheels now ['a', 'b', 'r'] enciphering s -> b\n",
3412 "Wheels now ['a', 'b', 's'] enciphering t -> y\n",
3413 "Wheels now ['a', 'b', 't'] enciphering g -> b\n",
3414 "Wheels now ['a', 'b', 'u'] enciphering y -> r\n",
3415 "Wheels now ['a', 'b', 'v'] enciphering w -> i\n",
3416 "Wheels now ['a', 'b', 'w'] enciphering d -> t\n",
3417 "Wheels now ['a', 'b', 'x'] enciphering m -> i\n",
3418 "Wheels now ['a', 'b', 'y'] enciphering p -> s\n",
3419 "Wheels now ['a', 'b', 'z'] enciphering r -> h\n"
3420 ]
3421 },
3422 {
3423 "data": {
3424 "text/plain": [
3425 "('theyweredetectedbybritish', 'Theyw erede tecte d byBri tishs hipsi nclud')"
3426 ]
3427 },
3428 "execution_count": 206,
3429 "metadata": {},
3430 "output_type": "execute_result"
3431 }
3432 ],
3433 "source": [
3434 "tbt_enigma.set_wheels('a', 'a', 'a')\n",
3435 "tbt_enigma.encipher(target_ct, debug=True), target_pt"
3436 ]
3437 },
3438 {
3439 "cell_type": "code",
3440 "execution_count": 207,
3441 "metadata": {},
3442 "outputs": [
3443 {
3444 "data": {
3445 "text/plain": [
3446 "('theyweredetectedbybritish', 'Theyw erede tecte d byBri tishs hipsi nclud')"
3447 ]
3448 },
3449 "execution_count": 207,
3450 "metadata": {},
3451 "output_type": "execute_result"
3452 }
3453 ],
3454 "source": [
3455 "tbt_enigma.set_wheels('a', 'a', 'a')\n",
3456 "tbt_enigma.encipher(target_ct), target_pt"
3457 ]
3458 },
3459 {
3460 "cell_type": "code",
3461 "execution_count": 208,
3462 "metadata": {},
3463 "outputs": [
3464 {
3465 "data": {
3466 "text/plain": [
3467 "'mdtbjzuvielkawosqrpcghnxyf'"
3468 ]
3469 },
3470 "execution_count": 208,
3471 "metadata": {},
3472 "output_type": "execute_result"
3473 }
3474 ],
3475 "source": [
3476 "cat(tbt_enigma.plugboard.forward(l) for l in string.ascii_lowercase)"
3477 ]
3478 },
3479 {
3480 "cell_type": "code",
3481 "execution_count": 209,
3482 "metadata": {},
3483 "outputs": [
3484 {
3485 "data": {
3486 "text/plain": [
3487 "10"
3488 ]
3489 },
3490 "execution_count": 209,
3491 "metadata": {},
3492 "output_type": "execute_result"
3493 }
3494 ],
3495 "source": [
3496 "tbt_enigma.set_wheels('a', 'a', 'a')\n",
3497 "tbt_enigma.left_wheel.position"
3498 ]
3499 },
3500 {
3501 "cell_type": "code",
3502 "execution_count": null,
3503 "metadata": {
3504 "collapsed": true
3505 },
3506 "outputs": [],
3507 "source": []
3508 }
3509 ],
3510 "metadata": {
3511 "kernelspec": {
3512 "display_name": "Python 3",
3513 "language": "python",
3514 "name": "python3"
3515 },
3516 "language_info": {
3517 "codemirror_mode": {
3518 "name": "ipython",
3519 "version": 3
3520 },
3521 "file_extension": ".py",
3522 "mimetype": "text/x-python",
3523 "name": "python",
3524 "nbconvert_exporter": "python",
3525 "pygments_lexer": "ipython3",
3526 "version": "3.5.3"
3527 }
3528 },
3529 "nbformat": 4,
3530 "nbformat_minor": 1
3531 }