

/*
 *This is the source code for the "Mini DES" project we discussed yesterday.
 *It contains two source files: minides.c and getpassw.c.  Getpassw.c
 *is a small module that prompts the user for a 4-letter password (the key) 
 *and is UNIX specific.  Everything else is in minides.c and is not system
 *specific.
 */
/*These modules were compiled on the SGI. (I think the Ultrix compiler chokes
 *on a few things like the multi-line strings I use for the usage message.)
 */
/**
*  minides.c - Implement a 'mini' version of DES.
*
*  Project 8 for CMSC 443, Spring 1993, UMBC.  Instructor: Dr. Stephens
*
*  Author:     J.I. 
*  Date:       May 1, 1993
*
*  This program allows a user to encrypt or decrypt a file using a
*  system similar to DES.  The command line must specify an option
*  (-d for decrypt,  -e for encrypt) and two file names, an input
*  file and an output file.  The input file is required, the output
*  will be written to stdout if the output file name is omitted.
*  The user is prompted to enter a 4-character key.
*
*  This version of DES operates on 32-bit data blocks using a 24-bit key.
*  It iterates over 8 keys and transforms the data stream through 4 S-boxes.
*  All permutations and transformations of the DES algorithm are mimicked.
**/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

int getpassw(char *buff, int len);  /* from getpassw.c */

#define TRUE 1
#define FALSE 0
#define BELL 0x07

/**
*  Constant parameters to the mini-DES routine.
**/
#define BITSPERBYTE  8   /* number of bits per byte */
#define USERKEYBYTES 4   /* number of bytes in the users key  */
#define USERKEYBITS 32   /* number of bits in the users key   */
#define BLOCKBYTES   4   /* number of bytes per data block     */
#define BLOCKSIZE   32   /* number of bits per data block      */
#define HALFBLOCK   16   /* number of bits in each half block  */
#define KEYSIZE     24   /* number of bits in the internal key */
#define HALFKEY     12   /* number of bits in each half key    */
#define NUMKEYS      8   /* number of keys or iterations       */
#define SUBSIZE      4   /* number of bits out of each S-box   */
#define NUMBOXES     4   /* number of S-boxes                  */
#define NUMROWS      4   /* number of rows in an S-box         */
#define NUMCOLS     16   /* number of columns in an S-box      */

char usage[] = 
"Usage:\n"
"         minides {-d|-e}  file_in  file_out\n"
"Options:\n"
"          -d    decrypt the input file and write to the output file\n"
"          -e    encrypt the input file and write to the output file\n"
"Example:\n\n"
"  (Encrypt the file named 'myplain.txt' and write it to 'mycipher.txt')\n"
"     minides -e myplain.txt mycipher.txt\n\n"
"  (Decrypt the file named 'mycipher.txt' and write it to 'myplain.txt')\n"
"     minides -d mycipher.txt myplain.txt\n\n";

/**
*  IP = initial permutation of 32-bit data block.
**/
char IP[ BLOCKSIZE ]=
      {25,17, 9,1,
       27,19,11,3,
       29,21,13,5,
       31,23,15,7,
       24,16,8, 0,
       26,18,10,2,
       28,20,12,4,
       30,22,14,6};

/**
*  PI = final permutation of 32-bit message block (inverse of IP)
**/
char PI[ BLOCKSIZE ]=
           {19,3,23,7,27,11,31,15,18,2,22,6,26,10,30,14,
           17,1,21,5,25,9,29,13,16,0,20,4,24,8,28,12};

/**
*  PC1 = permuted choice for the initial selection of 24 bits from the key.
*  The key consists of 4 ASCII characters chosen by the user.
*  The 24 bits are taken from bits {-x-xxxxx} of each byte.  (These are
*  virtually the only significant bits if the key consists of printable
*  ASCII characters.)
**/
char PC1[ KEYSIZE ] =
   {26,20,16,10,4,0,27,22,17,11,6,1,28,24,18,12,8,2,30,25,19,14,9,3};

/**
* PC2 = permuted choice 2.
* Used to permute the 24-bit key[i] for XORing with 24 bits of data
* in the F module.
**/
char PC2[ KEYSIZE ] =
  {2,3,5,6,12,11,8,9,1,7,4,10,21,13,14,16,17,23,22,24,20,19,18,15};

/**
* Left shift table for key generation.
**/
int L[ NUMKEYS ] = {1,1,2,2,1,2,2,1};

/**
*  E = expansion permutation for the F module.
*  Accepts a 16-bit data block and expands to 24 bits which are XORed
*  with the 24-bit key permutation.
**/
char E[ KEYSIZE ] =
  {15,0,1,2,1,2,3,4,5,6,5,6,7,8,9,10,9,10,11,12,13,14,15,0};

