/* #define rasterAndTime          uncomment this line if you use raster files */

/******************************************************************************/
/*                                                                            */
/* I N T E R N A T I O N A L  D A T A  E N C R Y P T I O N  A L G O R I T H M */
/*                                                                            */
/******************************************************************************/
/* Author:       Richard De Moliner (demoliner@isi.ethz.ch)                   */
/*               Signal and Information Processing Laboratory                 */
/*               Swiss Federal Institute of Technology                        */
/*               CH-8092 Zuerich, Switzerland                                 */
/* Last Edition: 23 April 1992                                                */
/* System:       SUN SPARCstation, SUN cc C-Compiler, SUN-OS 4.1.1            */
/******************************************************************************\
NAME
     idea - encrypt and decrypt using IDEA

SYNOPSIS
     idea [ -e | -d ] [ -r ] [ -ecb | -cbcN | -cfbN | -ofbN ]
          ( -k keyString | -K keyHexString )
          [ inputFile [ ouputFile ] ]

     idea [ -h | -H ] [ -tan | -abr ]
          [ -k keyString | -K keyHexString ]
          [ inputFile [ [ ouputFile ] hashvalFile ]

DESCRIPTION
     Idea reads inputFile and writes  the  en/decrypted  data  to
     outputFile or writes the hash value to hashvalFile.  If file
     name is not given in command line, idea uses standard  input
     or  output.   If  the  input file name is given as '-', idea
     reads from the standard input.

     IDEA (International Data Encryption Algorithm)  is  a  block
     cipher developed by Xuejia Lai and Prof. Dr. J. L. Massey at
     the Swiss Federal Institute of Technology.

OPTIONS
     -e   Encrypt data. (default)

     -d   Decrypt data.

     -r   InputFile is a raster-file.

     -k   Key is specified with keyString.

     -K   Key and initial values are specified with keyHexString.

     -h   Compute a 128 bit hash value from the input  data.  The
          hash  value is written to hashvalFile (or standard out-
          put).

     -H   Compute a 128 bit hash value from the input  data.  The
          input  is copied to outputFile (or standard output) and
          the hash value is written to hashvalFile  (or  standard
          error).

     Notation:
          N           = interleave factor (1 is default)
          z           = 128 bit key
          x[i]        = 64 bit plaintext block
          y[i]        = 64 bit ciphertext block
          x[1]..x[L]  = plaintext  (last  block  is  filled  with
                        zeros)
          x[L+1]      = length of plaintext in bits
          y[1]..y[L+1]= ciphertext
          IDEA(z, .)  = encryption function
          IIDEA(z, .) = decryption function
          x = IIDEA(z, IDEA(z ,x))

     Encryption / Decrypion Modes:

     -ecb electronic code book mode
          y[i] = IDEA(z, x[i])
          x[i] = IIDEA(z, y[i])

     -cbc cipher block chaining mode (cbc1 is default)
          y[i] = IDEA(z, x[i] ^ y[i-N])
          x[i] = IIDEA(z, y[i]) ^ y[i-N]

     -cfb ciphertext feedback mode
          y[i] = x[i] ^ IDEA(z, y[i-N])
          x[i] = y[i] ^ IDEA(z, y[i-N])

     -ofb output feedback mode
          h[i] = IDEA(z, h[i-N])
          y[i] = x[i] ^ h[i]
          x[i] = y[i] ^ h[i]

     Hash Functions:
          If no key is given, idea uses the all zero key.
          hash value = h[L+1] * 2**64 + g[L+1]
          h[0] = z / 2**64
          g[0] = z % 2**64

     -tan tandem DM-scheme (default)
          w = IDEA(g[i-1] * 2**64 + x[i], h[i-1])
          h[i] = h[i-1] ^ w
          g[i] = g[i-1] ^ IDEA(x[i] * 2**64 + w, g[i-1])

     -abr abreast DM-scheme
          h[i] = h[i-1] ^ IDEA(g[i-1] * 2**64 + x[i], h[i-1])
          g[i] = g[i-1] ^ IDEA(x[i] * 2**64 + h[i-1], ~ g[i-1])

     Key Formats:
          keyHexString = { '0'..'9' | 'a'..'f' | 'A'..'F' | ':' }
          keyHexString = z:y[1-N]:y[2-N]:y[3-N]..
          keyString = { '!'..'~' } = z

EXAMPLES
     To encrypt and decrypt a file in cipher block chaining  mode
     with an interleave factor of 8:

          idea -e -cbc8 -K 12345:67:89::ab:cDEf data data.cr
          idea -d -cbc8 -K 12345:67:89::ab:cDef data.cr data.ori
          data and data.ori are identical

     To compute the hash value with tandem DM-scheme:

          idea -h data

     To compute the hash value and encrypt the data in one step:

          idea -Hk "k e y" data | idea -K 123:9a::eF - data.cr

PATENT
     IDEA is registered as the international patent  WO  91/18459
     "Device for Converting a Digital Block and the Use thereof".
     For commercial use of IDEA, one should contact

     ASCOM TECH AG
     Freiburgstrasse 370
     CH-3018 Bern, Switzerland

AUTHOR
     Richard De Moliner (demoliner@isi.ethz.ch)
     Signal and Information Processing Laboratory
     Swiss Federal Institute of Technology
     CH-8092 Zurich, Switzerland

BUGS
     This program is at most as strong as  IDEA  itself.  So,  we
     urge  the user to use this program only after he has assured
     himself that IDEA is strong  enough  AND  he  has  read  the
     source  code  of  this  program and its libraries AND he has
     compiled the program himself with a troyan-free compiler. WE
     DO  NOT  GUARANTEE  THAT THIS PROGRAM IS A DECENT ENCRYPTION
     PROGRAM.

\******************************************************************************/

