/*      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.  Last modified 8 July 92.

        Zero-based indexing added, names changed from IPES to IDEA.
        CFB functions added.  Random number routines added.

        The IDEA(tm) block cipher is covered by a patent held by ETH and a
        Swiss company called Ascom-Tech AG.  The Swiss patent number is
        PCT/CH91/00117.  International patents are pending. IDEA(tm) is a
        trademark of Ascom-Tech AG.  There is no license fee required for
        noncommercial use.  Commercial users may obtain licensing details
        from Dieter Profos, Ascom Tech AG, Solothurn Lab, Postfach 151, 4502
        Solothurn, Switzerland, Tel +41 65 242885, Fax +41 65 235761.

        The IDEA block cipher uses a 64-bit block size, and a 128-bit key 
        size.  It breaks the 64-bit cipher block into four 16-bit words
        because all of the primitive inner operations are done with 16-bit 
        arithmetic.  It likewise breaks the 128-bit cipher key into eight 
        16-bit words.

        For further information on the IDEA cipher, see these papers:
        1)      Xuejia Lai, "Detailed Description and a Software Implementation of 
        the IPES Cipher", Institute for Signal and Information
        Processing, ETH-Zentrum, Zurich, Switzerland, 1991
        2)      Xuejia Lai, James L. Massey, Sean Murphy, "Markov Ciphers and 
        Differential Cryptanalysis", Advances in Cryptology- EUROCRYPT'91

        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 the
        internal native byte order of the target CPU.

*/
#include <stdio.h>
#include "idea.h"

/* #define lower16(x) ((word16)((x) & mask16)) */ /* unoptimized version */
#define lower16(x)   ((word16)(x))      /* optimized version */

#define maxim        0x10001
#define fuyi         0x10000    /* Why did Lai pick a name like this? */
#define mask16       ((word16) 0xffff)
#define ROUNDS       8          /* Don't change this value, should be 8 */

/*	Assembler primitives */
word16 mul(word16 a, word16 b);
void cipher_idea(word16 inblock[4], word16 outblock[4], idea_keyblock Z);

/*      Compute multiplicative inverse of x, modulo (2**16)+1,
        using Euclid's GCD algorithm. */
static word16 inv(word16 x)     
{
        long n1,n2,q,r,b1,b2,t;
        if (x == 0)
                b2 = 0;
        else
        {       n1 = maxim;
                n2 = x;
                b2 = 1;
                b1 = 0;
                do   
                {       r = (n1 % n2);
                        q = (n1-r)/n2;
                        if (r == 0)  
                        {       if (b2 < 0)
                                        b2 = maxim+b2;
                        }
                        else
                        {       n1 = n2; 
                                n2 = r; 
                                t = b2;
                                b2 = b1 - q*b2; 
                                b1 = t;
                        }
                } while (r != 0);
        } 
        return ((word16) b2);
}        /* inv */

/*      Compute IDEA encryption subkeys Z */
extern void en_key_idea(word16 userkey[8], idea_keyblock Z)
{
        word16 S[54];
        int i,j,r;
        /*   shifts   */
        for (i = 0; i<8; i++)
                S[i] = userkey[i];
        for (i = 8; i<54; i++)
        {       if ((i+2)%8 == 0)  /* for S[14],S[22],...  */  
                        S[i] = lower16((S[i-7] << 9) ^ (S[i-14] >> 7)); 
                else
                        if ((i+1)%8 == 0)  /* for S[15],S[23],...   */
                        S[i] = lower16((S[i-15] << 9) ^ (S[i-14] >> 7)); 
                else
                        S[i] = lower16((S[i-7] << 9) ^ (S[i-6] >> 7));
        }
        /* get subkeys */
        for (r=0; r<ROUNDS+1; r++)
                for (j=0; j<6; j++)
                        Z[j][r] = S[6*r + j];

        /* clear sensitive key data from memory... */
        for (i=0; i<54; i++) S[i] = 0;

}        /* en_key_idea */


/*      Compute IDEA decryption subkeys DK from encryption subkeys Z */
extern void de_key_idea(idea_keyblock Z, idea_keyblock DK)
{  
        int j;
        for (j=0; j<ROUNDS+1; j++)
        {       DK[0][ROUNDS-j] = inv((word16) Z[0][j]);
                DK[3][ROUNDS-j] = inv((word16) Z[3][j]);
                if (j==0 || j==ROUNDS)
                {       DK[1][ROUNDS-j] = lower16(fuyi-Z[1][j]);
                        DK[2][ROUNDS-j] = lower16(fuyi-Z[2][j]);
                }
                else
                {       DK[1][ROUNDS-j] = lower16(fuyi-Z[2][j]);
                        DK[2][ROUNDS-j] = lower16(fuyi-Z[1][j]);
                }
        }  
        for (j=0; j<ROUNDS; j++)
        {       DK[4][ROUNDS-1-j] = Z[4][j];
                DK[5][ROUNDS-1-j] = Z[5][j];
        }
}        /* de_key_idea */