/**
*  P = permutation of 16 bits.  This is used to permute the combined
*  output of the four S boxes in the F module.
**/
char P[ HALFBLOCK ] = {7,9,5,13,12,8,4,0,3,11,6,15,14,2,10,1};

/**
*  S boxes.  Used in the F function to take 24 bits as four 6-bit strings
*  and yield four 4-bit strings.  (These are the first four S-boxes
*  from the DES standard.)
*
*  Indexing is [group][row][col] where
*    group is [0..3] representing which 6-bit group is being processed.
*    row   is [0..4] indexed by the outer two bits (0,5) of the group.
*    col   is [0..15] indexed by the inner four bits (1-4) of the group.
**/
char S[ NUMBOXES ][ NUMROWS ][ NUMCOLS ]=
{ { {14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7},    /* s1 = s[0] */
    {0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8},
    {4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0},
    {15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13}, },
  { {15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10},    /* s2 = s[1] */
    {3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5},
    {0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15},
    {13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9} },
  { {10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8},    /* s3 = s[2] */
    {13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1},
    {13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7},
    {1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12} },
  { {7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15},    /* s4 = s[3] */
    {13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9},
    {10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4},
    {3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14} }
};

/**
*  e r r o r
*
*  Print an error message and exit with an abnormal return code.
**/
void error(char *s)
{
  fprintf(stderr, "%s\n", s);
  exit(1);
}

/**
* f _ m o d u l e
*
* This module combines 16 bits of data with a 24-bit key and performs
* substitutions with the DES S-boxes.  The 16-bits of data are expanded
* to 24 bits with the E[] table, then XOR'ed with the key.  The result
* is divided into four 6-bit groups which are fed through the S-boxes as
* described in the DES standard (the outer two bits select the S-box row,
* and the inner four bits select the S-box column.)
*
*  Input:
*    char data[]  - 16-bit input block
*    char key[]   - 24-bit key
*  Output:
*    char bit[]   - 16-bit output block of transformed data
*  Returns:
*    nothing
**/
void f_module(char bit[ HALFBLOCK ],
              char data[ HALFBLOCK ],
              char key[ KEYSIZE ])
{
  char W[ KEYSIZE ];      /* working 24-bit array   */
  char t[ HALFBLOCK ];    /* temporary 16-bit array */
  char c;                 /* substitution character from S-box */
  int i,j,k,box,row,col;  /* indices */

  /**
  * Expand data from 16 to 24 bits using E table and XOR the
  * result with the key.
  **/
  for(i = 0; i < KEYSIZE; i++)
    W[i] = data[ E[i] ] ^ key[i];

  /**
  * Process the four 6-bit groups through four S-boxes.
  * The bits are in the array W[].  Bits 0-5 are used with S[0],
  * bits 6-11 with S[1], bits 12-17 with S[2], and bits 18-23 with S[3].
  * 'j' is used to index the first (low order) bit of a 6-bit group.
  * The outer two bits of each group are the row index for an S-box.
  * The inner four bits of each group are the column index for the S-box.
  *
  * The 4-bit substitution character is obtained from the S-box and saved in
  * array t[]. t[0-3] are from S[0], t[4-7] from S[1], etc.
  **/
  for(box = 0, k = 0; box < NUMBOXES; box++)
    {
      j = box * 6;                        /* start of 6-bit group in W */
      row = W[j] + 2*W[j+5];                          /* two outer bits */
      col = W[j+1] + 2*W[j+2] + 4*W[j+3] + 8*W[j+4];  /* four inner bits */
      c = S[box][row][col];          /* look up the substitution character */
      for(i = 0; i < SUBSIZE; i++)
        {
          t[k++] = c & 0x01;   /* copy the bits to working array t[] */
          c >>= 1;
        }
    }

  /* permute t[] through the P table to obtain the result in bit[] */
  for (i = 0; i < HALFBLOCK; i++)
    bit[i] = t[ P[i] ];
}

