/*IDEA.C   v2.2
	c source code for IDEA block cipher. IDEA (International Data
	Encryption Algorithm), formerly known as IPES (Improved Proposed
	Encryption Standard). Algorithm developed by Xuejia Lai and James L.
	Massey, of ETH Zurich. This implementation modified and derived from
	original C code developed by Xuejia Lai. Zero-based indexing added,
	names changed from IPES to IDEA. CFB functions added. Random Number
	routines added. Optimized for speed 21 Oct 92 by Colin Plumb
	<colin@nsq.gts.org>  This code assumes that each pair of 8-bit bytes
	comprising a 16-bit word in the key and in the cipher block are
	externally represented with the Most Significant Byte (MSB) first,
	regardless of internal native byte order of the target cpu.
	modified for use with PC files by Colin Maroney 4/1/94*/

/*   USAGE:     pass a key made up of 8 16-bit numbers (word16) in an array
		("word16 key[8];"), an input FILE * and an output temporary
		FILE * to either encode_file() or decode_file().
		where the key comes from is up to you.
		then call swap_files_and_clean_up() with the original file's
		name as the argument, to replace the original file
		with the encoded data (stored in the temporary file).

		you can remname the tempfile to be used in idea.h
		noisy is an integer which tells encrypting/decrypting
		functions to echo a "." every 256 writes, so the user can
		see that something is happening. set it to 0 for quiet
		running.

		please note that for really good security the original file
		is overwritten before being erased if you use the w switch.
		otherwise it outputs a file "<filename>.enc"

		the main() used here as illustration reads the filename
		from the command line arguments, as well as a command
		"e" or "d" to tell it whether to encrypt or
		decrypt, and a key.  the older versions had an interface
		for when a command line was not use.  lack of editing 
		features made this buggy, so i axed it. */

#include "idea.h"
#include "ideap.h"

uint16 inv(uint16 x)
{
   uint16 t0,t1;
   uint16 q,y;
   if (x<=1)
      return x;
   t1=(uint16)(0x10001l/x);
   y=(uint16)(0x10001l%x);
   if (y==1)
	return low16(1-t1);
   t0=1;
   do
   {
      q=x/y;
      x=x%y;
      t0+=q*t1;
      if (x==1)
	return t0;
      q=y/x;
      y=y%x;
      t1+=q*t0;
   } while (y!=1);
   return low16(1-t1);
}

void en_key_idea(word16 *userkey, word16 *Z)
{
   int i,j;
   /* shifts */
   for (j=0;j<8;j++)
      Z[j]=*userkey++;
   for (i=0;j<KEYLEN;j++)
   {
      i++;
      Z[i+7]=((Z[i&7] << 9) | (Z[i+1 & 7] >> 7));
      Z+=i&8;
      i&=7;
   }
}

void de_key_idea(IDEAkey Z,IDEAkey DK)
{
   int j;
   uint16 t1,t2,t3;
   IDEAkey T;
   word16 *p=T+KEYLEN;
   t1=inv(*Z++);
   t2=-*Z++;
   t3=-*Z++;
   *--p=inv(*Z++);
   *--p=t3;
   *--p=t2;
   *--p=t1;
   for (j=1;j<ROUNDS;j++)
   {
      t1=*Z++;
      *--p=*Z++;
      *--p=t1;
      t1=inv(*Z++);
      t2=-*Z++;
      t3=-*Z++;
      *--p=inv(*Z++);
      *--p=t2;
      *--p=t3;
      *--p=t1;
   }
   t1=*Z++;
   *--p=*Z++;
   *--p=t1;
   t1=inv(*Z++);
   t2=-*Z++;
   t3=-*Z++;
   *--p=inv(*Z++);
   *--p=t3;
   *--p=t2;
   *--p=t1;
   /*copy and destroy temp copy*/
   for(j=0,p=T;j<KEYLEN;j++)
   {
      *DK++=*p;
      *p++=0;
   }
}


uint16 mul(uint16 a, uint16 b)
{
   word32 p;

   if (a)
   {
      if (b)
      {
	 p=(word32)a*b;
	 b=(uint16)(low16(p));
	 a=(uint16)(p>>16);
	 return b-a+(b<a);
      }
      else
      {
	 return 1-a;
      }
   }
   else
      return 1-b;
}

void cipher_idea(word16 in[4],word16 out[4],register IDEAkey Z)
{
   register uint16 x1,x2,x3,x4,t1,t2;
   int r=ROUNDS;
   x1=*in++; x2=*in++;
   x3=*in++; x4=*in;
   do
   {
      MUL(x1,*Z++);
      x2+=*Z++;
      x3+=*Z++;
      MUL(x4,*Z++);
      t2=x1^x3;
      MUL(t2,*Z++);
      t1=t2+(x2^x4);
      MUL(t1,*Z++);
      t2=t1+t2;
      x1^=t1;
      x4^=t2;
      t2^=x2;
      x2=x3^t1;
      x3=t2;
   } while (--r);
   MUL(x1,*Z++);
   *out++=x1;
   *out++=(x3+*Z++);
   *out++=(x2+*Z++);
   MUL(x4,*Z);
   *out=x4;
}


