crypto-enigma-0.0.2.1: An Enigma machine simulator with display.

Copyright(c) 2014-2015 Roy Levien
LicenseBSD3
Maintainerroyl@aldaron.com
Stabilityexperimental
PortabilityPOSIX
Safe HaskellSafe
LanguageHaskell2010

Crypto.Enigma

Contents

Description

An Enigma machine simulator with rudimentary display, currently limited to the I, M3, and M4 models.

Richer display is provided by Crypto.Enigma.Display.

Synopsis

Machine components

data Component Source

A component used to construct an Enigma machine (embodied in an EnigmaConfig) identified by its name, and characterized by its physical wiring and additionally — for rotors other than the reflector — by turnovers which govern the stepping of the machine in which it is installed.

Instances

Show Component Source

Show a Component as a formatted string consisting of its name, wiring, and turnovers (if any).

name :: Component -> Name Source

The component's Name.

wiring :: Component -> Wiring Source

The component's Wiring.

type Name = String Source

A string identifying a Component of an Enigma machine. For rotors (including the reflector) this is one of the conventional letter or roman numeral designations (e.g., "IV" or "β"). For the plugboard this is the conventional string of letter pairs (separated by periods), indicating letters wired together by plugging (e.g., "AU.ZM.ZL.RQ"). Absence or non-use of a plugboard can be indicated with a lone "~". See name.

type Wiring = Mapping Source

The Mapping established by the physical wiring of a Component, when 01 is at the window position for rotors, and by the plug arrangement for the plugboard. See wiring.

type Turnovers = String Source

The list of letters on the rotor's ring that appear at the window when a Component's ring is in the turnover position. Not applicable (and empty) for the plugboard and for reflectors. See turnovers.

component :: Name -> Component Source

The Component with the specified Name.

rotors :: [Name] Source

The list of valid Component Names for rotors.

reflectors :: [Name] Source

The list of valid Component Names for reflectors.

Machine configurations and transitions

data EnigmaConfig Source

The complete description of the state of an Enigma machine, consisting components, positions, and rings.

Instances

Eq EnigmaConfig Source 
Read EnigmaConfig Source

Read the elements of a conventional specification (see configEnigma) joined by spaces into a single string.

>>> let cfg = configEnigma "b-β-V-VIII-III" "XQVI" "UX.MO.KZ.AY.EF.PL" "03.17.24.11"
>>> let cfg' = read "b-β-V-VIII-III XQVI UX.MO.KZ.AY.EF.PL 03.17.24.11" :: EnigmaConfig
>>> cfg == cfg'
True
Show EnigmaConfig Source

Show the elements of a conventional specification (see configEnigma) joined by spaces into a single string.

>>> configEnigma "b-β-V-VIII-II" "XQVI" "UX.MO.KZ.AY.EF.PL" "03.17.24.11"
"b-β-V-VIII-III XQVI UX.MO.KZ.AY.EF.PL 03.17.24.11"

configEnigma :: String -> String -> String -> String -> EnigmaConfig Source

A (safe public, "smart") constructor that does validation and takes a conventional specification as input, in the form of four strings:

  • The rotor Names, separated by dashes (e.g. "C-V-I-II"); see Name.
  • The letters visible at the windows (e.g. "MQR"); see windows.
  • The plugboard specification (which may me omitted with "~"); see Name.
  • The position of the letter ring on each rotor, separated by periods (e.g. "22.11.16"); see rings.

Following convention, the elements of these specifications are in physical machine order as the operator sees them, which is the reverse of the order in which they are encountered in processing (see stages).

Validation is permissive, allowing for ahistorical collections and numbers of rotors (including reflectors at the rotor stage, and trivial degenerate machines; e.g., configEnigma "-" "A" "" "01"), and any number of (non-contradictory) plugboard wirings (including none).

stages :: EnigmaConfig -> [Stage] Source

The sequential, (forward) processing-order, Stage occupied by each Component in an EnigmaConfig, starting with 0 for the plugboard and ending with the reflector.

>>> stages $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"
[0,1,2,3,4,5]
components cfg == ((components cfg !!) <$> stages cfg)

The term 'stage' (lowercase) is also used here to encompass subsequent reverse processing order stages (see, for example, stageMappingList).

components :: EnigmaConfig -> [Name] Source

The Name of each Component in an EnigmaConfig, in processing order. Unchanged by step.

>>> components $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"
["AM.EU.ZL","II","III","V","\946","c"]

(Note that any Unicode charcters are stored by Haskell as their Unicode value: here "\946" == "β".)

positions :: EnigmaConfig -> [Position] Source