/**
*  m a k e k e y s
*
*  Generate eight 24-bit keys from the 4-byte key provided by the user.
*
*  Input:
*    char buff[]   - the 4-byte ASCII key provided by the user.
*  Output:
*    char key[][]  - an array of eight 24-bit keys
*  Returns:
*    nothing
**/
void makekeys(char key[ NUMKEYS ][ KEYSIZE ], char buff[ USERKEYBYTES ])
{
  static char W[ USERKEYBITS ];  /* 32-bit working array */
  static char bit[ KEYSIZE ];    /* 24-bit intermediate result array  */
  int i,j,k;            /* general indices */
  char c;               /* character from user's key */

  /* copy the user key to the 32-bit W (working) array */
  for(i = USERKEYBYTES - 1, k = 0; i >= 0; i--)
    {
     c = buff[i];
     for(j = 0; j < BITSPERBYTE; j++)
       {
         W[k++] = c & 0x01;
         c >>= 1;
       }
    }

  /* select and permute 24 bits using PC1 */
  for(i = 0; i < KEYSIZE; i++)
    bit[i] = W[ PC1[i] ];

  /* generate 8 keys */
  for(i = 0; i < NUMKEYS; i++)
    {
      j = L[i];   /* j = L[i] is the number of left shifts for i'th key */

      /* shift lower 12 bits */
      memcpy(W+j, bit,           HALFKEY-j);
      memcpy(W,   bit+HALFKEY-j, j);

      /* shift upper 12 bits */
      memcpy(W+HALFKEY+j, bit+HALFKEY,   HALFKEY-j);
      memcpy(W+HALFKEY,   bit+KEYSIZE-j, j);

      /* save 24-bit intermediate result */
      memcpy(bit, W, KEYSIZE);
      for(j = 0; j < KEYSIZE; j++)
        key[i][j] = bit[ PC2[j] ];    /* copy to i'th key */
    }
}

/**
*  m i n i d e s
*
*  Encrypt or decrypt a file using a mini-version of DES.  All the
*  steps of the grownup DES are used, but with fewer iterations and
*  smaller blocks.  A 24-bit key is used instead of a 56-bit key,
*  the data is blocked into 32-bit blocks instead of 64-bit blocks,
*  there are only 8 iterations through the F-module instead of 16,
*  and there are only 4 S-boxes instead of 8.
*
*  The encryption and decryption steps are virtually the same, except
*  that decryption uses the keys in reverse order, and the roles of
*  the left and right hand sides of the data block are reversed.
*
*  Input:
*    FILE *inf      - pointer to open input file
*    char *userkey  - pointer to 4-byte user key
*    int  encrypt   - TRUE if encrypting, FALSE if decrypting
*  Output:
*    FILE *outf     - the encrypted/decrypted file
*  Returns:
*    nothing
**/
void minides(FILE *inf, FILE *outf, char *userkey, int encrypt)
{
   static char key[NUMKEYS][KEYSIZE]; /* eight 24-bit keys */
   static char W[BLOCKSIZE];      /* 32-bit working area */
   static char bit[BLOCKSIZE];    /* 32-bit input array */
   static char LHS[HALFBLOCK];    /* 16-bit Left hand side */
   static char RHS[HALFBLOCK];    /* 16-bit Right hand side */
   static char buff[BLOCKBYTES];  /* 4-byte I/O buffer */
   int    nread;           /* number of bytes actually read */
   int i,j,k;              /* general indices */

   /* generate eight 24-bit keys key[0..8] */
   makekeys(key,userkey);

   /* Main loop.  Read input file in 4-byte blocks. */
   while (!feof(inf))
     {
       /* read next 32-bit input block        */
       nread = fread(buff, 1, BLOCKBYTES, inf);
       if (ferror(inf))
          error("Unable to read input file.");
       if (nread == 0)
          continue;

       /* pad block with spaces if necessary to make 4 bytes */
       for(;nread < BLOCKBYTES; nread++)
         buff[nread] = ' ';

       /* copy input to the 32-bit working array W[] */
       for(i = BLOCKBYTES-1, k = 0; i >= 0; i--)
         for(j = 0; j < BITSPERBYTE; j++)
           {
             W[k++] = buff[i] & 0x01;
             buff[i] >>= 1;
           }

       /* permute W[] through IP to obtain array bit[]  */
       for(i = 0; i < BLOCKSIZE; i++)
         bit[i] = W[ IP[i] ];

       /* perform 8 iterations i = [0..7] */
       for (i = 0; i < NUMKEYS; i++)
         {
          memcpy(RHS, &bit[0], HALFBLOCK);        /* RHS[i] = bits[0..15] */
          memcpy(LHS, &bit[HALFBLOCK], HALFBLOCK); /* LHS[i] = bits[16..31] */
          if (encrypt)
            {
               f_module(&bit[0], RHS, key[i]);  /* call f module */

               /* RHS[i+1] = F[i] XOR LHS[i] */
               for (j = 0; j < HALFBLOCK; j++)
                bit[j] ^= LHS[j];

               /* LHS[i+1] = RHS[i] */
               memcpy(&bit[HALFBLOCK], RHS, HALFBLOCK);
            }
          else
            {
               /**
               *  When decrypting, use the keys in reverse order, and
               *  call f module with the LHS instead of the RHS.
               **/
               f_module(&bit[HALFBLOCK], LHS, key[NUMKEYS-1-i]);

               /* LHS[i+1] = F[i] XOR RHS[i] */
               for (j = 0; j < HALFBLOCK; j++)
                bit[j+HALFBLOCK] ^= RHS[j];

               /* RHS[i+1] = LHS[i] */
               memcpy(&bit[0], LHS, HALFBLOCK);
            }
         }

       /* permute through PI to obtain 32-bit working array W[] */
       for(i = 0; i < BLOCKSIZE; i++)
         W[i] = bit[ PI[i] ];

       /* move the 32-bit W[] array to a 4-byte buffer */
       for(i = 0, k = BLOCKSIZE-1; i < BLOCKBYTES; i++)
         for(j = 0; j < BITSPERBYTE; j++)
           {
             buff[i] <<= 1;
             buff[i] |= W[k--];
           }

       /* write four bytes to the output file */
       fwrite(buff, 1, BLOCKBYTES, outf);

     /* repeat on next input block */
     }
}

