Click here to Skip to main content
15,881,803 members
Articles / Programming Languages / C++

ENIGMA

Rate me:
Please Sign up or sign in to vote.
4.91/5 (49 votes)
20 Oct 2014CPOL15 min read 141.8K   285   53   27
ENIGMA

Introduction

NOTE: This article is adapted from Chapter 12 of my unpublished textbook Applied Algorithms and Data Structures.

Part of the presentation and the figures are from Bauer, F.L., Decrypted Secrets, pp. 106-108, Figures 49, 50, and Plates I, K.

The ENIGMA enciphering/deciphering machine was patented in Holland in the fall of 1919 by Hugo Koch, who sold the patent to the German engineer Arthur Scherbius, who in turn filed for a patent in 1926 (US patent number 1,584,660). There were commercial and military versions of this machine, which is famous for having been used by the German armed forces (Wehrmacht, Kriegsmarine, and Luftwaffe) and by the intelligence service (Abwehr) during World War II. The machine is also famous because the British cryptographers at Bletchley Park, most notably Alan Mathison Turing, were able to crack the ENIGMA code (with substantial initial help from Polish mathematicians who had been given an ENIGMA machine). It is beyond the scope of this article to recount the fascinating history of the codebreaking of ENIGMA. (The interested reader is referred to the book by Bauer, the chapter titled “The COLOSSUS” by B. Randell in Metropolis, N., et al. A History of Computing in the Twentieth Century. New York: Academic Press, 1980, and the following books (to name a few): Welchman, G. The Hut Six Story: Breaking the Enigma Codes. New York: McGraw-Hill, 1982. Garlinski, J. The Enigma War. New York: Charles Scribner’s Sons, 1979. Kahn, D. Seizing the Enigma. New York: Houghton-Mifflin, 1991. Sebag-Montefiore, H. Enigma: The Battle for the Code. New York: John Wiley and Sons, 2000. Budiansky, J. Battle of Wits: The Complete Story of Codebreaking in World War II. New York: The Free Press, 2000. For technical details on the breaking of the code, the master himself is the source: on the world-wide web, R. Erskine, P. Marks, and F. Weierud have edited and put available the document “Turing’s Treatise on Enigma” from the original documents in the National Archives.) This article deals with the development of a simulator of the ENIGMA machine.

The ENIGMA machine, shown in the figure below with its cover closed, is a rotor crypto-machine. It consists of a typewriter, a set of lamps labeled with letters in front of the typewriter keys, a set of rotors (partially hidden by the cover) in front of the lamps, and a plugboard on the front vertical wall of the box. The plugboard was a later addition to the machine to provide one more encryption step by associating pairs of letters.

enigmaA

To understand the operation of ENIGMA, one must be familiar with its fundamental components, namely the rotors. The following figure shows the views of the two sides of a typical ENIGMA rotor.

enigmaB

The bottom photograph shows the left side of a rotor when viewed in such a way that the letters on it are right-side up. The top photograph shows the right side of the rotor. On the surface of the right side, there is a circle of 26 spring contacts, while on the surface of the left side there is a circle of 26 plate contacts. The notches on the flange protruding from the rotor can be “pushed” manually to set the starting position of the rotor. The teeth shown in the top photograph allow turning the rotor one letter position in a manner to be explained shortly. The letters on the face of the rotor are on a movable ring, whose position relative to the starting position of the rotor can be changed by disengaging the ring’s catch (not shown), sliding the ring on the rotor, and engaging the catch to fix the ring.

Image 3

The following figure illustrates the encryption of one character by the ENIGMA machine.

enigma0

This diagram shows in simplified form a three-rotor ENIGMA. The rotors are labeled RL, RM, RN, the reflector is U, and the stator is connected to both the keyboard and the lamps. When the key E is pressed, electrical current flows from the stator, through the rotor wirings, to the reflector, and then back from the reflector, through the rotor wirings, to the lampboard. In this example, the letter E is translated into M. After a key is pressed, rotor RN turns one letter position, thus changing the wires connected to both the stator and rotor RM for the next letter to be encrypted. After 26 turns of rotor RM, a notch in it engages a stepping lever in rotor RM, thus making it turn one letter position. Similarly, after 26 turns of rotor RM, rotor RL will turn one position. (This is not entirely accurate: Due to a very particular “feature” of the ENIGMA machine, the middle rotor exhibits a “double-stepping” behavior. The reader interested in the details is referred to Hamer, D.H. “Enigma: Actions Involved in the ‘Double Stepping of the Middle Rotor” [paper submitted to Cryptologia, and available on the web at dhamer@eclipse.net]. This feature will be incorporated in the simulation.) The behavior is easily extended to four rotors. The machine also has a “window” that shows the letters at the current position of the rotors.

enigmaC gs

As shown in the figure above (from a Wehrmacht ENIGMA operating manual of 8 July 1937), the “programming” of a real ENIGMA machine involved the following steps:

  1. Deciding the order in which the rotors would go into the machine (Walzenlage: I III II, or rotor 1 in the left position, rotor 3 in the middle, and rotor 2 in the right).
  2. Deciding the initial position of the ring of letters on each rotor with respect to the position of the rotor’s notch (Ringstellung: 16 11 13, or letters Q L N).
  3. Deciding the initial position of the rotors (Grundstellung: 01 12 22, or letters A M W)