#ifdef rasterAndTime
#include <sys/times.h>
#include <pixrect/pixrect_hs.h>
#endif

#include <stdio.h>
#include <string.h>
#include <crypt.h>

#define TRUE                 1 /* boolean constant for true                   */
#define FALSE                0 /* boolean constant for false                  */
#define nofTestData     163840 /* number of blocks encrypted in time test     */

#define nomode               0 /* no mode is specified                        */
#define ecb                  1 /* electronic code book mode                   */
#define cbc                  2 /* cipher block chaining mode                  */
#define cfb                  3 /* ciphertext feedback mode                    */
#define ofb                  4 /* output feedback mode                        */
#define tan                  5 /* tandem DM-scheme for hashing                */
#define abr                  6 /* abreast DM-scheme for hashing               */
#define error               -1 /* error constant                              */
#define eol                 -2 /* end of line                                 */
#define colon               -3 /* character ':'                               */
#define maxInterleave     1024 /* maximal interleave factor + 1               */
#define nofChar ('~' - '!' + 1) /* number of different printable characters   */
#define maxBufLen  (dataSize * 1024) /* size of input and output buffer       */

UserKeyT(userKey);             /* user selected 128 bit key                   */
KeyT(key);                     /* expanded key with 832 bits                  */
DataT(state[maxInterleave]);   /* state informations for interleaving modes   */
DataT(hashLow);                /* lower 64 bits of hash value                 */
DataT(hashHigh);               /* higher 64 bits of hash value                */

u_int32 inputLen    = 0;       /* current number of bytes read from 'inFile'  */
int interleave      = 0;       /* current interleave factor                   */
int time            = 0;       /* time for interleaving modes                 */
int time_N          = 0;       /* time-interleave for interleaving modes      */
int mode            = nomode;  /* current mode                                */

int optEncrypt      = FALSE;   /* encrypt option 'e'                          */
int optDecrypt      = FALSE;   /* decrypt option 'd'                          */
int optHash         = FALSE;   /* hash option 'h'                             */
int optCopyHash     = FALSE;   /* copy and hash option 'H'                    */
int optKeyHexString = FALSE;   /* key as hex-string option 'K'                */
int optKeyString    = FALSE;   /* key as string option 'k'                    */
int optRas          = FALSE;   /* raster file option 'r'                      */
int optTime         = FALSE;   /* measure time option 'T'                     */

int inBufLen        = maxBufLen; /* current length of data in 'inBuf'         */
int inBufPos        = maxBufLen; /* current read position of 'inBuf'          */
int outBufLen       = 0;       /* current write position of 'outBuf'          */
u_int8 inBuf[maxBufLen];       /* buffer for file read                        */
u_int8 outBuf[maxBufLen];      /* buffer for file write                       */

