File: guide.rst

package info (click to toggle)
python-enigma 0.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 268 kB
  • sloc: python: 750; makefile: 132
file content (364 lines) | stat: -rw-r--r-- 16,171 bytes parent folder | download | duplicates (3)
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
User's guide
============

This short guide attempts to get you up and running with Py-Enigma quickly. For
more detailed information, please see the :doc:`Reference manual <reference>`.


If you are new to Enigma machines
---------------------------------

This guide assumes you know the basics of Enigma machines. Before proceeding
with Py-Enigma please explore some of the links presented in the
:ref:`references-label`. For the most complete and detailed description of how
an Enigma machine works, please see Dirk Rijmenants' excellent `Technical
Details of the Enigma Machine
<http://users.telenet.be/d.rijmenants/en/enigmatech.htm>`_.

Building your Enigma machine
----------------------------

If you are interested in working with historically accurate Enigma machines, the
easiest way to build your first machine is to use the "key sheet" shortcut
functions. If instead you wish to experiment with custom designed rotors or
configurations, you can build a machine out of separate components by hand.
These two approaches are demonstrated in the following sections.

Using key sheet shortcuts
~~~~~~~~~~~~~~~~~~~~~~~~~

During the war, Enigma machine operators re-configured their machines every day
according to a code book, or key sheet, to help increase security. Each key
sheet contained daily Enigma settings for one month. Before transmitting the
first message of the day, the operator looked up the current day on the key
sheet for the given month and configured the machine accordingly. The key sheet
specified:

* *Walzenlage*: what rotors to use, and what order to put them into the machine
* *Ringstellung*: the ring settings for each rotor
* *Steckerverbindungen*: the plugboard connections
* *Kenngruppen*: special text fragments that should be transmitted to identify
  the transmitter's key settings to any receiver. This is also known as the
  *message indicator*.

The reflector setting was usually fixed and not changed once in the field. The
choice of reflector seems to have been decided at the unit level to establish
different networks. Of course our simulation is not hindered by these logistical
concerns, and our simulated key sheets will also specify reflector type.

When an Enigma machine operator received a message from a radio operator,
probably his first task was to determine what key settings were used to transmit
the message. For example, the message could have been transmitted the day
before, and he was only handed the message just now. This was accomplished by
transitting (in the clear) certain text fragments, the so-called *Kenngruppen*,
at certain points in the message. By examining these text groups, the operator
could scan the key sheet for today and perhaps the past few days and hopefully
identify what day the message was sent. The operator would then reconfigure his
Enigma machine accordingly and decode the message. The *Kenngruppen* was ignored
when decrypting the actual message.

The :class:`EnigmaMachine <enigma.machine.EnigmaMachine>` class has two
class methods for constructing machines from key sheet data. The first class
method is called :meth:`from_key_sheet
<enigma.machine.EnigmaMachine.from_key_sheet>`::

   from enigma.machine import EnigmaMachine

   machine = EnigmaMachine.from_key_sheet(
          rotors='IV V I', 
          reflector='B',
          ring_settings='21 15 16',
          plugboard_settings='AC LS BQ WN MY UV FJ PZ TR OK')

This is all well and good if you wish to simulate an army or air force Enigma
machine. But what about navy (*Kriegsmarine*) models? Navy Enigma machines and
key sheets have slightly different nomenclature. This is also no problem for
Py-Enigma::
   
   machine = EnigmaMachine.from_key_sheet(
          rotors='Beta VII IV V',
          reflector='B-Thin',
          ring_settings='G N O',
          plugboard_settings='18/26 17/4 21/6 3/16 19/14 22/7 8/1 12/25 5/9 10/15')

Some notes on the parameters:

* ``rotors`` can either be a space separated list of rotor names, or a list of
  rotor name strings. For a complete list of supported rotor names, see
  :ref:`rotor-table-label`.
* ``reflector`` is a string that names the reflector to use. For a complete list
  of supported reflector names, see :ref:`reflector-table-label`.
* ``ring_settings`` can be a space separated list of uppercase letters or
  numbers, as would be found on a key sheet. An empty string or ``None`` means
  ring settings of all 'A' or 1.
* ``plugboard_settings`` can either be space separated uppercase letter pairs,
  or slash separated numbers. Note that 'AB' is equivalent to '1/2', etc.

.. warning::

   ``ring_settings`` can also take a list of integers, but these integers are
   **0-based**. Remember that when using a string of numbers they are
   **1-based** to correspond to actual historical key sheet data. In other
   words, these values produce identical ring settings: ``[0, 5, 15]``,
   ``'A F P'``, and ``'1 6 16'``.

The second shortcut function allows you to keep your key settings stored in an
external file::

   from enigma.machine import EnigmaMachine

   with open('my_enigma_keys.txt', 'r') as f:
      machine = EnigmaMachine.from_key_file(f, day=13)

The class method :meth:`from_key_file
<enigma.machine.EnigmaMachine.from_key_file>` builds an :class:`EnigmaMachine
<enigma.machine.EnigmaMachine>` from settings stored in a simulated monthly key
sheet file. The format of this file is explained in :doc:`keyfile`. The ``day``
argument allows you to specify the day of the month (1-31). If this parameter is
omitted or ``None``, the day value is obtained from the current date.