//
// write encrypted string of length length from strin to strout
// sizeof strout must be multiple of 8!!!
//
void cipher_string( const char *instr, char *outstr,word16 *key,long length)
{
   word16 input[4],output[4];
   IDEAkey Z;
   int i,j;
   unsigned char *pinstr=(unsigned char *)instr;
   unsigned char *poutstr=(unsigned char *)outstr;

   en_key_idea(key,Z);
   for(i=0;i<length;i+=8)
       {
       input[0]=input[1]=input[2]=input[3]=0;
       for(j=0;j<8 && i+j<length;j+=2)   
           {
           // fill two chars in one word
           input[j>>1]=(*pinstr++)<<8;
           if(i+j+1<length)
               input[j>>1]|=(*pinstr++);
           }
       cipher_idea(input,output,Z);
       for(j=0;j<8;j+=2)
           {
           (*poutstr++)=(output[j>>1] & 0xFF00)>>8;
           (*poutstr++)=output[j>>1] & 0xFF;
           }
       }
}

//
// write decrypted string of length length from strin to strout
// sizeof strin must be multiple of 8
//
void decipher_string( const char *instr, char *outstr,word16 *key,long length)
{
   word16 input[4],output[4];
   int i,j;
   IDEAkey Z,DK;
   unsigned char *pinstr=(unsigned char *)instr;
   unsigned char *poutstr=(unsigned char *)outstr;

   en_key_idea(key,Z);
   de_key_idea(Z,DK);
  for(i=0;i<length;i+=8)
       {
       input[0]=input[1]=input[2]=input[3]=0;
       for(j=0;j<8;j+=2)   
           {
           // fill two chars in one word
           input[j>>1]=(*pinstr++)<<8;
           input[j>>1]|=(*pinstr++);
           }
       cipher_idea(input,output,DK);
       for(j=0;j<8 && i+j<length;j+=2)
           {
           (*poutstr++)=(output[j>>1] & 0xFF00)>>8;
           if(i+j+1<length)
              (*poutstr++)=output[j>>1] & 0xFF;

           }
       }
}

/*-----------------------------------------------*/
//
// make a key (word16 key[8]) from first 8 chars of password
//
//
// make a key (word16 key[8]) from first 8 (16) chars of password
//
void getuserkeyfromargv(word16 key[8],const char *password)
{
   int i;
   for(i=0;i<8;i++)
       key[i]=0;
   if(!*password)
       return;
   key[0]=((unsigned char)*password++)<<8;
   for (i=1;i<8;i++)
       {
       if(!*password)
           return;
       key[i]=(((unsigned char)*password++)<<8)|(key[i-1]>>8);
       }
   if(!*password)
       return;
   key[0] ^= ((unsigned char)*password++)<<8;
   for (i=1;i<8;i++)
       {
       if(!*password)
           return;
       key[i] ^= (((unsigned char)*password++)<<8)|(key[i-1]>>8);
       }
}


#ifdef STANDALONE

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
main(int argc, char **argv)
{
   word16 userkey[8];
   int encryptflag=1;
   unsigned char str[1000];
   unsigned char result[sizeof(str)];
   FILE *fp;
   size_t n;
   int i;

#ifdef FILE_CRYPT
   if (argc!=4)
   {
      printf("\nUsage: idea <infile> <password> [e|d]\n");
      printf("          e=encode   d=decode\n");
      exit(-1);
   }

  if (argv[3][0]=='d') 
      encryptflag=0;
  getuserkeyfromargv(userkey,argv[2]);

   if ((fp=fopen(argv[1],"rb"))==NULL)
   {
      printf("\nError opening file\n");
      exit(-1);
   }
   n=fread(str,1,sizeof(str),fp);
   fclose(fp);
   if (encryptflag)
   {
      printf("\nEncoding\n");
      cipher_string(str,result,userkey,n);
   }
   else
   {
      printf("\nDecoding\n");
      decipher_string(str,result,userkey,n);
   }
   fp=fopen(argv[1],"wb");
   fwrite(result,1,n,fp);
   fclose (fp);
#else

   getuserkeyfromargv(userkey,"Pass");

   for(i=0;i<8;i++)
      printf("%04x ",userkey[i]);


   strcpy(str,"0123456789A"); //Der bloedeTest-Der bloedeTest-Der bloedeTest-δόφ");
   n=strlen(str);
   printf("Encoding >%s<\n",str);
   cipher_string(str,result,userkey,n);
   printf("Decoding ");
   decipher_string(result,str,userkey,n);
   str[n]=0;
   printf(">%s<\n",str);
   _getch();
#endif
   return 0;
}
#endif /* STANDALONE */