/**
* m a i n
*
* Interpret and validate the command line arguments, open the file(s) and
* call the mini-DES function.
**/
int main(int argc, char *argv[])
{
  FILE *inf;     /* input stream */
  FILE *outf;    /* output stream */
  char key[5];   /* character string for the key */
  int cryptsw;   /* TRUE if encrypting, FALSE otherwise */

  if (argc < 3)
     error(usage);

  if (0 == strcmp(argv[1], "-e"))
    cryptsw = TRUE;
  else if (0 == strcmp(argv[1], "-d"))
    cryptsw = FALSE;
  else
    error(usage);

  if (NULL == (inf = fopen(argv[2], "rb")))
     error("Unable to open input file.");

  if (argc > 3)
    {
      if (NULL == (outf = fopen(argv[3], "wb")))
        error("Unable to open output file.");
    }
  else
    outf = stdout;

  /* ask the user for a 4-byte key */
  getpassw(key,4);

  /* perform the mini-DES routine */
  minides(inf, outf, key, cryptsw);

  fclose(inf);
  fclose(outf);

  return 0;
}

/*---------------------------------------------------------------------*/
/**
*  getpassw.c - password module for 'mini' version of DES.
*
*  Project 8 for CMSC 443, Spring 1993, UMBC.  Instructor: Dr. Stephens
*
*  Author:     Jim Irwin 
*  Date:       May 1, 1993
*
*  This module turns off echo on the control terminal, prompts the
*  user for a password, and resets the control terminal.
**/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <termio.h>

#define STDIN 0

void error(char *s);  /* see minides.c */

/**
* g e t p a s s w
*
* Get a password from the user without echoing on the terminal (UNIX)
*
* Input:
*    char * buff - pointer to buffer for password
*    int len     - maximum length of the password
* Output:
*    char * buff - buffer contains the password, padded with spaces to
*                  the proper length, if necessary.
* Returns:
*    Password length if successful
**/
int getpassw(char *buff, int len)
{
  char s[64];  /* message buffer */
  int nread;   /* number of characters read */
  struct termio tio;  /* termio struct for ioctl */
  int ttyfd;   /* file descriptor for control terminal */

  /* open the control terminal for I/O */
  if (-1 == (ttyfd = dup(STDIN)))
    {
      perror("dup(STDIN)");
      error("Can't open terminal for password.");
    }

  if (!isatty(ttyfd))
    {
      fprintf(stderr, "Warning! Standard input is not TTY.\n");
      fprintf(stderr, "The password may echo to the screen.\n");
    }
  else
    {
      /* obtain the current device driver state */
      if (-1 == ioctl(ttyfd, TCGETA, &tio))
	{
	  perror("ioctl(TCGETA)");
	  error("Can't obtain terminal state.");
	}
      
      /* turn off echoing */
      tio.c_lflag &= ~ECHO;
      
      if (-1 == ioctl(ttyfd, TCSETA, &tio))
	{
	  perror("ioctl(TCSETA)");
	  error("Can't set terminal state.");
	}
    }

  fprintf(stderr, "Please enter a %d-character password: ", len);

  if (-1 == (nread = read(ttyfd, buff, len)))
     error("Read error: tty");

  fprintf(stderr, "\n");

  /* pad the password with spaces if necessary */
  for(;nread < len; nread++)
    buff[nread] = ' ';
  buff[nread] = 0;

  if (isatty(ttyfd))
    {
      /* reset ECHO to ON */
      tio.c_lflag |= ECHO;
      
      if (-1 == ioctl(ttyfd, TCSETA, &tio))
	{
	  perror("ioctl(TCSETA)");
	  error("Can't set terminal state.");
	}
    }

  close(ttyfd);

  return nread;
}