FILE *inFile;                  /* file with input data (plain or ciphertext)  */
FILE *outFile;                 /* file for output data (plain or ciphertext)  */
FILE *hashFile;                /* 128 bit hash value is written to this file  */

#ifdef rasterAndTime
int pictureSize;               /* stuff related to raster files               */
Pixrect *picPtr;
colormap_t colormap;
u_int8 *picture;
#endif

/******************************************************************************/
/* initialize global variables                                                */

void Init()

{ int i, pos;

  for (i = 0; i < userKeyLen; i++) userKey[i] = 0;
  for (pos = 0; pos < maxInterleave; pos++) {
    for (i = 0; i < dataLen; i++) {
      state[pos][i] = 0;
    }
  }
} /* Init */

/******************************************************************************/
/*                          E R R O R - H A N D L I N G                       */
/******************************************************************************/
/* write usage error message and terminate program                            */

void UsageError(num)

int num;
{
#ifdef rasterAndTime
fprintf(stderr, "(%d)\n\
Usage:   idea [ -e | -d ] [ -r ] [ -ecb | -cbcN | -cfbN | -ofbN ]\n\
              ( -k keyString | -K keyHexString )                 \n\
              [ inputFile [ outputFile ] ]                       \n\
         idea [ -h | -H ] [ -tan | -abr ]                        \n\
              [ -k keyString | -K keyHexString ]                 \n\
              [ inputFile [ [ outputFile ] hashvalFile ] ]       \n\
         idea -T                                                 \n\
\nExample: idea -Hk \"k e y\" infile | idea -cbc8 -K 123:9a::eF - outfile\n\
\n", num);
#else
fprintf(stderr, "(%d)\n\
Usage:   idea [ -e | -d ] [ -ecb | -cbcN | -cfbN | -ofbN ]       \n\
              ( -k keyString | -K keyHexString )                 \n\
              [ inputFile [ outputFile ] ]                       \n\
         idea [ -h | -H ] [ -tan | -abr ]                        \n\
              [ -k keyString | -K keyHexString ]                 \n\
              [ inputFile [ [ outputFile ] hashvalFile ] ]       \n\
\nExample: idea -Hk \"k e y\" infile | idea -cbc8 -K 123:9a::eF - outfile\n\
\n", num);
#endif
  exit(-1);
} /* UsageError */

/******************************************************************************/
/* write error message and terminate program                                  */

void Error(num, str)

int num;
char *str;
{  fprintf(stderr, "error %d in idea: %s\n", num, str); exit(-1); } /* Error */

/******************************************************************************/
/* write system error message and terminate program                           */

void PError(str)

char *str;
{ perror(str); exit(-1); } /* PError */

/******************************************************************************/
/*                          D E C R Y P T I O N  /  E N C R Y P T I O N       */
/******************************************************************************/
/* read one data-block from 'inFile'                                          */

int GetData(data)

DataT(data);
{ register int i, len;
  register u_int16 h;
  register u_int8 *inPtr;

  if (inBufPos >= inBufLen) {
    if (inBufLen != maxBufLen) return 0;
    inBufLen = fread(inBuf, 1, maxBufLen, inFile);
    inBufPos = 0;
    if (inBufLen == 0) return 0;
    if (inBufLen % dataSize != 0)
      for (i = inBufLen; i % dataSize != 0; i++) inBuf[i] = 0;
  }
  inPtr = &inBuf[inBufPos];
  for (i = 0; i < dataLen; i++) {
    h = ((u_int16)(*inPtr++) & 0xFF) << 8;
    data[i] = h | ((u_int16)(*inPtr++) & 0xFF);
  }
  inBufPos += dataSize;
  if (inBufPos <= inBufLen) len = dataSize;
  else len = inBufLen + dataSize - inBufPos;
  inputLen += len;
  return len;
} /* GetData */

/******************************************************************************/
/* write one data-block to 'outFile'                                          */

void PutData(data, len)

DataT(data);
int len;
{ register int i;
  register u_int16 h;
  register u_int8 *outPtr;

  outPtr = &outBuf[outBufLen];
  for (i = 0; i < dataLen; i++) {
    h = data[i];
    *(outPtr++) = (h >> 8) & 0xFF;
    *(outPtr++) = h & 0xFF;
  }
  outBufLen += len;
  if (outBufLen >= maxBufLen) {
    fwrite(outBuf, 1, maxBufLen, outFile);
    outBufLen = 0;
  }
} /* PutData */