These steps apply to an ordinary, or unsteckered ENIGMA machine. The steckered version had a plugboard to associate pairs of letters. The plugboard would be the first transformation from the keyboard to the stator, and the last transformation from the stator to the lampboard. The ENIGMA manual in the figure above calls for plugboard connections (Otederverbindung) CO DI FR HU JW LS TX. So, if key F is pressed on the keyboard, it is first translated into R, and sent to the stator as if an R had been pressed. Similarly, if L comes out of the stator, lamp S lights up. All the letters (like A and Z) not associated on the plugboard are not affected by this transformation.

The ENIGMA code is symmetric: the same settings are used for encryption and decryption. These settings are the enciphering “key”, and they cannot be sent to the receiver over an insecure communications line (radio, telephone, and so on). The other entries in the ENIGMA manual have to do with groups of three letters (Renngruppen) encoding the settings of the machine, to be sent encrypted to the receiver so that the decryption could be achieved. (This ended up helping the British cryptographers do their job. German operators tended to be lazy in selecting the settings, and sometimes even re-used them!)

Technical Specification of the ENIGMA Rotors

The key to a successful simulation of the ENIGMA machine is to know the internal wirings of the rotors (or “wheels”), shown below. The rotors are labeled I through VIII, b, and g. The letters of the alphabet in order represent the input, and the wiring for each rotor indicates the transformation for each letter. For example, rotor I changes A to E, and Z to J.

Image 6

       Wheel          Wiring              Notch   Turnover
        No.   ABCDEFGHIJKLMNOPQRSTUVWXYZ

         I    EKMFLGDQVZNTOWYHXUSPAIBRCJ    Y        Q
        II    AJDKSIRUXBLHWTMCQGZNPYFVOE    M        E
       III    BDFHJLCPRTXVZNYEIWGAKMUSQO    D        V
        IV    ESOVPZJAYQUIRHXLNFTGKDCMWB    R        J
         V    VZBRGITYUPSDNHLXAWMJQOFECK    H        Z
        VI    JPGVOUMFYQBENHZRDKASXLICTW    H,U      Z,M
       VII    NZJHGRCXMYSWBOUFAIVLPEKQDT    H,U      Z,M
      VIII    FKQHTLXOCBJSPDZRAMEWNIUYGV    H,U      Z,M

         b    LEYJVCNIXWPBQMDRTAKZGFUHOS    M-4 only
         g    FSOKANUERHMBTIYCWLQPZXVGJD    M-4 only

Reflector
        B     YRUHQSLDPXNGOKMIEBFZCWVJAT
        C     FVPJIAOYEDRZXWGCTKUQSBNMHL
 ‘Thin’ B     ENKQAUYWJICOPBLMDXZVFTHRGS    M-4 only
 ‘Thin’ C     RDOBJNTKVEHMLFCWZAXGYIPSUQ    M-4 only
  1. Notch: location of notch on the index ring.
  2. Turnover: letter appearing in window when the notch is engaged with the stepping lever.
  3. Wheels Beta and Gamma have index rings but no notches.
  4. 'Thin' reflectors B and C are used only in the Model M-4 with Beta and Gamma wheels.
  5. All wiring measurements are made – per convention – with Ringstellung = ‘A’. Alphabet rotates clockwise when viewed ‘through’ the Eintrittwalze along the wheel axis.

With this brief introduction to the operation of the ENIGMA machine, we proceed to implement a simulator of it.

A Simulator of the ENIGMA Machine

The typewriter of the ENIGMA machine had only 26 letters (A through Z), and we could define the alphabet of the simulator as such. However, to be of any practical use, a cryptosystem must have an alphabet containing all the possible characters comprising the plain texts to be encrypted. Just consider using a system to encrypt the text files of the source code of computer programs. Clearly, a 26-letter alphabet will not be very useful, for to write code we use just about all the printable ASCII characters.

Bauer (op. cit., p. 37) has stated that “[t]he suppression of word spacing and of punctuation [is] one of the basic rules of classical professional cryptography,” and then goes on to discuss the approaches to dealing with decrypted texts such as “twothousandyearoldhorses,” which admits three interpretations after spaces and hyphens are restored by a human interpreter: “two-thousand-year-old horses,” “two thousand-year-old horses,” and “two-thousand year-old horses.” I agree that the suppression of spaces and punctuation works fine when humans do the interpretation. In the case of computers, however, it would be next to impossible for a compiler to interpret a computer program devoid of spaces and special separating characters.

The first task, then, is to extend the alphabets of the ENIGMA rotors to the full set of printable ASCII characters. The question is how to do it. I will illustrate what I did with rotor I, whose wiring is repeated (in lowercase) for convenience.

abcdefghijklmnopqrstuvwxyz
ekmflgdqvzntowyhxuspaibrcj

