/***************************************************************************
 * Peggy Dagley                                                            *
 *                                                                         *
 * Lucifer Project                                                         *
 *                                                                         *
 * Usage:  program_name, '+'/'-', key, input_filename, output_filename     *
 *                                                                         *
 * This program is designed to simulate the Lucifer cryptosystem.          *
 * M = block of plaintext of length 2m, where m=8.  M[i] are one-half of   *
 * the M during the encryption/decryption process.  k = a 16 bit key       *
 * K1....K6 are the subkeys, that were originally randomly chosen.         *
 * d = 12 which determines the number of iterations and the the of         *
 * subkeys required (d-1), the number of iterations are 2/d - 1.           *
 * The input is a text message and the output is the encryption of the     *
 * input message, each character is put through 2/d - 1 iterations and     *
 * then the output is put in the output file specified.  If decryption     *
 * is requested then the entire process is performed the same way on the   *
 * encrypted file except the decryption runs the key schedule backwards    *
 * to result in plaintext.                                                 *
 ***************************************************************************/
#include <stdio.h>
#include <stdlib.h>

#define d 12       /*for 2/d-1  iterations*/
#define m 8        /*breaking the M into M0 and M1 each 8 bits long*/

int M[d];         /*each M[d] holds half of the plaintext or in this case
		    one ascii character (m) equivalent */     

int K[d-1];       /*subkeys*/

/*Function Prototypes*/
void read_file(FILE *, FILE *, char);
char f(int, int);
int encrypt(int, int, int);
void get_key_bits(char key[], int k[]);
void get_subkeys(int k[]);
void usage(void);

main(int argc, char *argv[])
{
  FILE *ifp;
  FILE *ofp;
  char key[3];
  char ende;
  int k[16];      /*key transformed into bits*/
  int i;

  if(argc != 5) 
    usage();
  
  if(strcmp(argv[1], "+") == 0)      /*decryption desired*/
     ende = 'E';
  else if(strcmp(argv[1], "-") == 0) /*encryption desired*/
    ende = 'D';
  else {
    printf("%s is an illegal char\n",argv[1]);
    usage();
  }
  strcpy(key, argv[2]);

  get_key_bits(key, k);         
  get_subkeys(k);
    
  if((ifp = fopen(argv[3], "r")) == NULL) {
    printf("\nUnable to open input file for reading\n");
    exit(1);
  }
  
  if((ofp = fopen(argv[4], "w")) == NULL) {
    printf("\nUnable to open output file for writing\n");
    exit(1);
  }
  read_file(ifp, ofp, ende);
  if(ende == 'E')
    printf("The encryption is complete\n");
  else
    printf("The decryption is complete\n");
}

/****************************************************************************
  Function:  read_file
  This function takes in the input file, output file and the procedure
  requested by the user.  It then reads the file, character by character
  and calls the function encrypt to do one iteration of the code.  This
  sequence is repeated d/2 - 1 additional times.  Then the last set of bits
  for M10 and M11 are then printed as the cipher, or in the event that it
  it a decrypt, the last set of bits will be M0 and M1. All characters
  are printed to the output file as requested.  To make the cipher more
  secure, one could increase the iterations, but this code is just to
  demonstrate the Lucifer system.
  **************************************************************************/
void read_file(FILE *ifp, FILE *ofp, char ende)
{
  int i;

  while(!feof(ifp)) {
    if(ende == 'E') {
      M[0] = fgetc(ifp);
      M[1] = fgetc(ifp);
      for(i = 2; i < d; i++) 
	M[i] = encrypt(M[i-2], M[i-1], i-1);
      if((M[d-1] != -1) && (M[d-2] != -1)) {
        fputc(M[d-2], ofp);
        fputc(M[d-1], ofp);
      }
      else if(M[d-1] == -1)
        fputc(M[d-2], ofp);
      else
	fputc('\0', ofp);
	
    }
    else {
      M[d-2] = fgetc(ifp);
      M[d-1] = fgetc(ifp);
      for(i = d-3; i >= 0; i--)
	M[i] = encrypt(M[i+2], M[i+1], i+1);
      if((M[0] != -1) && (M[1] != -1)) {
	fputc(M[0], ofp);
	fputc(M[1], ofp);
      }
      else if(M[1] == -1)
	fputc(M[0], ofp);
      else
	fputc('\0', ofp);
    }
  }
  return;
}

/****************************************************************************
  Funtion:  encrypt
  This function takes in M[i], M[i-2], M[i-1], and i and then defines
  new blocks by the formula Mi = Mi-2 Xor F(ki-1, M-1), where i runs
  from 2 to d.  This function calls the f function to perform the 
  mapping of bits.  Then ciphertext 'C' is returned.
****************************************************************************/
int encrypt(int c1, int c2, int i)
{
  int f_result;
  int C;

  f_result = f(K[i], c2);
  C = c1 ^ f_result;

  return C;
}


