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.

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.

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.

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

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.

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:

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). 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). 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.

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

Notch: location of notch on the index ring. Turnover: letter appearing in window when the notch is engaged with the stepping lever. Wheels Beta and Gamma have index rings but no notches. 'Thin' reflectors B and C are used only in the Model M-4 with Beta and Gamma wheels. 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.

#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 char *ROTOR[ Nrotors ] = { " abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?" , " ekmflgdqvzntowyhxuspaibrcj4.:5,63)-&;' +*7/\"](081[29?><\\|}{=^_%$#@!`~" , " ajdksiruxblhwtmcqgznpyfvoe093.]8[\"/1,7+':2)6&;(*5- 4?><\\|}{=^_%$#@!`~" , " bdfhjlcprtxvznyeiwgakmusqo13579,2(['/-&;*48+60.:\"]) ?><\\|}{=^_%$#@!`~" , " esovpzjayquirhxlnftgkdcmwb4] -(&90*)\"8[7/,;5'6.32:+1?><\\|}{=^_%$#@!`~" , " vzbrgityupsdnhlxawmjqofeck-&1[68'*\"(]3;7,/0+:9) 542.?><\\|}{=^_%$#@!`~" , " jpgvoumfyqbenhzrdkasxlictw9(6- \":5*)14;7&[3.0]/,82'+?><\\|}{=^_%$#@!`~" , " nzjhgrcxmyswboufaivlpekqdt;&976[2/:*]+1 \"508-,(4.)3'?><\\|}{=^_%$#@!`~" , " fkqhtlxocbjspdzramewniuygv5.)7',/ 219](3&[0:4+;8\"*6-?><\\|}{=^_%$#@!`~" , " leyjvcnixwpbqmdrtakzgfuhos,4*9-2;8/+(1):3['0.&65\"7 ]?><\\|}{=^_%$#@!`~" , " fsokanuerhmbtiycwlqpzxvgjd5] .0;\"4[7:1'8*2+,)(&/-693?><\\|}{=^_%$#@!`~" }; char NOTCH[ Nrotors ] = { ' z' , ' q' , ' e' , ' v' , ' j' , ' z' , ' z' , ' z' , ' z' , ' a' , ' a' }; char *REFLECTOR[ Nrefls ] = { " abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?" , " yruhqsldpxngokmiebfzcwvjat*[\"7)],3(/;6 .:8415&2+-90'?<>\\|}{=_^%$#@`!~" , " fvpjiaoyedrzxwgctkuqsbnmhl5-(980 *43[&/+62'.\")]1;:7,?<>\\|}{=_^%$#@`!~" , " enkqeuywjicopblmdxzvfthrgs4;.)0\"*+982 (1,:3/&-5'7[6]?<>\\|}{=_^%$#@`!~" , " rdobjntkvehmlfcwzrxgyipsuq[3 19;'.-47:,52+&0/6*8(]\")?<>\\|}{=_^%$#@`!~" }; char *PLUGBOARD = " abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?" ; char *alphabet = " abcdefghijklmnopqrstuvwxyz0123456789.,:; ()[]'\"-+/*&~`!@#$%^_={}|\\<>?" ; int mRotors, mSteps; int RotPos[ Nrotors ]; char window[ Nrotors ], Iwindow[ Nrotors ]; char *RotWiring[ Nrotors ]; char RotNotch[ Nrotors ]; int RotNumber[ Nrotors ]; char *reflector, plugboard[ Mchars ]; int ReflType; char step[ Nsteps ]; #define Nline 255 FILE *inFp, *outFp, *logFp; char inLine[ Nline ], outLine[ Nline ];

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.

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).

void InitEnigma() { 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.

void TryUserSetup() { 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.

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.

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.

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.

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.

void ReportMachine() { fprintf( logFp, " Plugboard mappings:

" ); fprintf( logFp, " %s

" , ROTOR[ 0 ] ); fprintf( logFp, " %s

" , plugboard ); fprintf( logFp, "

Rotor wirings:

" ); fprintf( logFp, " position rotor ring setting notch sequence

" ); for ( int i = mRotors; i >= 1 ; --i ) fprintf( logFp, " %8d %5d %12c %5c %s

" , i, RotNumber[ i ], window[ i ], RotNotch[ i ], RotWiring[ i ] ); fprintf( logFp, "

reflector %c %s

" , ReflType, reflector ); fprintf( logFp, "

rotors:

" ); 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.

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, "

" ); } }

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.

void SetPlugboard() { int i, n, x; char p1, p2, ch; 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 ) { plugboard[ index( ch ) ] = ch; plugboard[ x ] = p1; } plugboard[ x ] = p2; x = index( p2 ); if ( (ch = plugboard[ x ]) != p2) { plugboard[ index( ch ) ] = ch; plugboard[ x ] = p1; } plugboard[ x ] = p1; } } void SetRotorsAndReflector() { int i, n, rotor, rotPos; char ch, ringPos; 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 ); } 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.

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

Function to convert an ASCII digit to its decimal equivalent.

int ChrToInt( char c ) { return ( int )( c - ' 0' ); }

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

void PlaceRotor( int position, int r ) { 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.

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.

int mod( int n, int modulus ) { 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 ('

') 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.

void ProcessPlainText() { int i, n; char c1, c2; fprintf( logFp, "



Encryption

" ); 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 ); ShowWindow(); fprintf( logFp, " %c" , c1 ); ShowSteps(); fprintf( logFp, "

" ); outLine[ i ] = c2; } fprintf( logFp, "

" ); outLine[ i ] = ' \0' ; fprintf( outFp, " %s

" , outLine ); } }

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

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.

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.

char encrypt( char c ) { int i, r; turn(); i = 0 ; step[ i++ ] = plugboard[ index( c ) ]; for ( r = 1 ; r <= mRotors; ++r ) step[ i++ ] = RtoLpath( step[ i- 1 ], r ); step[ i++ ] = reflector[ index( step[ i- 1 ] ) ]; for ( r = mRotors; r >= 1 ; --r ) step[ i++ ] = LtoRpath( step[ i- 1 ], r ); step[ i ] = plugboard[ index( step[ i- 1 ] ) ]; return step[ i ]; } void turn() { int doit[ Nrotors ], n; char *r1 = RotWiring[ 1 ];, *r2 = RotWiring[ 2 ];, *r3; if ( mRotors > 3 ) r3 = RotWiring[ 3 ]; doit[ 1 ] = 1 ; for ( i = 2 ; i <= mRotors; ++i ) doit[ i ] = 0 ; if ( (RotNotch[ 1 ] == r1[ RotPos[ 1 ] ]) || (RotNotch[ 2 ] == r2[ RotPos[ 2 ] ]) ) doit[ 2 ] = 1 ; if ( RotNotch[ 2 ] == r2[ RotPos[ 2 ] ] ) doit[ 3 ] = 1 ; if ( mRotors > 3 ) { if ( RotNotch[ 3 ] == r3[ RotPos[ 3 ] ] ) doit[ 4 ] = 1 ; } for ( n = 1 ; n <= mRotors; ++n ) TurnRot( n, doit[ n ] ); } void TurnRot( int n, int width ) { 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:

char RtoLpath( char c, int 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 ]; } char LtoRpath( char c, int 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.