/******************************************************************************/
/* write last block to 'outFile' and close 'outFile'                          */

void CloseOutput()

{ if (outBufLen > 0) {
    fwrite(outBuf, 1, outBufLen, outFile);
    outBufLen = 0;
  }
  close(outFile);
} /* CloseOutput */

/******************************************************************************/
/* increment time and time_N                                                  */

void IncTime()
{ time = (time + 1) % maxInterleave;
  time_N = (time_N + 1) % maxInterleave;
} /* IncTime */

/******************************************************************************/
/* encrypt one data-block                                                     */

void EncryptData(data)

DataT(data);
{ int i;

  switch (mode) {
    case ecb:
      Idea(data, data, key);
      break;
    case cbc:
      for (i = dataLen - 1; i >= 0; i--) data[i] ^= state[time_N][i];
      Idea(data, data, key);
      for (i = dataLen - 1; i >= 0; i--) state[time][i] = data[i];
      IncTime();
      break;
    case cfb:
      Idea(state[time_N], state[time], key);
      for (i = dataLen - 1; i >= 0; i--) data[i] = state[time][i] ^= data[i];
      IncTime();
      break;
    case ofb:
      Idea(state[time_N], state[time], key);
      for (i = dataLen - 1; i >= 0; i--) data[i] ^= state[time][i];
      IncTime();
      break;
    default: break;
  }
} /* EncryptData */

/******************************************************************************/
/* decrypt one data-block                                                     */

void DecryptData(data)

DataT(data);
{ int i;

  switch (mode) {
    case ecb:
      Idea(data, data, key);
      break;
    case cbc:
      for (i = dataLen - 1; i >= 0; i--) state[time][i] = data[i];
      Idea(data, data, key);
      for (i = dataLen - 1; i >= 0; i--) data[i] ^= state[time_N][i];
      IncTime();
      break;
    case cfb:
      for (i = dataLen - 1; i >= 0; i--) state[time][i] = data[i];
      Idea(state[time_N], data, key);
      for (i = dataLen - 1; i >= 0; i--) data[i] ^= state[time][i];
      IncTime();
      break;
    case ofb:
      Idea(state[time_N], state[time], key);
      for (i = dataLen - 1; i >= 0; i--) data[i] ^= state[time][i];
      IncTime();
      break;
    default: break;
  }
} /* DecryptData */

/******************************************************************************/
/* hash one data-block                                                        */

void HashData(data)

DataT(data);
{ int i;
  UserKeyT(userKey);
  KeyT(key);
  DataT(w);

  for (i = dataLen - 1; i >= 0; i--) { 
    userKey[i] = hashLow[i];
    userKey[i + dataLen] = data[i]; 
  }
  ExpandUserKey(userKey, key);
  Idea(hashHigh, w, key);
  if (mode == abr) {
    for (i = dataLen - 1; i >= 0; i--) { 
      userKey[i] = data[i];
      userKey[i + dataLen] = hashHigh[i]; 
      hashHigh[i] ^= w[i];
      w[i] = ~ hashLow[i];
    }
  }
  else { /* mode == tan */
    for (i = dataLen - 1; i >= 0; i--) {
      hashHigh[i] ^= w[i];
      userKey[i] = data[i];
      userKey[i + dataLen] = w[i];
      w[i] = hashLow[i];
    }
  }
  ExpandUserKey(userKey, key);
  Idea(w, w, key);
  for (i = dataLen - 1; i >= 0; i--) hashLow[i] ^= w[i];
} /* HashData */

/******************************************************************************/
/* write value of a 16-bit unsigned integer in hex format to 'hashFile'       */

void WriteHex(val)

u_int16 val;
{ char str[8];
  int i;

  sprintf(str, "%4X", val);
  for (i = 0; i < 4; i++) if (str[i] == ' ') str[i] = '0';
  fprintf(hashFile, "%s", str);
} /* WriteHex */

/******************************************************************************/
/* write the hash value to 'hashFile'                                         */

void WriteHashValue()

