 * enigma 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 Crypyanalysis", C.A. Deavours and L.
 *     Kurh, 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 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 */
};

/*
 * enigma 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 coresponding to rotors */
int  double_step;                 /* rotor 2 step twice */

/*
 *  encipher - c implementation of the enigma 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 durring 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][v] = 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 to stdout\n");
  exit(0);
}

/*
 * main - the main function, nothing special here
 */

void
main( int argc, char ** argv)
{
  char *infile;                   /* plaintext/cphertext 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 enigma simulation
 */