The Position of each Component in an EnigmaConfig, in machine processing order. May be changed by step.

>>> positions $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"
[1,25,2,17,23,1]

For plugboard and reflector, this will always be 1 since the former cannot rotate, and the latter does not (neither will be changed by step):

head (positions cfg) == 1
last (positions cfg) == 1

This determines the encoding performed by a component (see componentMapping).

rings :: EnigmaConfig -> [Int] Source

The location of ring letter A on the rotor for each Component in an EnigmaConfig, in machine processing order. Unchanged by step.

>>> rings $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"
[1,11,21,1,16,1]

For plugboard and reflector, this will always be 1 since the former lacks a ring, and for latter ring position is irrelevant (the letter ring is not visible, and has no effect on turnovers):

head (rings cfg) == 1
last (rings cfg) == 1

windows :: EnigmaConfig -> String Source

The letters at the window in an EnigmaConfig, in physical, conventional order. This is the (only) visible manifestations of configuration changes during operation.

>>> windows $ configEnigma "c-β-V-III-II" "LQVI" "AM.EU.ZL" "16.01.21.11"
"LQVI"

type Position = Int Source

The generalized rotational position of a Component. For rotors, this is denoted by number on the rotor (not letter ring) that is at the "window position". For other components the only meaningful position is 1 (see positions).

This (alone) determines the permutations applied to the component's Wiring to produce its current Mapping (see componentMapping).

type Stage = Int Source

The (zero-based) index of the processing stage occupied by a Component in an EngmaConfig. See stages.

step :: EnigmaConfig -> EnigmaConfig Source

Step the machine to a new EnigmaConfig by rotating the rightmost (first) rotor one position, and other rotors as determined by the positions of rotors in the machine. In the physical machine, a step occurs in response to each operator keypress, prior to processing that key's letter. (See enigmaEncoding.)

Stepping leaves the components, stages and rings of a configuration unchanged, changing only positions, which is manifest in changes of the letters at the windows:

>>> let cfg = configEnigma "c-γ-V-I-II" "LXZO" "UX.MO.KZ.AY.EF.PL" "03.17.04.01"
>>> putStr $ unlines $ show <$> take 5 (iterate step cfg)
c-γ-V-I-II LXZO UX.MO.KZ.AY.EF.PL 03.17.04.01
c-γ-V-I-II LXZP UX.MO.KZ.AY.EF.PL 03.17.04.01
c-γ-V-I-II LXZQ UX.MO.KZ.AY.EF.PL 03.17.04.01
c-γ-V-I-II LXZR UX.MO.KZ.AY.EF.PL 03.17.04.01
c-γ-V-I-II LXZS UX.MO.KZ.AY.EF.PL 03.17.04.01
>>> let cfg = configEnigma "c-γ-V-I-II" "LXZO" "UX.MO.KZ.AY.EF.PL" "03.17.04.01"
>>> take 5 $ map windows $ iterate step cfg
["LXZO","LXZP","LXZQ","LXZR","LXZS"]
>>> let cfg = configEnigma "c-γ-V-I-II" "LXZO" "UX.MO.KZ.AY.EF.PL" "03.17.04.01"
>>> take 5 $ map positions $ iterate step cfg
[[1,15,23,8,10,1],[1,16,23,8,10,1],[1,17,23,8,10,1],[1,18,23,8,10,1],[1,19,23,8,10,1]]

Mappings

The encodings established by the machine and its components.

type Mapping = String Source

The mapping used by a component (see wiring and componentMapping) or by the machine (see enigmaMapping) to perform a simple substitution encoding. This is expressed as a string of letters indicating the mapped-to letter for the letter at that position in the alphabet — i.e., as a permutation of the alphabet. For example, the mapping EKMFLGDQVZNTOWYHXUSPAIBRCJ encodes A to E, B to K, C to M, ... Y to C, and Z to J.

data Direction Source

The direction that a signal flows through a Component. During encoding of a character, the signal passes first through the wiring of each component, from right to left in the machine, in a forward (Fwd) direction, then through the reflector, and then, from left to right, through each component again, in reverse (Rev). This direction affects the encoding performed by the component (see componentMapping).

Constructors

Fwd 
Rev 

componentMapping :: Direction -> Component -> Position -> Mapping Source

The Mapping performed by a Component as a function of its Position and the Direction of the signal passing through it.

The base encoding of a Component, performed with rotor position 1 at the window, is set by its wiring.

componentMapping Fwd comp 1 == wiring comp

For all other positions, the encoding is a cyclic permutation this mapping's inputs (backward) and outputs (forward) by the rotational offset of the rotor away from the 1 position (though in an actual EmigmaConfig such positions occur only for rotors; see positions).