{ int i;

  for (i = 0; i < dataLen; i++) WriteHex(hashHigh[i]);
  for (i = 0; i < dataLen; i++) WriteHex(hashLow[i]);
} /* WriteHashValue */

/******************************************************************************/
/* store integer 'value' in 'data'                                            */

void PlainLenToData(value, data)

u_int32 value;
DataT(data);
{ data[3] = (u_int16)((value << 3) & 0xFFFF);
  data[2] = (u_int16)((value >> 13) & 0xFFFF);
  data[1] = (u_int16)((value >> 29) & 0x0007);
  data[0] = 0;
} /* PlainLenToData */

/******************************************************************************/
/* extract integer 'value' from 'data'                                        */

void DataToPlainLen(data, value)

DataT(data);
u_int32 *value;
{ if ((data[0] != 0) || (data[1] > 7) || ((data[3] & 7) != 0))
    Error(1, "input is not a valid cryptogram");
  *value = ((u_int32)(data[3]) >> 3) & 0x1FFF |
           ((u_int32)(data[2]) << 13) |
           ((u_int32)(data[1]) << 29);
} /* DataToPlainLen */

/******************************************************************************/
/* encrypt / decrypt complete data-stream or compute hash value of data-stream*/

void CryptData()

{ int t, i;
  u_int32 len;
  DataT(dat[4]);
  DataT(data);

  if (optRas) {
#ifdef rasterAndTime
    if (optEncrypt)
      for (i = 0; i <= (pictureSize - dataSize); i += dataSize)
        EncryptData((u_int16 *)&picture[i]);
    else
      for (i = 0; i <= (pictureSize - dataSize); i += dataSize)
        DecryptData((u_int16 *)&picture[i]);
#endif
  }
  else if (optEncrypt) { /* encrypt data */
    while ((len = GetData(data)) == dataSize) {
      EncryptData(data); 
      PutData(data, dataSize); 
    }
    if (len > 0) { EncryptData(data); PutData(data, dataSize); }
    PlainLenToData(inputLen, data);
    EncryptData(data);
    PutData(data, dataSize);
    CloseOutput();
  }
  else if (optDecrypt) { /* decrypt data */
    if ((len = GetData(dat[0])) != dataSize) {
      if (len != 0) Error(2, "input is not a valid cryptogram");
      else Error(3, "there are no data to decrypt");
    }
    DecryptData(dat[0]);
    if ((len = GetData(dat[1])) != dataSize) {
      if (len != 0) Error(4, "input is not a valid cryptogram");
      DataToPlainLen(dat[0], &len);
      if (len != 0) Error(5, "input is not a valid cryptogram");
    }
    else {
      DecryptData(dat[1]);
      t = 2;
      while ((len = GetData(dat[t])) == dataSize) {
        DecryptData(dat[t]);
        PutData(dat[(t + 2) & 3], dataSize);
        t = (t + 1) & 3;
      }
      if (len != 0) Error(6, "input is not a valid cryptogram");
      DataToPlainLen(dat[(t + 3) & 3], &len);
      len += 2 * dataSize;
      if ((inputLen < len) && (len <= inputLen + dataSize)) {
        len -= inputLen;
        PutData(dat[(t + 2) & 3], len);
      }
      else Error(7, "input is not a valid cryptogram");
    }
    CloseOutput();
  }
  else { /* compute hash value */
    for (i = dataLen - 1; i >= 0; i--) {
      hashHigh[i] = userKey[i];
      hashLow[i] = userKey[i + dataLen];
    }
    if (optCopyHash) { 
      while ((len = GetData(data)) == dataSize) {
        HashData(data); 
        PutData(data, dataSize); 
      }
      if (len > 0) { HashData(data); PutData(data, len); }
      PlainLenToData(inputLen, data);
      HashData(data);
      CloseOutput();
    }
    else { /* optHash */
      while ((len = GetData(data)) == dataSize) HashData(data); 
      if (len > 0) HashData(data);
      PlainLenToData(inputLen, data);
      HashData(data);
    }
    WriteHashValue();
  }
} /* CryptData */

/******************************************************************************/
/* measure the time to encrypt 'nofTestData' data-blocks                      */

void TimeTest()