Constructing by hand
~~~~~~~~~~~~~~~~~~~~

It is also possible to "build an Enigma machine by hand" by explicitly providing
the component objects to the :class:`EnigmaMachine
<enigma.machine.EnigmaMachine>` constructor. This makes it possible to invent
different rotor and reflector types::

   from enigma.rotors.rotor import Rotor
   from enigma.plugboard import Plugboard
   from enigma.machine import EnigmaMachine

   r1 = Rotor('my rotor1', 'EKMFLGDQVZNTOWYHXUSPAIBRCJ', ring_setting=0, stepping='Q')
   r2 = Rotor('my rotor2', 'AJDKSIRUXBLHWTMCQGZNPYFVOE', ring_setting=5, stepping='E')
   r3 = Rotor('my rotor3', 'BDFHJLCPRTXVZNYEIWGAKMUSQO', ring_setting=15, stepping='V')

   reflector = Rotor('my reflector', 'FVPJIAOYEDRZXWGCTKUQSBNMHL')

   pb = Plugboard.from_key_sheet('PO ML IU KJ NH YT GB VF RE DC')

   machine = EnigmaMachine([r1, r2, r3], reflector, pb)

This example illustrates a few different things:

* When calling the :class:`Rotor <enigma.rotors.rotor.Rotor>` constructor
  directly, the internal wiring is specified as a 26-character long string which
  specifies the cipher substitution. This notation is consistent with several
  online sources of Enigma information.
* :class:`Rotor <enigma.rotors.rotor.Rotor>` ``ring_setting`` arguments are
  0-based integers (0-25).
* :class:`Rotor <enigma.rotors.rotor.Rotor>` ``stepping`` arguments specify 
  when rotors turn their neighbors. For more information see the 
  :class:`Rotor <enigma.rotors.rotor.Rotor>` reference.
* Reflectors are simulated as rotors that have no ring setting or stepping
  capability.
* :class:`Plugboard <enigma.plugboard.Plugboard>` objects have a convenient
  :meth:`from_key_sheet <enigma.plugboard.Plugboard.from_key_sheet>` class method 
  constructor that works in exactly the same way as the previous example.
* When calling the :class:`EnigmaMachine <enigma.machine.EnigmaMachine>`
  constructor directly, the rotor assignment is specified by a list of rotors
  where order specifies the left-to-right order in the machine.

.. note::

   If you decide to create your own reflector, and you desire to maintain
   reciprocal encryption & decryption (a fundamental characteristic of war-time
   Enigma machines), your connections must be made in pairs. Thus if you wire
   'A' to 'G', you must also wire 'G' to 'A', and so on.

For more details on the various constructor arguments, please see the
:doc:`reference`.
   

Encrypting & Decrypting
-----------------------

Now that you have built your Enigma machine, you probably want to start using it
to encrypt and decrypt text! The first step is to set your initial rotor
positions. This is critical if you want someone else to understand your message!

::

   machine.set_display('XYZ')       # set rotor positions

The value given to :meth:`set_display
<enigma.machine.EnigmaMachine.set_display>` is a simple string, which must have
one uppercase letter per rotor in your machine. In this example, we are
setting the leftmost rotor to 'X', the middle rotor to 'Y', and the rightmost
rotor to 'Z'.

If you ever need to obtain the current rotor positions, you can use the
:meth:`get_display <enigma.machine.EnigmaMachine.get_display>` method::

   position = machine.get_display()    # read rotor position

.. note::

   The :meth:`set_display <enigma.machine.EnigmaMachine.set_display>` method
   always takes letters for simulation convenience. If you are simulating an
   Enigma machine with numeric rotors, you'll have to translate the numbers to
   the appropriate letters. On actual Enigma machines, a label on the inside box
   lid had such a table to aid the operator.

Next, you can simulate a single key press::

   c = machine.key_press('A')

The input to :meth:`key_press <enigma.machine.EnigmaMachine.key_press>` is a
string that consists of a single uppercase letter. Invalid input will raise an
``EnigmaError`` exception. The transformed text is returned.

To process a whole string of text::

   c = machine.process_text('This is a test!', replace_char='X')

The :meth:`process_text <enigma.machine.EnigmaMachine.process_text>` method
accepts an arbitrary string and performs some processing on it before internally
calling :meth:`key_press <enigma.machine.EnigmaMachine.key_press>` on each element of
the string. 

First, all input is converted to uppercase. Next, any character not in the
Enigma uppercase character set is either replaced or dropped from the input
according to the ``replace_char`` parameter. If ``replace_char`` is a string of
one character, it is used as the replacement character. If it is ``None``, the
invalid input character is removed from the message. Thus the previous example
is equivalent to::

   c = machine.process_text('THISXISXAXTESTX')

This is all you need to start creating encrypted and decrypted messages.


Example communication procedure
-------------------------------