/***************************************************************************
 Function:  f
 This function is the heart of the algorithm.  It takes in a K and a M then
 maps the 16 combined bits into 8 bits in a non-linear fashion.  The
 function r result is extremely long, so for practical purposes, I have
 broken it up into 8 sub mappings.  res[0] is equal to the first binary
 digit in the new code....res[8] = the last binary digit in the new code.
 Then each res is multiplied with the ascii equivalent and then added
 to the next until a final result is determined.  This result is then
 returned to the calling function.
*****************************************************************************/
char f(int X, int Y)
{
  int y[8];
  int x[8];
  int bit, i, result;
  int res[8];

  for(i = 7; i >= 0; i--) {   /*putting the characters into bits*/
    bit = X >> 7 - i;
    x[i] = bit & 0x01;
    bit = Y >> 7 - i;
    y[i] = bit & 0x01;
  }
  res[0] = x[2] & x[6] | y[7] & y[1] | x[4] & y[7];
  res[1] = (x[0] | x[5]) & y[5] & y[3] & (x[1] | y[7]);
  res[2] = (x[4] | x[1]) | y[3] & y[7] | x[7] & y[1];
  res[3] = x[7] & x[3] & y[1] & y[0] | (x[4] | y[2]);
  res[4] = (y[0] | x[3]) & y[0] & y[6] & (x[3] | x[2]);
  res[5] = y[3] & x[6] | y[2] & y[5] | (x[1] & x[7]);
  res[6] = (y[5] | x[5]) & y[7] & y[3] & x[5] & x[4];
  res[7] = y[2] | x[0] | y[4] & y[6] & (x[6] | x[0]);

  result = (128 * res[0]) + (64 * res[1]) + (32 * res[2]) + (16 * res[3])\
    + (8 * res[4]) + (4 * res[5]) + (2 * res[6]) + res[7];

  return result;
}


/****************************************************************************
 Function: get_key_bits
 This function takes in the 2 character key and the empty k array and 
 converts the 2 character key into bits and stores them in the k array.
****************************************************************************/
void get_key_bits(char key[], int k[])
{
  int bit;
  int i, j;

  for(i = 7; i >= 0; i--) {
    bit = key[0] >> 7 - i;
    k[i] = bit & 0x01;
  }  
  for(i = 15; i >= 8; i--) {
    j = 15 - i + 8;
    bit = key[1] >> 15 - i;
    k[j] = bit & 0x01;
  }

  return;
}


/****************************************************************************
 Function:  get_subkeys
 This function takes in the k array of 16 ints and with those binary digits
 calculates what K1-K10 would be in ascii digits according to an originally
 configured random subkey format.  This is done to aid in the passing of
 args from function to function.
****************************************************************************/
void get_subkeys(int k[])
{

  K[1] = 128*k[3] + 64*k[8] + 32*k[15] + 16*k[9] + 8*k[14] + 4*k[4] \
    + 2*k[1] + k[11];
  K[2] = 128*k[1] + 64*k[4] + 32*k[9] + 16*k[15] + 8*k[10] + 4*k[3] \
    + 2*k[2] + k[0];
  K[3] = 128*k[10] + 64*k[1] + 32*k[0] + 16*k[4] + 8*k[12] + 4*k[14] \
    + 2*k[11] + k[5];
  K[4] = 128*k[6] + 64*k[2] + 32*k[11] + 16*k[1] + 8*k[12] + 4*k[3] \
    + 2*k[5] + k[15];
  K[5] = 128*k[12] + 64*k[7] + 32*k[3] + 16*k[10] + 8*k[6] + 4*k[8] \
    + 2*k[13] + k[2];
  K[6] = 128*k[13] + 64*k[2] + 32*k[8] + 16*k[3] + 8*k[0] + 4*k[10] \
    + 2*k[7] + k[14];
  K[7] = 128*k[9] + 64*k[13] + 32*k[4] + 16*k[8] + 8*k[15] + 4*k[7] \
    + 2*k[5] + k[2];
  K[8] = 128*k[14] + 64*k[2] + 32*k[5] + 16*k[11] + 8*k[1] + 4*k[9] \
    + 2*k[7] + k[3];
  K[9] = 128*k[0] + 64*k[15] + 32*k[8] + 16*k[5] + 8*k[6] + 4*k[12] \
    + 2*k[4] + k[5];
  K[10] = 128*k[6] + 64*k[14] + 32*k[3] + 16*k[10] + 8*k[13] + 4*k[9] \
    + 2*k[7] + k[1];
  
  return;
}

/***************************************************************************
 Function:  usage
 This function prints out to the screen the instructions on how to use the
 program, in the event that the user has not input the input line correctly.
 If this function is run then the entire program exits and the program
 must be re-run.
***************************************************************************/
void usage(void)
{
  printf("\nUSAGE:  program_name, '+'/'-', key, input_file, output_file\n");
  printf("\tprogram_name:  name of the program\n");
  printf("\t'+'\'-':  + for Encrypt \ - for Decrypt\n");
  printf("\tkey:  A two letter code\n");
  printf("\tinput_file:  Input filename to be encrypted/decrypted\n");
  printf("\toutput_file:  Output filename for resultant action\n");
  exit(1);
}
   