{ 
#ifdef rasterAndTime
  struct tms startTime, endTime;
  DataT(data);
  int i;
  float size, time;

  for (i = 0; i < dataLen; i++) data[i] = 0;
  if (times(&startTime) != 0) PError("(8) times");
  for (i = 0; i < nofTestData; i++) Idea(data, data, key);
  if (times(&endTime) != 0) PError("(9) times");
  size = ((float)nofTestData * 8.0 * (float)(dataSize)) / 1048576.0;
  time = (endTime.tms_utime - startTime.tms_utime) / 60.0;
  fprintf(stderr, "user time needed to encrypt %4.1f MBit of data was %4.1f\
 seconds (%6.3f MBit/sec)\n", size, time, size/time);
#endif
} /* TimeTest */

/******************************************************************************/
/*                          I N I T I A L I Z A T I O N                       */
/******************************************************************************/
/* set option to TRUE                                                         */

void SetOption(option)

int *option;
{ if (*option) UsageError(10);
  *option = TRUE;
} /* SetOption */

/******************************************************************************/
/* set encryption / decryption mode                                           */

void SetMode(newMode, str)

int newMode;
char **str;
{ if (mode != nomode) UsageError(11);
  mode = newMode;
  (*str)++; (*str)++;
  if ((newMode == cbc) || (newMode == cfb) || (newMode == ofb)) {
    if (('0' <= **str) && (**str <= '9')) {
      interleave = 0;
      do {
        interleave = 10 * interleave + (**str - '0');
        if (interleave >= maxInterleave)
          Error(12, "interleave factor is too large");
        (*str)++;
      } while (('0' <= **str) && (**str <= '9'));
      if (interleave == 0) Error(13, "interleave factor is zero");
    }
    else interleave = 1;
  }
} /* SetMode */

/******************************************************************************/
/* read options from string 'str'                                             */

void ReadOptions(str, readKeyString, readKeyHexString)

char *str;
int *readKeyString, *readKeyHexString;
{ char ch;

  str++;
  *readKeyString = *readKeyHexString = FALSE;
  while((ch = *(str++)) != 0) {
    switch (ch) {
      case 'a':
        if ((str[0] == 'b') && (str[1] == 'r')) SetMode(abr, &str);
        else UsageError(14);
        break;
      case 'c':
        if ((str[0] == 'b') && (str[1] == 'c')) SetMode(cbc, &str);
        else if ((str[0] == 'f') && (str[1] == 'b')) SetMode(cfb, &str);
        else UsageError(15);
        break;
      case 'd': SetOption(&optDecrypt); break;
      case 'e': 
        if ((str[0] == 'c') && (str[1] == 'b')) SetMode(ecb, &str);
        else SetOption(&optEncrypt);
        break;
      case 'h': SetOption(&optHash); break;
      case 'H': SetOption(&optCopyHash); break;
      case 'o':
        if ((str[0] == 'f') && (str[1] == 'b')) SetMode(ofb, &str);
        else UsageError(16);
        break;
      case 'k': SetOption(&optKeyString); *readKeyString = TRUE; break;
      case 'K': SetOption(&optKeyHexString); *readKeyHexString = TRUE; break;
      case 't':
        if ((str[0] == 'a') && (str[1] == 'n')) SetMode(tan, &str);
        else UsageError(17);
        break;
#ifdef rasterAndTime
      case 'r': SetOption(&optRas); break;
      case 'T': SetOption(&optTime); break;
#endif
      default: UsageError(18); break;
    }
  }
  if (*readKeyString && *readKeyHexString) UsageError(19);
} /* ReadOptions */

/******************************************************************************/
/* check if options are unique and set default options                        */

void AdjustOptions()

{ if (optTime && (optDecrypt || optEncrypt || optHash || optCopyHash ||
      optKeyString || optKeyHexString || optRas)) UsageError(20);
  if (optDecrypt && optEncrypt) UsageError(21);
  if (optHash && optCopyHash) UsageError(22);
  if (optKeyString && optKeyHexString) UsageError(23);
  if (optDecrypt || optEncrypt) {
    if (optHash || optCopyHash) UsageError(24);
    if ((! optKeyString) && (! optKeyHexString)) UsageError(25);
    if (mode == nomode) { mode = cbc; interleave = 1; }
    else if ((mode == tan) || (mode == abr)) UsageError(26);
  }
  else {
    if (optHash || optCopyHash) {
      if (mode == nomode) mode = tan;
      else if ((mode != tan) && (mode != abr)) UsageError(27);
    }
    else {
      if (mode == nomode) { mode = cbc; interleave = 1; }
      if ((mode == tan) || (mode == abr)) SetOption(&optHash);
      else SetOption(&optEncrypt);
    }
  }
  if (optRas && (optHash || optCopyHash)) UsageError(28);
  time = interleave;
  time_N = 0;
} /* AdjustOptions */