The same method applies to the rest of the rotors, and to the reflectors. Every rotor is wired to associate 26 pairs of characters: a character on the right side with a character on the left side. Take the ASCII characters other than a through z, line them beneath the basic alphabet as follows

abcdefghijklmnopqrstuvwxyz
0123456789.,:; ()[]'"-+/*&~`!@#$%^_={}|\<>?
 
4.:5,63)-&;' +*7/"](081[29?><\|}{=^_%$#@!`~

In this lineup, characters 0 through & are under one character from a to z, while characters ~ to ? are under no character (these will be dealt with shortly). Character 0 is under a, and rotor I maps a to e. Character e is above character 4 in the new lineup. Therefore, rotor I will map 0 to 4, as shown on the third line. Perform this procedure with all the characters up to &. Thus, & is under z in the lineup, rotor I maps z to j, and j is above 9. Therefore, rotor I will map & to 9.

Having the extended character mappings for all the rotors, the simulator has to cope with uppercase characters. Simply translate them to their lowercase counterparts. I thought about converting from uppercase to lowercase before encryption, and converting back to uppercase after encryption. However, the tell-tale presence of uppercase characters in a cipher text made me not to do so. Observe that this is the only loss of information presented by the system, and it is harmless. Any human can understand a properly punctuated text without uppercase letters, and any compiler can interpret a computer program in which a procedure originally named ApplyDES has been changed to applydes throughout!

With these considerations behind, we now turn to the definition of constants and variables to be used in the implementation of the simulator of ENIGMA.

C++
#define Nchars 69  // Total number of encipherable characters
#define Mchars 70  // Buffer size for strings containing Nchars
#define Nrotors 11 // Maximum number of rotors (1-based: 1-10)
#define Nrefls 5   // Total number of reflectors (1-based: 1-4)
#define Nsteps 11  // Maximum total number of encryption steps
                   // = 2*4 (rotors) + 2 (plugboard) + 1 (reflector)
 
char *ROTOR[ Nrotors ]  // Wirings of the rotors
   = {
     // input alphabet ("rotor" 0, not used)
     "abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?",
     // rotor 1
     "ekmflgdqvzntowyhxuspaibrcj4.:5,63)-&;' +*7/\"](081[29?><\\|}{=^_%$#@!`~",
     // rotor 2
     "ajdksiruxblhwtmcqgznpyfvoe093.]8[\"/1,7+':2)6&;(*5- 4?><\\|}{=^_%$#@!`~",
     // rotor 3
     "bdfhjlcprtxvznyeiwgakmusqo13579,2(['/-&;*48+60.:\"]) ?><\\|}{=^_%$#@!`~",
     // rotor 4
     "esovpzjayquirhxlnftgkdcmwb4] -(&90*)\"8[7/,;5'6.32:+1?><\\|}{=^_%$#@!`~",
     // rotor 5
     "vzbrgityupsdnhlxawmjqofeck-&1[68'*\"(]3;7,/0+:9) 542.?><\\|}{=^_%$#@!`~",
     // rotor 6
     "jpgvoumfyqbenhzrdkasxlictw9(6- \":5*)14;7&[3.0]/,82'+?><\\|}{=^_%$#@!`~",
     // rotor 7
     "nzjhgrcxmyswboufaivlpekqdt;&976[2/:*]+1 \"508-,(4.)3'?><\\|}{=^_%$#@!`~",
     // rotor 8
     "fkqhtlxocbjspdzramewniuygv5.)7',/ 219](3&[0:4+;8\"*6-?><\\|}{=^_%$#@!`~",
     // beta rotor
     "leyjvcnixwpbqmdrtakzgfuhos,4*9-2;8/+(1):3['0.&65\"7 ]?><\\|}{=^_%$#@!`~",
     // gamma rotor
     "fsokanuerhmbtiycwlqpzxvgjd5] .0;\"4[7:1'8*2+,)(&/-693?><\\|}{=^_%$#@!`~"
     };
 
// Position in which each rotor causes its left neighbor to turn
// (The beta and gamma rotors could only be used in the Naval-Enigma
//  fourth position, and did not have knock-on effect.  So, their
//  notch positions are meaningless)
 
char NOTCH[ Nrotors ]
     = { 'z', 'q', 'e', 'v', 'j', 'z', 'z', 'z', 'z', 'a', 'a' };
 
char *REFLECTOR[ Nrefls ]  // Reflectors
   = {
     // input alphabet ("REFLECTOR" 0, not used)
     "abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?",
     // reflector B, thick
     "yruhqsldpxngokmiebfzcwvjat*[\"7)],3(/;6 .:8415&2+-90'?<>\\|}{=_^%$#@`!~",
     // reflector C, thick
     "fvpjiaoyedrzxwgctkuqsbnmhl5-(980 *43[&/+62'.\")]1;:7,?<>\\|}{=_^%$#@`!~",
     // reflector B, dunn
     "enkqeuywjicopblmdxzvfthrgs4;.)0\"*+982 (1,:3/&-5'7[6]?<>\\|}{=_^%$#@`!~",
     // reflector C, dunn
     "rdobjntkvehmlfcwzrxgyipsuq[3 19;'.-47:,52+&0/6*8(]\")?<>\\|}{=_^%$#@`!~"
     };
 
