enigma.c
2411e28a26b5e4e8b6a18973dcb2c584efa7bcb1f64bfa8468dd37287200ad6b
/*
* engima simulation
*
* author: Henry Tieman
*
* references:
* "How Polish Mathematicians Deciphered the Enigma", Marian Rejewski,
* Annals of the History of Computing, Vol 3, no 3, July 1981, Pg 213 ff
* appendix by C. A. Devours.
*
* "Machine Cryptography and Modern Cryptanalysis", C.A. Deavours and L.
* Kurth, Artech House, Dedham, Mass 1985.
*/
#include <stdio.h>
#include <ctype.h>
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#define LINE_LEN 80
/*
* rotor data
* reference "Machine Cryptography and Modern Cryptanalysis" pg. 100
*
* note: rotor stepping is associated with each @PROGCODE = rotor instead
* of position or being constant.
*/
#define NUM_ROTORS 5
char ref_rotor[27] = "YRUHQSLDPXNGOKMIEBFZCWVJAT";
char rotor[NUM_ROTORS][27] = { /* pre defined rotors */
"EKMFLGDQVZNTOWYHXUSPAIBRCJ",
"AJDKSIRUXBLHWTMCQGZNPYFVOE",
"BDFHJLCPRTXVZNYEIWGAKMUSQO",
"ESOVPZJAYQUIRHXLNFTGKDCMWB",
"VZBRGITYUPSDNHLXAWMJQOFECK",
};
int step_data[NUM_ROTORS] = {
16, 4, 21, 9, 25 /* steps at: q, e, v, j, z */
};
/*
* engima key default settings
*/
int order[3] = { 0, 1, 2}; /* rotor order, user input is +1 */
char ring[8] = { /* ring settings */
'\0', 'A', 'A', 'A', /* default: AAA */
'\0', '\0', '\0', '\0' };
int n_plugs = 0; /* number of plugs */
char plugs[80] = ""; /* plug string */
int pos[3] = { 0, 0, 0 }; /* rotor positions */
/*
* simulation data and machine state data
*/
int data[8][26]; /* working array for machine */
int step[3]; /* steps corresponding to rotors */
int double_step; /* rotor 2 step twice */
/*
* encipher - C implementation of the engima cipher function
*/
int
encipher(int c)
{
int j; /* index for counting */
int idx; /* rotor index */
if (isalpha(c))
{
pos[0] = (pos[0] + 1) % 26; /* first, advances the rotors */
if (pos[0] == step[0])
pos[1] = (pos[1] + 1) % 26;
if (double_step)
{
pos[1] = (pos[1] + 1) % 26;
pos[2] = (pos[2] + 1) % 26;
double_step = FALSE;
}
if (pos[1] == step[1])
double_step = TRUE;
c -= 'A'; /* start to encipher */
if (n_plugs != 0)
c = data[0][c];
for (j=0;j<3;j++) /* do rotors forward */
{
idx = (c + pos[j]) % 26;
c = (c + data[j+1][idx]) % 26;
}
c = (data[4][c]) % 26; /* reflecting rotor */
for (j=0;j<3;j++) /* do rotors reverse */
{
idx = (c + pos[2-j]) % 26;
c = (c + data[j+5][idx]) % 26;
}
if (n_plugs != 0)
c = data[0][c];
c += 'A';
}
return(c);
}
/*
* encipher_file - open and encipher a file
*/
void
encipher_file(char *file_name)
{
FILE *fp; /* plaintext/ciphertext FILE pointer */
char line[LINE_LEN + 1]; /* input data line, inc. '\n' */
char *ret_val; /* value from fgets(), used for EOF check */
char c; /* character from data line */
int len; /* length of data line */
int idx; /* index/counter */
fp = fopen(file_name, "r");
ret_val = fgets(line, LINE_LEN, fp);
while(ret_val != NULL)
{
len = strlen(line);
for (idx=0;idx<len;idx++)
{
c = line[idx];
if (isalpha(c))
{
c = encipher((int)(toupper(c)));
line[idx] = c;
}
}
printf("%s", line);
ret_val = fgets(line, LINE_LEN, fp);
}
fclose(fp);
}
/*
* init_mach - set up data according to the input data
*/
void
init_mach( void )
{
int i, j; /* indexes */
int ds; /* used during ring settings */
int u, v; /* temps for plugboard input */
/* setup rotor data */
for (j=0;j<26;j++)
data[4][j] = ((int)ref_rotor[j]-'A'+26)%26;
for (i=1;i<4;i++)
{
step[i-1] = step_data[order[i-1]];
for (j=0;j<26;j++)
{
data[i][j] = ((int)(rotor[order[i-1]][j])-'A' + 26) % 26;
data[8-i][data[i][j]] = j;
}
}
/* setup ring settings */
ring[7] = ring[1];
ring[6] = ring[2];
ring[5] = ring[3];
for (i=1;i<8;i++)
if (i != 4)
{
ds = (int)(ring[i]) - 'A';
if (ds != 0)
{
for (j=0;j<26;j++)
data[0][j] = data[i][j];
for (j=0;j<26;j++)
data[i][j] = data[0][(26-ds+j)%26];
}
}
/* setup plug data */
if (n_plugs != 0)
{
j = 0;
for (i=0;i<26;i++)
data[0][i] = i;
for (i=0;i<n_plugs;i++)
{
while (!isalpha(plugs[j]))
{
j++;
if (plugs[j] == '\0')
break;
}
u = toupper(plugs[j++]) - 'A';
v = toupper(plugs[j++]) - 'A';
data[0][u] = v;
data[0][u] = u;
}
}
/* convert all moving rotor data to displacements */
for (i=1;i<8;i++)
{
if (i!=4)
for (j=0;j<26;j++)
data[i][j] = (data[i][j] - j + 26) % 26;
}
/* setup rotor starting positions */
double_step = FALSE; /* no previous rotor position */
/* input function has already done the rotor positions */
}
/*
* read_keyfile - a simple function to read in the key file
*/
void
read_keyfile( char *str )
{
FILE *kf; /* input key FILE pointer */
int num; /* dummy returned from fscanf() */
int idx; /* index/counter */
char a[3]; /* dummy for input */
kf = fopen(str, "r");
num = fscanf(kf, "%d %d %d\n", &order[0], &order[1], &order[2]);
num = fscanf(kf, "%c %c %c\n", &ring[1], &ring[2], &ring[3]);
num = fscanf(kf, "%d\n", &n_plugs);
if (n_plugs != 0)
num = fscanf(kf, "%[^\n]\n", plugs);
num = fscanf(kf, "%c %c %c\n", &a[0], &a[1], &a[2]);
for (idx = 0; idx < 3; idx++)
{
(order[idx])--;
ring[idx+1] = toupper(ring[idx+1]);
pos[idx] = toupper(a[idx]) - 'A';
}
fclose(kf);
}
/*
* usage - function to print out the correct usage of the program
*/
void
usage( char *str )
{
fprintf(stderr, "usage: %s [<keyfile>] <infile>\n\n", str);
fprintf(stderr, "\tkeyfile has the form:\n");
fprintf(stderr, "\t\tn n n\t\t- for rotor order, 1 <= n <= 5\n");
fprintf(stderr, "\t\tx x x\t\t- for ring settings, x alpha\n");
fprintf(stderr, "\t\tn\t\t- number of plugs, 0 <= n <= 13\n");
fprintf(stderr, "\t\txx xx xx ...\t- plug letter pairs, one pair"
" for each n\n");
fprintf(stderr, "\t\tx x x\t\t- initial rotor position, x alpha\n\n");
fprintf(stderr, "\toutput is stdout\n");
exit(0);
}
/*
* main - the main function, nothing special here
*/
void
main( int argc, char ** argv )
{
char *infile; /* plaintext/ciphertext file name ptr */
if ((argc < 2) || (argc > 3))
usage(argv[0]);
if (argc == 2)
{
infile = argv[1];
}
else
{
infile = argv[2];
read_keyfile(argv[1]);
}
init_mach();
encipher_file(infile);
}
/*
* end of engima simulation
*/