Skip to content
Snippets Groups Projects
idea.c 5.81 KiB
Newer Older
  • Learn to ignore specific revisions
  • Niels Möller's avatar
    Niels Möller committed
    /* $Id$
     *
     * The basic IDEA transformation
     *
     * This implementation is taken from pgp, see note below.
     *
     * Only primitive operations are done here, chaining modes etc
     * are implemented in a higher level program.
     *
     **********************************************************************
     *
     *    idea.c - 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.
     *
     *      Extensively optimized and restructured by Colin Plumb.
     *
     ***********************************************************************
     *
     * Some changes including endianness cleanup done by Niels Mller.
     *
     */
    
    #include "crypto_types.h"
    #include <idea.h>
    
    #include <string.h>
    
    /*-------------------------------------------------------------*/
    
    #define low16(x)  ((x) & 0xffff)
    
    /*
     *	Multiplication, modulo (2**16)+1
     * Note that this code is structured on the assumption that
     * untaken branches are cheaper than taken branches, and the
     * compiler doesn't schedule branches.
     */
    #ifdef SMALL_CACHE
    const static unsigned INT16
    mul(unsigned INT16 a, unsigned INT16 b)
    {
      register unsigned INT32 p;
    
      p = (unsigned INT32)a * b;
      if (p)
        {
          b = low16(p);
          a = p>>16;
          return (b - a) + (b < a);
        }
      else if (a)
        {
          return 1-b;
        }
      else
        {
          return 1-a;
        }
    } /* mul */
    #endif /* SMALL_CACHE */
    
    /*
     * Compute the multiplicative inverse of x, modulo 65537, using Euclid's
     * algorithm. It is unrolled twice to avoid swapping the registers each
     * iteration, and some subtracts of t have been changed to adds.
     */
    static const unsigned INT16
    inv(unsigned INT16 x)     
    {
      unsigned INT16 t0, t1;
      unsigned INT16 q, y;
    
      if (x <= 1)
        return x;	/* 0 and 1 are self-inverse */
      t1 = 0x10001L / x;	/* Since x >= 2, this fits into 16 bits */
      y = 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);
    } /* inv */
    
    /*
     * Expand a 128-bit user key to a working encryption key ctx
     */
    void
    idea_expand(unsigned INT16 *ctx,
    	    const unsigned INT8 *userkey)
    {
      int i,j;
      
      for (j=0; j<8; j++) {
        ctx[j] = (userkey[0]<<8) + userkey[1];
        userkey += 2;
      }
      for (i=0; j < IDEA_KEYLEN; j++) {
        i++;
        ctx[i+7] = ctx[i & 7] << 9 | ctx[(i+1) & 7] >> 7;
        ctx += i & 8;
        i &= 7;
      }
    } /* idea_expand */
    
    /*
     * Compute IDEA decryption key DK from an expanded IDEA encryption key EK
     * Note that the input and output may be the same.  Thus, the key is
     * inverted into an internal buffer, and then copied to the output.
     */
    void
    idea_invert(unsigned INT16 *d,
    	    const unsigned INT16 *e)
    {
      int i;
      unsigned INT16 t1, t2, t3;
      unsigned INT16 temp[IDEA_KEYLEN];
      unsigned INT16 *p = temp + IDEA_KEYLEN;
    
      t1 = inv(*e++);
      t2 = -*e++;
      t3 = -*e++;
      *--p = inv(*e++);
      *--p = t3;
      *--p = t2;
      *--p = t1;
    
      for (i = 0; i < IDEA_ROUNDS-1; i++) {
        t1 = *e++;
        *--p = *e++;
        *--p = t1;
    
        t1 = inv(*e++);
        t2 = -*e++;
        t3 = -*e++;
        *--p = inv(*e++);
        *--p = t2;
        *--p = t3;
        *--p = t1;
      }
      t1 = *e++;
      *--p = *e++;
      *--p = t1;
    
      t1 = inv(*e++);
      t2 = -*e++;
      t3 = -*e++;
      *--p = inv(*e++);
      *--p = t3;
      *--p = t2;
      *--p = t1;
      /* Copy and destroy temp copy */
      memcpy(d, temp, sizeof(temp));
      memset(temp, 0, sizeof(temp));
    } /* idea_invert */
    
    /*
     * MUL(x,y) computes x = x*y, modulo 0x10001.  Requires two temps, 
     * t16 and t32.  x is modified, and must me a side-effect-free lvalue.
     * y may be anything, but unlike x, must be strictly 16 bits even if
     * low16() is #defined.
     * All of these are equivalent - see which is faster on your machine
     */
    #ifdef SMALL_CACHE
    #define MUL(x,y) (x = mul(low16(x),y))
    #else /* !SMALL_CACHE */
    #ifdef AVOID_JUMPS
    #define MUL(x,y) (x = low16(x-1), t16 = low16((y)-1), \
    		t32 = (unsigned INT32)x*t16 + x + t16 + 1, x = low16(t32), \
    		t16 = t32>>16, x = (x-t16) + (x<t16) )
    #else /* !AVOID_JUMPS (default) */
    #define MUL(x,y) \
    	((t16 = (y)) ? \
    		(x=low16(x)) ? \
    			t32 = (unsigned INT32)x*t16, \
    			x = low16(t32), \
    			t16 = t32>>16, \
    			x = (x-t16)+(x<t16) \
    		: \
    			(x = 1-t16) \
    	: \
    		(x = 1-x))
    #endif
    #endif
    
    /* Endian independent conversions */
    #define char2word(dest, p) \
         do { \
    	    (dest) = *(p)++ << 8; (dest) |= *(p)++; \
    	} while(0)
         
    #define word2char(src, p) \
         do { \
    	    *(p)++ = (src) >> 8; *(p)++ = (src) & 0xff; \
    	} while(0)
         
    /*	IDEA encryption/decryption algorithm */
    /* Note that in and out can be the same buffer */
    void
    idea_crypt(const unsigned INT16 *key,
    	   unsigned INT8 *dest,
    	   const unsigned INT8 *src)
    {
      register unsigned INT16 x1, x2, x3, x4, s2, s3;
      
      /* Setup */
        
      char2word(x1, src); char2word(x2, src);
      char2word(x3, src); char2word(x4, src);
      
      /* Encrypt */
      {
    #ifndef SMALL_CACHE
        register unsigned INT16 t16;	/* Temporaries needed by MUL macro */
        register unsigned INT32 t32;
    #endif
        int r = IDEA_ROUNDS;
        do
          {
    	MUL(x1,*key++);
    	x2 += *key++;
    	x3 += *key++;
    	MUL(x4, *key++);
    
    	s3 = x3;
    	x3 ^= x1;
    	MUL(x3, *key++);
    	s2 = x2;
    	x2 ^= x4;
    	x2 += x3;
    	MUL(x2, *key++);
    	x3 += x2;
    
    	x1 ^= x2;  x4 ^= x3;
    
    	x2 ^= s3;  x3 ^= s2;
          }
        while (--r);
        MUL(x1, *key++);
        x3 += *key++;
        x2 += *key++;
        MUL(x4, *key);
      }
      word2char(x1, dest); word2char(x3, dest);
      word2char(x2, dest); word2char(x4, dest);
    } /* idea_crypt */
    
    /*-------------------------------------------------------------*/