char *PLUGBOARD  // Default wirings of the plugboard
     =
     "abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?";
 
char *alphabet  // Input alphabet
     =
     "abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?";
 
int mRotors,                // Number of rotors placed in the machine
                            // (1-based: 1-4)
     mSteps;                // Actual number of encryption steps
                            // = 2*mRotors + 2 (plugboard) + 1 (reflector)
 int RotPos[ Nrotors ];     // Rotor placed in each position
char window[ Nrotors ],     // Characters in window
     Iwindow[ Nrotors ];    // Initial values in 'window' (for resetting)
char *RotWiring[ Nrotors ]; // Rotor wirings
char RotNotch[ Nrotors ];   // Rotor switching positions
 int RotNumber[ Nrotors ];  // Which physical rotor (t,1-8,b,g)
char *reflector,            // Wiring of the reflector
     plugboard[ Mchars ];   // Wirings of the plugboard
 int ReflType;              // Reflector used
char step[ Nsteps ];        // Array to store encryption steps
 
// Files and variables for setup and reporting
 
#define Nline 255
 
FILE *inFp,             // input file pointer
     *outFp,            // output file pointer
     *logFp;            // log file pointer
char inLine[ Nline ],   // input line
     outLine[ Nline ];  // output line

Proceeding in a top-down, level-by-level fashion, start off with the main function and its auxiliary functions.

The main function calls function InitEnigma to initialize the machine to a default configuration, and then calls TryUserSetup to attempt to read a configuration from a file (if it exists).

The input text is assumed to reside in file plain. Function ProcessFile is called to encrypt the input text, with the result stored in file encrypt, and the log of the process in file elog.

After resetting the machine to its initial state, function ProcessFile is called again to work on file encrypt as the input file, generating a decrypted version of the original text in file decrypt, and the process log in file dlog.

C++
void main()
{
   InitEnigma();
   TryUserSetup();
 
   ProcessFile( "plain", "encrypt", "elog" );
   reset();
   ProcessFile( "encrypt", "decrypt", "dlog" );
}

In a default initialization, the machine is configured to have three rotors (it may have up to four).

The number of encryption steps is computed as a function of the number of rotors, and the default PLUGBOARD (identical to the machine alphabet) is copied to the working plugboard.

Each rotor position is assigned its corresponding rotor number, each notch has the default position, each rotor is assigned the default number, and the default window (ground) position for all the rotors is ‘a’.

The working reflector is set to point to the second REFLECTOR (number 1 in a 0-based indexing).

C++
void InitEnigma() // Default initialization
{
   int i;
 
   mRotors = 3;
   mSteps = (mRotors << 1) + 3;
   strcpy( plugboard, PLUGBOARD );
   for ( i = 0; i <= mRotors; ++i ) {
      RotWiring[ i ] = ROTOR[ i ];
      RotNotch[ i ] = NOTCH[ i ];
      RotNumber[ i ] = i;
      Iwindow[ i ] = window[ i ] = 'a';
   }
   reflector = REFLECTOR[ 1 ];
   ReflType = 1;
}

An attempt is made to open file esetup, containing a description of the machine’s configuration.

If the file exists and is opened sucessfully, the plugboard and the reflector wirings are read from the file by suitable functions.

C++
void TryUserSetup() 
// Attempt initialization from user file
{
   if ( (inFp = fopen( "esetup", "rt" ))
        !=
        NULL )
   {
      SetPlugboard();
      SetRotorsAndReflector();
      fclose( inFp );
   }
}

Function ProcessFile receives the names of the input file, the file to store the result of the encryption, and the log file. If the files can be opened for the intended read/ write operations, the rotor positions are set, the machine configuration is reported, the plain text is processed, and all the files are closed.

C++
void ProcessFile( char *inFname,
                  char *encFname,
                  char *logFname )
{
   if ( OpenFiles( inFname,
                   encFname,
                   logFname ) ) {
      SetRotorPositions();
      ReportMachine();
      ProcessPlainText();   
      CloseFiles();
   }
}

Function reset simply re-initializes the ring position of the rotors on the “window” of the machine.

C++
void reset()
{
   for ( int i = 1; i <= mRotors; ++i )
      window[ i ] = Iwindow[ i ];
}

An attempt is made to open the input file inFname for reading, the encryption file encFname for writing, and the log file logFname for writing. The return value indicates whether the files were opened successfully.

C++
int OpenFiles( char *inFname,
               char *encFname,
               char *logFname )
{
   inFp = fopen( inFname, "rt" );
   outFp = fopen( encFname, "wt" );
   logFp = fopen( logFname, "wt" );
   return    (inFp != NULL) && (outFp != NULL) && (logFp != NULL);
}

After processing the input file, all the files are closed to flush their buffers.

C++
void CloseFiles()
{
   fclose( InFp ); fclose( OutFp ); fclose( LogFp );
}

Function ReportMachine writes to the log file a description of the machine configuration. The first line shows the machine alphabet to facilitate reading the mappings of the plugboard, the rotors, and the reflector. The rotors are listed in a top-down fashion as they would have been “placed” from left-to-right inside the machine.

