/* idea_enc.c */
/* Copyright (C) 1995 Eric Young (eay@mincom.oz.au).
 * All rights reserved.
 * Copyright remains Eric Young's, and as such any Copyright notices in
 * the code are not to be removed.
 * See the COPYRIGHT file in the SSLeay distribution for more details.
 */

#include "idea.h"
#include "idea_lcl.h"

static char *version="\0IDEA part of SSLeay v 0.4.2 06/06/95";

static void idea_enc();
static IDEA_INT inverse();

void idea_encrypt_key(key,ks)
unsigned char *key;
IDEA_KEY_SCHEDULE *ks;
	{
	int i;
	register IDEA_INT *kt,*kf,r0,r1,r2;

	kt=kf= &(ks->data[0][0]);
	c2s(key,kt[0]); c2s(key,kt[1]); c2s(key,kt[2]); c2s(key,kt[3]);
	c2s(key,kt[4]); c2s(key,kt[5]); c2s(key,kt[6]); c2s(key,kt[7]);

	kt+=8;
	for (i=0; i<7; i++)
		{
		r2= kf[1];
		r1= kf[2];
		*(kt++)= ((r2<<9) | (r1>>7))&0xffff;
		r0= kf[3];
		*(kt++)= ((r1<<9) | (r0>>7))&0xffff;
		r1= kf[4];
		*(kt++)= ((r0<<9) | (r1>>7))&0xffff;
		r0= kf[5];
		*(kt++)= ((r1<<9) | (r0>>7))&0xffff;
		r1= kf[6];
		*(kt++)= ((r0<<9) | (r1>>7))&0xffff;
		r0= kf[7];
		*(kt++)= ((r1<<9) | (r0>>7))&0xffff;
		r1= kf[0];
		if (i == 6) break;
		*(kt++)= ((r0<<9) | (r1>>7))&0xffff;
		*(kt++)= ((r1<<9) | (r2>>7))&0xffff;
		kf+=8;
		}
	}

void idea_decrypt_key(ek,dk)
IDEA_KEY_SCHEDULE *ek,*dk;
	{
	int r;
	register IDEA_INT *fp,*tp,t;

	tp= &(dk->data[0][0]);
	fp= &(ek->data[8][0]);
	for (r=0; r<9; r++)
		{
		*(tp++)=inverse(fp[0]);
		*(tp++)=(0x10000-fp[2])&0xffff;
		*(tp++)=(0x10000-fp[1])&0xffff;
		*(tp++)=inverse(fp[3]);
		if (r == 8) break;
		fp-=6;
		*(tp++)=fp[4];
		*(tp++)=fp[5];
		}

	tp= &(dk->data[0][0]);
	t=tp[1];
	tp[1]=tp[2];
	tp[2]=t;

	t=tp[49];
	tp[49]=tp[50];
	tp[50]=t;
	}

void idea_encrypt(in,out,ks)
unsigned char *in,*out;
IDEA_KEY_SCHEDULE *ks;
	{
	unsigned long d[2];

	c2l(in,d[0]);
	c2l(in,d[1]);
	idea_enc(ks,d);
	l2c(d[0],out);
	l2c(d[1],out);
	}

void idea_cbc_encrypt(in,out,length,ks,iv,encrypt)
unsigned char *in,*out;
long length;
IDEA_KEY_SCHEDULE *ks;
unsigned char *iv;
int encrypt;
	{
	register unsigned long tin0,tin1;
	register unsigned long tout0,tout1,xor0,xor1;
	register long l=length;
	unsigned long tin[2];

	if (encrypt)
		{
		c2l(iv,tout0);
		c2l(iv,tout1);
		iv-=8;
		for (; l>0; l-=8)
			{
			if (l >= 8)
				{
				c2l(in,tin0);
				c2l(in,tin1);
				}
			else
				c2ln(in,tin0,tin1,l);
			tin0^=tout0;
			tin1^=tout1;
			tin[0]=tin0;
			tin[1]=tin1;
			idea_enc(ks,tin);
			tout0=tin[0];
			tout1=tin[1];
			l2c(tout0,out);
			l2c(tout1,out);
			}
		l2c(tout0,iv);
		l2c(tout1,iv);
		}
	else
		{
		c2l(iv,xor0);
		c2l(iv,xor1);
		iv-=8;
		for (; l>0; l-=8)
			{
			c2l(in,tin0);
			c2l(in,tin1);
			tin[0]=tin0;
			tin[1]=tin1;
			idea_enc(ks,tin);
			tout0=tin[0]^xor0;
			tout1=tin[1]^xor1;
			if (l >= 8)
				{
				l2c(tout0,out);
				l2c(tout1,out);
				}
			else
				l2cn(tout0,tout1,out,l);
			xor0=tin0;
			xor1=tin1;
			}
		l2c(xor0,iv);
		l2c(xor1,iv);
		}
	tin0=tin1=tout0=tout1=xor0=xor1=0;
	tin[0]=tin[1]=0;
	}

static void idea_enc(key,d)
IDEA_KEY_SCHEDULE *key;
unsigned long *d;
	{
	int i;
	register IDEA_INT *p;
	register unsigned long x1,x2,x3,x4,t0,t1,ul;
	register long sl;

	x1=d[0];
	x2=(d[0]>>16);
	x3=d[1];
	x4=(d[1]>>16);

	p=&(key->data[0][0]);
	for (i=0; i<8; i++)
		{
		x1&=0xffff;
		idea_mul(x1,x1,*p,ul,sl); p++;

		x2+= *(p++);
		x3+= *(p++);

		x4&=0xffff;
		idea_mul(x4,x4,*p,ul,sl); p++;

		t0=(x1^x3)&0xffff;
		idea_mul(t0,t0,*p,ul,sl); p++;

		t1=(t0+(x2^x4))&0xffff;
		idea_mul(t1,t1,*p,ul,sl); p++;

		t0+=t1;

		x1^=t1;
		x4^=t0;
		ul=x2^t0;		/* do the swap to x3 */
		x2=x3^t1;
		x3=ul;
		}

	x1&=0xffff;
	idea_mul(x1,x1,*p,ul,sl); p++;

	t0= x3+ *(p++);
	t1= x2+ *(p++);

	x4&=0xffff;
	idea_mul(x4,x4,*p,ul,sl);

	d[0]=(x1&0xffff)|((t0&0xffff)<<16);
	d[1]=(t1&0xffff)|((x4&0xffff)<<16);
	}

/* taken directly from the 'paper' I'll have a look at it later */
static IDEA_INT inverse(xin)
IDEA_INT xin;
	{
	long n1,n2,q,r,b1,b2,t;

	if (xin == 0)
		b2=0;
	else
		{
		n1=0x10001;
		n2=xin;
		b2=1;
		b1=0;

		do	{
			r=(n1%n2);
			q=(n1-r)/n2;
			if (r == 0)
				{ if (b2 < 0) b2=0x10001+b2; }
			else
				{
				n1=n2;
				n2=r;
				t=b2;
				b2=b1-q*b2;
				b1=t;
				}
			} while (r != 0);
		}
	return((IDEA_INT)b2);
	}