/******************************************************************************/
/* convert a hex-digit into an integer                                        */

int HexToInt(ch)

char ch;
{
  if (('0' <= ch) && (ch <= '9')) return ch - '0';
  else if (('a' <= ch)  && (ch <= 'f')) return 10 + (ch - 'a');
  else if (('A' <= ch) && (ch <= 'F')) return 10 + (ch - 'A');
  else if (ch == ':') return colon;
  else if (ch == 0) return eol;
  else return error;
} /* HexToInt */

/******************************************************************************/
/* convert a character into an integer                                        */

int32 CharToInt(ch)

char ch;
{
  if (('!' <= ch) && (ch <= '~')) return ch - '!';
  else if (ch == 0) return eol;
  else return error;
} /* CharToInt */

/******************************************************************************/
/* initializes key and initial values                                                */

void ReadKeyHexString(str)

char *str;
{ int i, pos;
  int32 h, val;

  while ((val = HexToInt(*(str++))) >= 0) {
    for (i = userKeyLen - 1; i >= 0; i--) {
      h = ((int32)(userKey[i]) >> 12) & 0xF;
      userKey[i] = ((int32)(userKey[i]) << 4) | val;
      val = h;
    }
    if (val != 0) Error(29, "key value is too large");
  }
  pos = 0;
  while ((val == colon) && (pos < maxInterleave)) {
    val = HexToInt(*(str++));
    while (val >= 0) {
      for (i = dataLen - 1; i >= 0; i--) {
        h = ((int32)(state[pos][i]) >> 12) & 0xF;
        state[pos][i] = ((int32)(state[pos][i]) << 4) | val;
        val = h;
      }
      if (val != 0) Error(30, "initial value is too large");
      val = HexToInt(*(str++));
    }
    pos++;
  }
  if (val == colon) Error(31, "too many initial values specified");
  if (val != eol) Error(32, "wrong character in initialization string");
} /* ReadKeyHexString */

/******************************************************************************/
/* initialize key and initial values                                          */

void ReadKeyString(str)

char *str;
{ int i;
  int32 h, val;

  while ((val = CharToInt(*(str++))) >= 0) {
    for (i = userKeyLen - 1; i >= 0; i--) {
      h = (int32)(userKey[i]) * nofChar + val;
      userKey[i] = h & 0xFFFF;
      val = h >> 16;
    }
  }
} /* ReadKeyString */

/******************************************************************************/
/* show value of a 16-bit unsigned integer in decimal and hex format          */

void ShowInt16(val)

u_int16 val;
{ fprintf(stderr, "%7u<%4x>", val, val);
} /* ShowInt16 */

/******************************************************************************/
/* show option name if option is set                                          */

void ShowOption(option, name)

int option;
char *name;
{ if (option) fprintf(stderr, ", %s", name);
} /* ShowOption */


/******************************************************************************/
/* show current state informations                                            */

void ShowState()

{ int i, j;

  fprintf(stderr, "Mode = {");
  switch (mode) {
    case ecb: fprintf(stderr, "ecb"); break;
    case cbc: fprintf(stderr, "cbc"); break;
    case cfb: fprintf(stderr, "cfb"); break;
    case ofb: fprintf(stderr, "ofb"); break;
    case tan: fprintf(stderr, "tan"); break;
    case abr: fprintf(stderr, "abr"); break;
    case nomode: fprintf(stderr, "nomode"); break;
    default: fprintf(stderr, "!!!wrong mode!!!"); break;
  }
  if (interleave > 0) fprintf(stderr, "%d", interleave);
  ShowOption(optEncrypt, "encrypt");
  ShowOption(optDecrypt, "decrypt");
  ShowOption(optHash, "hash");
  ShowOption(optCopyHash, "copy and hash");
  ShowOption(optKeyString, "key string");
  ShowOption(optKeyHexString, "key hex string");
  ShowOption(optRas, "raster file");
  ShowOption(optTime, "time test");
  fprintf(stderr, "}\n\nKey:\n");
  for (i = 0; i < keyLen; i++) {
    ShowInt16(key[i]);
    if ((i % 6) == 5) fprintf(stderr, "\n");
  }
  fprintf(stderr, "\n\nInitial values:");
  for (i = 0; i < interleave; i++) {
    fprintf(stderr, "\n  x[N -%2d] =", i + 1);
    for (j = 0; j < dataLen; j++) ShowInt16(state[i][j]);
  }
  fprintf(stderr, "\n");
} /* ShowState */