C++
void ReportMachine()
{
   fprintf( logFp, "Plugboard mappings:\n" );
   fprintf( logFp, "%s\n", ROTOR[ 0 ] );
   fprintf( logFp, "%s\n", plugboard );
 
   fprintf( logFp, "\nRotor wirings:\n" );
   fprintf( logFp, "position rotor ring setting notch sequence\n" );
   for ( int i = mRotors; i >= 1; --i )
      fprintf( logFp, "%8d %5d %12c %5c %s\n",
                      i, RotNumber[ i ], window[ i ],
                      RotNotch[ i ], RotWiring[ i ] );
   fprintf( logFp, "\nreflector %c %s\n", ReflType, reflector );
   fprintf( logFp, "\nrotors:\n" );
   ShowRotors();
}

Function ShowRotors can be used to write to the log file the wirings of the rotos on an encryption step. The characters “->” are used to mark the current position of each rotor under the window.

C++
void ShowRotors()
{
    int i, j, k;
   char *Rwiring;
 
   for ( i = mRotors; i >= 1; --i ) {
      fprintf( logFp, "%d: ", i );
      Rwiring = RotWiring[ i ];
      k = RotPos[ i ];
      for ( j = 0; j < k; ++j )
         fprintf( logFp, "%c", *Rwiring++ );
      fprintf( logFp, "->" );
      for ( j = k; j < Nchars; ++j )
         fprintf( logFp, "%c", *Rwiring++ );
      fprintf( logFp, "\n" );
   }
}

As mentioned before, file esetup is used to configure the machine with the plugboard mappings, the number of rotors, and the rotor positions (actual “placement” of each rotor in the machine). If the file does not exist, the default configuration defined by function InitEnigma is used. The line-by-line format of this file is illustrated with an example.

The functions that process this file are SetPlugboard and SetRotorsAndReflector. Function index simply returns the index of a character within array alphabet.

C++
// Initialization from file 'esetup' (step 1)
 
void SetPlugboard()  // make connections on the plug board
{
    int i, n, x;
   char p1, p2, ch;
 
   // Read a line containing pairs of letters corresponding to pairs of
   // plugs connected by a wire.  The length of the line is assumed to be even.
 
   fgets( inLine, Nline, inFp );
   inLine[ strlen( inLine ) - 1 ] = '\0';
   n = strlen( inLine );
 
   for ( i = 0; i < n; i += 2 ) {
      p1 = inLine[ i ];
      p2 = inLine[ i+1 ];
      x = index( p1 );
      if ( (ch = plugboard[ x ]) != p1 ) { // occupied? -> disconnect
         plugboard[ index( ch ) ] = ch;
         plugboard[ x ] = p1;
      }
      plugboard[ x ] = p2;                 // plug in
      x = index( p2 );
      if ( (ch = plugboard[ x ]) != p2) { // occupied? -> disconnect
         plugboard[ index( ch ) ] = ch;
         plugboard[ x ] = p1;
      }
      plugboard[ x ] = p1;                // plug in
   }
}
 
// Initialization from file 'esetup' (step 2)
 
void SetRotorsAndReflector()
{
    int i, n, rotor, rotPos;
   char ch, ringPos;
 
   // Read the actual number of rotors "mRotors", compute the total
   // number of encryption steps "mSteps", and read "mRotors" lines,
   // each one containing three characters denoting
   //
   //   (a) rotor ID (1-8,b,g)
   //   (b) rotor position (1-mRotors), and
   //   (c) ring character (position)
 
   fgets( inLine, Nline, inFp );
   mRotors = ChrToInt( inLine[ 0 ] );
   if ( mRotors > 4 )
      mRotors = 4;
   mSteps = (mRotors << 1) + 3;
   for ( i = 1; i <= mRotors; ++i ) {
      fgets( inLine, Nline, inFp );
      ch = inLine[ 0 ];
      if ( isdigit( (int)ch ) )
         rotor = ChrToInt( ch );
      else {
         ch = tolower( ch );
         rotor = ch == 'b' ? 9
                           : ch == 'g' ? 10 : 0;
      }
      rotPos = ChrToInt( inLine[ 1 ] );
      ringPos = inLine[ 2 ];
      Iwindow[ rotPos ] = window[ rotPos ] = ringPos;
      PlaceRotor( rotPos, rotor );
   }
 
   // Read a line containing the designation of the reflector (t,b,c,B,C)
 
   fgets( inLine, Nline, inFp );
   ch = inLine[ 0 ];
   switch ( ch ) {
      case 't': n = 0; break;      case 'b': n = 1; break;
      case 'c': n = 2; break;      case 'B': n = 3; break;
      case 'C': n = 4; break;       default: n = 0; break;
   }
   reflector = REFLECTOR[ n ];
   ReflType = i;
}

The auxiliary functions used by SetPlugboard and SetRotorsAndReflector are defined as follows.

Function to map c to its index in alphabet.

C++
int index( char c )
{
   // c in alphabet
 
   int i = 0;
 
   while ( (i < Nchars) && (c != alphabet[ i ]) )
      ++i;
   return i;
}