The Wehrmacht had various elaborate procedures for transmitting and receiving
messages. These procedures varied by service branch and also changed during the
course of the war.  In general, the Kriegsmarine procedures were more elaborate
and involved not only key sheets but other auxiliary documents. On top of this,
each branch of the military had its own conventions for encoding abbreviations,
numbers, space characters, place names, etc. Important words or phrases may need
to be repeated or stressed in some way. 

We will now present a simplified scenario based on a procedure employed by the
army (*Heer*) after 1940. This example is based upon one found in Dirk
Rijmenants' simulator manual, which is based upon a real-life example from Frode
Weierud's `Cryptocellar <http://cryptocellar.org>`_ website.

Suppose a message needs to be transmitted. The operator of the transmitting
machine consults his key sheet and configures his machine according to the daily
settings found inside. Let's suppose the key sheet dictates the following
initial parameters for the current day:

* Rotor usage and order is *II IV V*
* Ring settings for each rotor, in order, are: *B U L*
* Plugboard settings are: *AV BS CG DL FU HZ IN KM OW RX*
* One of the daily Kenngruppen possibilities is *UGZ*

Let us also assume the reflector employed by this army unit is 'B'.

The operator then configures his machine::

   machine = EnigmaMachine.from_key_sheet(
          rotors='II IV V',
          reflector='B',
          ring_settings='B U L',
          plugboard_settings='AV BS CG DL FU HZ IN KM OW RX')

Suppose the Enigma operator was handed a message for transmit by an officer
which reads "The Russians are coming!" The operator would first randomly decide
two things:

* Initial rotor positions, say ``WXC``
* A three letter *message key*, say ``BLA``

The operator would then turn the rotor thumb wheels to set the initial rotor
position and then type the three letter message key to produce an encrypted
message key::

   machine.set_display('WXC')    # set initial rotor positions
   enc_key = machine.process_text('BLA')      # encrypt message key

In this example, the encrypted key turns out to be ``KCH``. This is written down
for later. 

The operator then sets the rotors to the unencrypted message key ``BLA`` and
then types in the officer's message, performing various substitutions and
transformations according to training and current procedures. In our simple
case, he performs the following::

   machine.set_display('BLA')    # use message key BLA
   ciphertext = machine.process_text('THEXRUSSIANSXAREXCOMINGX')
   print(ciphertext)

This produces the ciphertext ``NIBLFMYMLLUFWCASCSSNVHAZ``.

Next, between the Enigma operator and the radio operator, a message is formed
up. This message includes the following components:

* The time of transmission
* The station identification for transmitter and intended recipient(s)
* The message length; in our case this is 24
* The initial rotor positions in unencrypted form (``WXC``)
* The encrypted message key value (``KCH``)
* The unencrypted message indicator (*Kenngruppen*)
* The encrypted message contents

In our example, the message handed over to the radio operator to be transmitted
by either Morse code or perhaps even voice would look something like this::

   U6Z DE C 1500 = 24 = WXC KCH =

   BNUGZ NIBLF MYMLL UFWCA 
   SCSSN VHAZ=

The top line indicates day 31, station C transmits to station U6Z, sent at 1500
hours and contains 24 letters. The starting position is ``WXC`` and the
encrypted message key is ``KCH``.

Next we have the body of the message. The army transmitted messages in 5 letter
groups. The first group contains the Kenngruppen, or indicator. Procedure
required the operator pick one of the Kenngruppen possibilities from the key
sheet, and then pad it out with two random letters. Here the operator chose to
prepend ``BN`` to the Kenngruppen value of ``UGZ``. He could have also appended
the two letters, or perhaps appended one and prepended the other.

After the message indicator group, the encrypted text follows in 5 letter
groups.

Now at receiving station U6Z, the radio operator receives the over-the-air
message and types or writes it up in the form shown and hands it to the Enigma
operator.

The Enigma operator first looks for the message indicator. He uses the group
``BNUGZ`` and scans his key sheet for either ``BNU``, ``NUG``, or ``UGZ``. He
could presumably also use the date information found in the message preamble to
help his search of the key sheet. If everything checks out the operator now
knows which entry in his monthly key sheet to use.  Thus, as was done at the
transmitting station, he configures his Enigma according to the key sheet::

   machine = EnigmaMachine.from_key_sheet(
          rotors='II IV V',
          reflector='B',
          ring_settings='B U L',
          plugboard_settings='AV BS CG DL FU HZ IN KM OW RX')

The receiving operator then must decrypt the message key::

   machine.set_display('WXC')
   msg_key = machine.process_text('KCH')

This should reveal that the message key is the original ``BLA``. The rotors are
then set to this value and the message can be decrypted, taking care to ignore
the Kenngruppen::

   machine.set_display(msg_key)     # original message key is BLA
   plaintext = machine.process_text('NIBLFMYMLLUFWCASCSSNVHAZ')
   print(plaintext)

The Enigma operator then decodes the message "THEXRUSSIANSXAREXCOMINGX". He then
uses his training and procedures to further process the message. Finally, the
somewhat troubling message "The Russians are coming" is handed to his commanding
officer.