Note that because the wiring of reflectors generates mappings that consist entirely of paired exchanges of letters, reflectors (at any position) produce the same mapping in both directions (the same is true of the plugboard):

>>> let tst c n = componentMapping Fwd (component c) n == componentMapping Rev (component c) n
>>> and $ tst <$> ["A","B","C","b","c"] <*> [1..26]
True

stageMappingList :: EnigmaConfig -> [Mapping] Source

The list of Mappings for each stage of an EnigmaConfig: the encoding performed by the Component at that point in the progress through the machine.

These are arranged in processing order, beginning with the encoding performed by the plugboard, followed by the forward encoding performed by each rotor (see componentMapping), then the reflector, followed by the reverse encodings by each rotor, and finally by the plugboard again.

>>> putStr $ unlines $ stageMappingList (configEnigma "b-γ-V-VIII-II" "LFAQ" "UX.MO.KZ.AY.EF.PL" "03.17.04.11")
YBCDFEGHIJZPONMLQRSTXVWUAK
LORVFBQNGWKATHJSZPIYUDXEMC
BJYINTKWOARFEMVSGCUDPHZQLX
ILHXUBZQPNVGKMCRTEJFADOYSW
YDSKZPTNCHGQOMXAUWJFBRELVI
ENKQAUYWJICOPBLMDXZVFTHRGS
PUIBWTKJZSDXNHMFLVCGQYROAE
UFOVRTLCASMBNJWIHPYQEKZDXG
JARTMLQVDBGYNEIUXKPFSOHZCW
LFZVXEINSOKAYHBRGCPMUDJWTQ
YBCDFEGHIJZPONMLQRSTXVWUAK

Note that, because plugboard Mapping is established by paired exchanges of letters (see componentMapping),

head (stageMappingList cfg) == last (stageMappingList cfg)

As noted (see stages) the term 'stage' here encompasses reverse processing:

length (stageMappingList cfg) == 2 * length (stages cfg) - 1

A richer example of how this list is used, and how it can be interpreted, can be found in Crypto.Enigma.Display.

enigmaMappingList :: EnigmaConfig -> [Mapping] Source

The list of Mappings an EnigmaConfig has performed by each stage: the encoding performed by the EnigmaConfig up to that point in the progress through the machine.

>>> putStr $ unlines $ enigmaMappingList (configEnigma "b-γ-V-VIII-II" "LFAQ" "UX.MO.KZ.AY.EF.PL" "03.17.04.11")
YBCDFEGHIJZPONMLQRSTXVWUAK
MORVBFQNGWCSJHTAZPIYEDXULK
EVCHJTGMKZYUAWDBXSOLNIQPFR
UDHQNFZKVWSAIOXLYJCGMPTRBE
BKNUMPIGREJYCXLQVHSTOAFWDZ
NCBFPMJYXAIGKRODTWZVLEUHQS
HIUTFNSAOPZKDVMBGREYXWQJLC
CAEQTJYUWIGMVKNFLPRXDZHSBO
RJMXFBCSHDQNOGELYUKZTWVPAI
COYWEFZPNVGHBIXATUKQMJDRLS
CMAWFEKLNVGHBIUYTXZQOJDRPS

Since these may be thought of as cumulative encodings,

enigmaMapping cfg == last (enigmaMappingList cfg)

enigmaMapping :: EnigmaConfig -> Mapping Source

The Mapping performed by the Enigma machine.

>>> enigmaMapping (configEnigma "b-γ-V-VIII-II" "LFAQ" "UX.MO.KZ.AY.EF.PL" "03.17.04.11")
"CMAWFEKLNVGHBIUYTXZQOJDRPS"

A example of a richer display of this information can be found in Crypto.Enigma.Display.

Encoding

Encoding messages.

type Message = String Source

A valid keyboard entry into an Enigma machine: a string of uppercase characters.

enigmaEncoding :: EnigmaConfig -> Message -> String Source

Encode a Message using a given (starting) machine configuration, by stepping the configuration prior to processing each character of the message. This produces a new configuration (with new positions only) for encoding each character, which serves as the "starting" configuration for subsequent processing of the message.

>>> enigmaEncoding (configEnigma "b-γ-V-VIII-II" "LFAP" "UX.MO.KZ.AY.EF.PL" "03.17.04.11") "KRIEG"
"GOWNW"

The details of this encoding and its relationship to stepping from one configuration to another are illustrated in Crypto.Enigma.Display.

Note that because of the way the Enigma machine is designed, it is always the case that

enigmaEncoding cfg (enigmaEncoding cfg msg) == msg