Function to convert an ASCII digit to its decimal equivalent.

C++
int ChrToInt( char c )
{
   // '0' <= c <= '9'
 
   return (int)( c - '0' );
}

Function to set the wiring of rotor r at the specified rotor position.

C++
void PlaceRotor( int position, int r ) 
// set wirings of a single rotor
{
   RotWiring[ position ] = ROTOR[ r ];
   RotNotch[ position ] = NOTCH[ r ];
   RotNumber[ position ] = r;
}

Once the rotors have been “placed” in the machine, function SetRotorPositions can be called by ProcessFile to initialize the rotor positions according to the settings under the window.

C++
void SetRotorPositions()
{
    int i, j, k;
   char *Rwiring, ch;
 
   for ( i = 1; i <= mRotors; ++i ) {
      j = RotNumber[ i ];
      ch = window[ j ];
      Rwiring = RotWiring[ j ];
      k = 0;
      while ( Rwiring[ k ] != ch )
         ++k;
      RotPos[ j ] = k;
   }
}

Before writing function ProcessPlainText and its auxiliary functions, which are the core of the simulator, I give the definition of an alternative way to implement the mathematical mod function. This function is needed to wrap around the character arrays representing rotor wirings.

C++
int mod( int n, int modulus )  // simple modulo function
{
   while ( n >= modulus )
      n -= modulus;
   while ( n < 0 )
      n += modulus;
   return n;
}

Function ProcessPlainText is the top-level driver of the simulator. Lines from the input file are processed one at a time. The newline character ('\n') at the end of each line is overwritten with a terminating NULL. Each character from a line is converted to lowercase (if necessary), and its encryption is stored in the output line.

After processing an input line, the output line is written to the encryption file. The log file is updated with information about the encryption process. If desired ShowRotors can be called to show how their positions change.

C++
void ProcessPlainText()
{
    int i, n;
   char c1, c2;
 
   fprintf( logFp, "\n\nEncryption\n" );
   while ( fgets( inLine, Nline, inFp ) != NULL ) {
 
      n = strlen( inLine ) - 1;
      inLine[ n ] = '\0';
 
      for ( i = 0; i < n; ++i ) {
         c1 = inLine[ i ];
         if ( isupper( (int)c1 ) )
            c1 = tolower( c1 );
 
         c2 = encrypt( c1 );
 
         // ShowRotors();
         ShowWindow();
         fprintf( logFp, " %c", c1 );
         ShowSteps();
         fprintf( logFp, "\n" );
         outLine[ i ] = c2;
      }
      fprintf( logFp, "\n" );
      outLine[ i ] = '\0';
      fprintf( outFp, "%s\n", outLine );
   }
}

Function to show the characters appearing under the window of the machine. The output is sent to the log file.

C++
void ShowWindow()
{
   int i;
 
   for ( i = mRotors; i >= 1; --i )
      fprintf( LogFp, "%c ", window[ i ] );
   fprintf( LogFp, "  " );
}

Function to show the encryption steps for a single input character. The output is sent to the log file.

C++
void ShowSteps()
{
   int i;
 
   for ( i = 0; i < mSteps; ++i )
     fprintf( LogFp, " -> %c", step[ i ] );
}

Function encrypt and its auxilary functions simulate the mechanical movements of the ENIGMA machine, including the “double stepping” of the middle (second) rotor on a 3-rotor (4-rotor) machine. Observe that the first step is to move the rotors as a consequence of the “typing” of character c on the hypothetical typewriter. The encryption sequence is: keyboard (argument c to encrypt) ® plugboard ® right-to-left path through the rotors ® reflector ® left-to-right path through the rotors ® plugboard ® lampboard (return value of encrypt). The encryption steps are saved in the global array step.

C++
    char encrypt( char c )
    {
       int i, r;
 
       turn();                                          // move rotors
       i = 0;                                           // pass through:
       step[ i++ ] = plugboard[ index( c ) ];           //    plugboard
       for ( r = 1; r <= mRotors; ++r )
          step[ i++ ] = RtoLpath( step[ i-1 ], r );     //    right-to-left path
       step[ i++ ] = reflector[ index( step[ i-1 ] ) ]; //    reflector
       for ( r = mRotors; r >= 1; --r )                 //    left-to-right path
          step[ i++ ] = LtoRpath( step[ i-1 ], r );
       step[ i ] = plugboard[ index( step[ i-1 ] ) ];   //    plugboard
 
       return step[ i ];
    } 
 
void turn()   // determine which rotors must turn
{
    int doit[ Nrotors ], n;
   char *r1 = RotWiring[ 1 ];, *r2 = RotWiring[ 2 ];, *r3;
 
   if ( mRotors > 3 )
      r3 = RotWiring[ 3 ];
 
    // calculate stepwidth for each rotor
    doit[ 1 ] = 1;
    for ( i = 2; i <= mRotors; ++i )
       doit[ i ] = 0;
    if ( (RotNotch[ 1 ] == r1[ RotPos[ 1 ] ])
         ||
         (RotNotch[ 2 ] == r2[ RotPos[ 2 ] ]) )  // double stepping
       doit[ 2 ] = 1;
    if ( RotNotch[ 2 ] == r2[ RotPos[ 2 ] ] )
       doit[ 3 ] = 1;
    if ( mRotors > 3 ) {
       if ( RotNotch[ 3 ] == r3[ RotPos[ 3 ] ] )
          doit[ 4 ] = 1;
    }
 
    // turn rotors "simultaneously"
    for ( n = 1; n <= mRotors; ++n )
       TurnRot( n, doit[ n ] );
}
 