/******************************************************************************/
/* read picture from 'inFile' to memory                                       */

void ReadRasFile()

{
#ifdef rasterAndTime
  if ((picPtr = pr_load(inFile, &colormap)) == NULL)
    PError("(33) input is not a rasterfile");
  if (picPtr->pr_depth != 8) Error(34, "depth of rasterfile should bee 8");
  picture = (u_int8 *)mpr_d(picPtr)->md_image;
  pictureSize = picPtr->pr_height * picPtr->pr_width;
#endif
} /* ReadRasFile */

/******************************************************************************/
/* write picture from memory to 'outFile'                                     */

void WriteRasFile()

{
#ifdef rasterAndTime
  if (pr_dump(picPtr, outFile, &colormap, RT_STANDARD, FALSE) != NULL)
    PError("(34) writing rasterfile");
#endif
} /* WriteRasFile */

/******************************************************************************/
/*                          M A I N - P R O C E D U R E                       */
/******************************************************************************/
main(argc, argv)

int argc;
char *argv[];
{ int readKeyString, readKeyHexString;

  Init();
  argv++; argc--;
  while ((argc > 0) && (*(argv[0]) == '-') && (*(argv[0]+1) != '\0')) {
    ReadOptions(*argv++, &readKeyString, &readKeyHexString); argc--;
    if (readKeyString || readKeyHexString) {
      if (argc <= 0)  Error(36, "missing key on command line");
      else if (readKeyString) { ReadKeyString(*(argv++)); argc--; }
      else { ReadKeyHexString(*(argv++)); argc--; }
    }
  }
  AdjustOptions();
  if ((optTime && (argc > 0)) || (optCopyHash && (argc > 3)) || 
      (! optCopyHash && (argc > 2))) Error(37, "too many parameters");
  if (optTime) TimeTest();
  else {
    if ((argc > 1) && (strcmp(argv[0], argv[1]) == 0))
      Error(38, "source and destination are identical");
    if ((argc > 2) && (strcmp(argv[0], argv[2]) == 0))
      Error(39, "source and destination are identical");
    if ((argc > 2) && (strcmp(argv[1], argv[2]) == 0))
      Error(40, "destinations are identical");
    inFile = stdin;
    outFile = hashFile = stdout;
    if (argc > 0) {
      if (strcmp(*argv, "-") == 0) { argv++; argc--; }
      else {
        inFile = fopen(*argv++, "r"); argc--;
        if (inFile == 0) PError(*--argv);
      }
    }
    if (optCopyHash) {
      if (argc > 1) {
        outFile = fopen(*argv++, "w"); argc--;
        if (outFile == 0) PError(*--argv);
      }
      if (argc > 0) {
        hashFile = fopen(*argv++, "w"); argc--;
        if (hashFile == 0) PError(*--argv);
      }
      else hashFile = stderr;
    }
    else if (optHash) {
      if (argc > 0) {
        hashFile = fopen(*argv++, "w"); argc--;
        if (hashFile == 0) PError(*--argv);
      }
    }
    else {
      if (argc > 0) {
        outFile = fopen(*argv++, "w"); argc--;
        if (outFile == 0) PError(*--argv);
      }
    }
    if (argc > 0) Error(41, "too many parameters");
    ExpandUserKey(userKey, key);
    if (optDecrypt && ((mode == ecb) || (mode == cbc))) InvertIdeaKey(key, key);
/*  ShowState(); */
    if (optRas) ReadRasFile();
    CryptData();
    if (optRas) WriteRasFile();
  }
  exit(0);
}
