/*
* 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
*/