void TurnRot( int n, int width )   // Turn rotor "n" "width" times
{                                  // wrapping around if necessary
   char *r;
 
   if ( width > 0 ) {
      RotPos[ n ] = mod( RotPos[ n ] + width, Nchars );
      r = RotWiring[ n ];
      window[ n ] = r[ RotPos[ n ] ];
   }
}

The right-to-left and left-to-right paths through the rotors are implemented by the following functions:

C++
// Transform on right-to-left path through rotors

char RtoLpath( char c, int r )  // transform character "c" with rotor "r"
{
    int n, offset, idx, ret;
   char *CurRotor;

   CurRotor = RotWiring[ r ];
   n = index( c );
   offset = index( CurRotor[ RotPos[ r ] ] );
   idx = mod( n + offset, Nchars );
   ret = mod( index( CurRotor[ idx ] ) - offset, Nchars );
   return alphabet[ ret ];
}

// Transform on left-to-right path through rotors

char LtoRpath( char c, int r ) // transform character "c" with rotor "r"
{
    int n, m, offset, idx, newchar;
   char *CurRotor;

   CurRotor = RotWiring[ r ];
   n = index( c );
   offset = index( CurRotor[ RotPos[ r ] ] );
   newchar = alphabet[ mod( n+offset, Nchars ) ];

   m = 0;
   while ( m < Nchars && CurRotor[ m ] != newchar )
      ++m;
   idx = mod( m - offset, Nchars );
   return alphabet[ idx ];
}

Function LtoRpath performs the inverse transformation of function RtoLpath.

Example of Encryption and Decryption with the ENIGMA Simulator

With the simulator in the default configuration (no esetup file), the following input file (plain)...

He [Sarek] was    delighted to discover   how very much like
him they    [computer people] were   ...  All they
cared about was the art of their
work, and doing            it right.  It was hard not
to admire such dedication and love of
computing for      its own   sake.
The programmers were the   first
Earth    people  he came   to   understand as being
really human.
Diane Duane.- "Spock's World."

...is encrypted into the following output file (encrypt):

`v;<7mw2h&$hrll&,&bjj_}~phg9=l@r@j|!yf:,s$*q0?k,)%dfsgw1b_di
<rq!f8&z@\tyib+8 o n+>'=\{0wik^)7ec&gxkaqtq~/o+^z;
1[# f5/|f%/l<6l-o)2i.y;6?__`ll|n
(::+u &aa?_,pl]#y@9{7/24@=^ce79=}id:21sa#3v&|_t9\pabv
m!!x 3az"i\zcq!4:)1(}iry,]-oz%v5dmnvo
\qyo^c5hvi=^j)cc&4&h60|sq8!]ds[~g:
vq?yth1%swo4@wh!8m|^w*b*xq-e63{w
9t87=|wg$yl&|'j[$<a1f"*`(0n9s)p_xtf~p*1!"?uk&?d='0y
- *bc#+s8!pv[
#8nsmm2+(@d2(l<p"(o@1)|$)|a@0l

After function reset re-initializes the machine for encipherment, the file encrypt is used as the input “plain” text and is deciphered into file decrypt (with log file dlog). The result of the decipherment is as follows:

he [sarek] was    delighted to discover   how very much like
him they    [computer people] were   ...  all they
cared about was the art of their
work, and doing            it right.  it was hard not
to admire such dedication and love of
computing for      its own   sake.
the programmers were the   first
earth    people  he came   to   understand as being
really human.
diane duane.- "spock's world." 
Observe that the only loss of information was the capital letters, all spaces and punctuation have been preserved!

The log files produced by the simulator are rather large, since they show all the encipherment steps for each character. The following is a printout of the beginning of file elog, produced in the encryption of the original plain file, for the processing of the characters “He [Sarek]”.

Plugboard mappings:

abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'"-+/*&~`!@#$%^_={}|\<>?
abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'"-+/*&~`!@#$%^_={}|\<>?

Rotor wirings:

position rotor ring setting notch sequence
       3     3            a     v bdfhjlcprtxvznyeiwgakmusqo13579,2(['/-&;*48+60.:"]) ?><\|}{=^_%$#@!`~
       2     2            a     e ajdksiruxblhwtmcqgznpyfvoe093.]8["/1,7+':2)6&;(*5- 4?><\|}{=^_%$#@!`~
       1     1            a     q ekmflgdqvzntowyhxuspaibrcj4.:5,63)-&;' +*7/"](081[29?><\|}{=^_%$#@!`~
 
reflector b yruhqsldpxngokmiebfzcwvjat*["7)],3(/;6 .:8415&2+-90'?<>\|}{=_^%$#@`!~

Rotors:

bdfhjlcprtxvznyeiwg->akmusqo13579,2(['/-&;*48+60.:"]) ?><\|}{=^_%$#@!`~
->ajdksiruxblhwtmcqgznpyfvoe093.]8["/1,7+':2)6&;(*5- 4?><\|}
{=^_%$#@!`~ ekmflgdqvzntowyhxusp->aibrcj4.:5,63)-&;' +*7/"](081[29?><\|}{=^_%$#@!`~

Encryption:

a a i    h -> h -> ? -> ~ -> ? -> ~ -> ? -> ~ -> ` -> `
a a b    e -> e -> f -> i -> r -> b -> a -> a -> v -> v
a a r      ->   -> " -> ( -> 4 -> ) -> * -> - -> ; -> ;
a a c    [ -> [ -> ; -> ' -> 0 -> * ->   -> * -> < -> <
a a j    s -> s -> 1 -> 9 -> ' -> & -> : ->   -> 7 -> 7
a a 4    a -> a -> h -> u -> k -> n -> n -> t -> m -> m
a a .    r -> r -> 5 -> 8 -> [ -> 1 -> 0 -> 0 -> w -> w
a a :    e -> e -> l -> h -> p -> i -> q -> q -> 2 -> 2
a a 5    k -> k -> c -> d -> h -> d -> b -> j -> h -> h
a a ,    ] -> ] -> " -> ( -> 4 -> ) -> * -> - -> & -> &

This concludes the article on the simulation of the ENIGMA machine.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMessage Closed Pin
18-Feb-22 23:11
Gustav Vogels18-Feb-22 23:11 
QuestionSome missing details Pin
KarstenK6-Dec-16 3:06
mveKarstenK6-Dec-16 3:06 
Questionesetup Pin
Member 1266436810-Aug-16 3:03
Member 1266436810-Aug-16 3:03 
AnswerRe: esetup Pin
JorgeLuisOrejel10-Aug-16 3:41
JorgeLuisOrejel10-Aug-16 3:41 
QuestionSource Code Pin
Member 126643682-Aug-16 6:41
Member 126643682-Aug-16 6:41 
AnswerRe: Source Code Pin
JorgeLuisOrejel4-Aug-16 6:51
JorgeLuisOrejel4-Aug-16 6:51 
GeneralRe: Source Code Pin
Rick York28-Mar-17 13:17
mveRick York28-Mar-17 13:17 
QuestionSource code? Pin
Willem Neethling6-Jul-16 19:09
Willem Neethling6-Jul-16 19:09 
GeneralModified to be a standard Enigma I/M3/M4 and not working quite like it should... Pin
Member 1258574016-Jun-16 5:43
Member 1258574016-Jun-16 5:43 
QuestionWhy don't I see a download link? Pin
nfatoys23-May-15 10:55
nfatoys23-May-15 10:55 
NewsArduino Enigma Machine Simulator Pin
ArduinoEnigma24-Nov-14 16:44
ArduinoEnigma24-Nov-14 16:44 
QuestionNice Article, But ... Pin
Member 1123821516-Nov-14 7:24
Member 1123821516-Nov-14 7:24 
GeneralMy vote of 5 Pin
KarstenK12-Nov-14 6:36
mveKarstenK12-Nov-14 6:36 
GeneralGood-onya Pin
PIEBALDconsult7-Nov-14 15:03
mvePIEBALDconsult7-Nov-14 15:03 
QuestionSteckerverbindung, not Otederverbindung Pin
Mark R.31-Oct-14 6:41
Mark R.31-Oct-14 6:41 
GeneralInteresting Article Pin
Paul R Benson30-Oct-14 22:26
Paul R Benson30-Oct-14 22:26 
QuestionInteresting article Pin
Mike Hankey27-Oct-14 12:34
mveMike Hankey27-Oct-14 12:34 
Questionenigma was not strong Pin
nissan123426-Oct-14 17:19
nissan123426-Oct-14 17:19 
GeneralMy vote of 1 Pin
nostagia23-Oct-14 3:55
nostagia23-Oct-14 3:55 
GeneralRe: My vote of 1 Pin
PIEBALDconsult7-Nov-14 14:46
mvePIEBALDconsult7-Nov-14 14:46 
QuestionBritish were able to crack the ENIGMA? What a joke... Pin
nostagia23-Oct-14 3:50
nostagia23-Oct-14 3:50 
AnswerRe: British were able to crack the ENIGMA? What a joke... Pin
Theo Buys4-Jan-15 23:46
Theo Buys4-Jan-15 23:46 
GeneralMy vote of 5 Pin
Roberto Saez Martin21-Oct-14 21:13
Roberto Saez Martin21-Oct-14 21:13 
GeneralMy vote of 5 Pin
kiberg21-Oct-14 20:41
professionalkiberg21-Oct-14 20:41 
Suggestionplease check better the history background of breaking Enigma next time Pin
elwoodkkk21-Oct-14 11:27
elwoodkkk21-Oct-14 11:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.