seal.c
/* seal.c - SEAL encryption algorithm */

#undef SEAL_DEBUG

#define ALG_OK 0
#define ALG_NOTOK 1

struct SealKey {
	unsigned long t[520]; /* 512 rounded up to a multiple of 5 + 5*/
	unsigned long s[265]; /* 256 rounded up to a multiple of 5 + 5*/
	unsigned long r[20];  /* 16 rounded up to multiple of 5 */
};

#define ROT2(x) (((x) >> 2) | ((x) << 30))
#define ROT9(x) (((x) >> 9) | ((x) << 23))
#define ROT8(x) (((x) >> 8) | ((x) << 24))
#define ROT16(x) (((x) >> 16) | ((x) << 16))
#define ROT24(x) (((x) >> 24) | ((x) << 8))
#define ROT27(x) (((x) >> 27) | ((x) << 5))

#define WORD(cp)  ((cp[0] << 24)|(cp[1] << 16)|(cp[2] << 8)|(cp[3]))

#define F1(x, y, z) (((x) & (y)) | ((~(x)) & (z)))
#define F2(x, y, z) ((x)^(y)^(z))
#define F3(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define F4(x, y, z) ((x)^(y)^(z))

int g(in, i, h)
unsigned char *in;
int i;
unsigned long *h;
{
unsigned long h0;
unsigned long h1;
unsigned long h2;
unsigned long h3;
unsigned long h4;
unsigned long a;
unsigned long b;
unsigned long c;
unsigned long d;
unsigned long e;
unsigned char *kp;
unsigned long w[80];
unsigned long temp;

	kp = in;
	h0 = WORD(kp); kp += 4;
	h1 = WORD(kp); kp += 4;
	h2 = WORD(kp); kp += 4;
	h3 = WORD(kp); kp += 4;
	h4 = WORD(kp); kp += 4;

	w[0] = i;
	for (i=1;i<16;i++)
		w[i] = 0;
	for (i=16;i<80;i++)
		w[i] = w[i-3]^w[i-8]^w[i-14]^w[i-16];

	a = h0;
	b = h1;
	c = h2;
	d = h3;
	e = h4;

	for (i=0;i<20;i++)
	{
		temp = ROT27(a) + F1(b, c, d) + e + w[i] + 0x5a827999;
		e = d;
		d = c;
		c = ROT2(b);
		b = a;
		a = temp;
	}
	for (i=20;i<40;i++)
	{
		temp = ROT27(a) + F2(b, c, d) + e + w[i] + 0x6ed9eba1;
		e = d;
		d = c;
		c = ROT2(b);
		b = a;
		a = temp;
	}
	for (i=40;i<60;i++)
	{
		temp = ROT27(a) + F3(b, c, d) + e + w[i] + 0x8f1bbcdc;
		e = d;
		d = c;
		c = ROT2(b);
		b = a;
		a = temp;
	}
	for (i=60;i<80;i++)
	{
		temp = ROT27(a) + F4(b, c, d) + e + w[i] + 0xca62c1d6;
		e = d;
		d = c;
		c = ROT2(b);
		b = a;
		a = temp;
	}
	h[0] = h0+a;
	h[1] = h1+b;
	h[2] = h2+c;
	h[3] = h3+d;
	h[4] = h4+e;

	return (ALG_OK);
}	

unsigned long gamma(a, i)
unsigned char *a;
int i;
{
unsigned long h[5];

	(void) g(a, i/5, h);
	return h[i % 5];
}
	
int  seal_init(key, ks)
unsigned char *key;
struct SealKey **ks;
{
struct SealKey *result;
int i;
unsigned long h[5];

	result = (struct SealKey *) calloc(1, sizeof(*result));
	if (result == (struct SealKey *) 0)
		return (ALG_NOTOK);

	for (i=0;i<510;i+=5)
		g(key, i/5, &(result->t[i]));
	/* horrible special case for the end */
	g(key, 510/5, h);
	for (i=510;i<512;i++)
		result->t[i] = h[i-510];
	/* 0x1000 mod 5 is +1, so have horrible special case for the start */
	g(key, (-1+0x1000)/5, h);
	for (i=0;i<4;i++)
		result->s[i] = h[i+1];
	for (i=4;i<254;i+=5)
		g(key, (i+0x1000)/5, &(result->s[i]));
	/* horrible special case for the end */
	g(key, (254+0x1000)/5, h);
	for (i=254;i<256;i++)
		result->s[i] = h[i-254];
	/* 0x2000 mod 5 is +2, so have horrible special case at the start */
	g(key, (-2+0x2000)/5, h);
	for (i=0;i<3;i++)
		result->r[i] = h[i+2];
	for (i=3;i<13;i+=5)
		g(key, (i+0x2000)/5, &(result->r[i]));
	/* horrible special case for the end */
	g(key, (13+0x2000)/5, h);
	for (i=13;i<16;i++)
		result->r[i] = h[i-13];
#ifdef SEAL_DEBUG
	printf("T:\n");
	for (i=0;i<8;i++)
		printf("%08x ", result->t[i]);
	printf("\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->t[i+8]);
	printf("\n");
	printf("...\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->t[i+504]);
	printf("\n");
	printf("S:\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->s[i]);
	printf("\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->s[i+8]);
	printf("\n");
	printf("...\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->s[i+248]);
	printf("\n");
	printf("R:\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->r[i]);
	printf("\n");
	for (i=0;i<8;i++)
                printf("%08x ", result->r[i+8]);
	printf("\n");
#endif /* SEAL_DEBUG */
	*ks = result;
	return (ALG_OK);
}

int seal(key, in, out)
struct SealKey *key;
unsigned long in;
unsigned long *out;
{
int i;
int j;
int l;
unsigned long a;
unsigned long b;
unsigned long c;
unsigned long d;
unsigned short p;
unsigned short q;
unsigned long n1;
unsigned long n2;
unsigned long n3;
unsigned long n4;
unsigned long *wp;

	wp = out;

	for (l=0;l<4;l++)
	{
		a = in ^ key->r[4*l];
		b = ROT8(in) ^ key->r[4*l+1];
		c = ROT16(in) ^ key->r[4*l+2];
		d = ROT24(in) ^ key->r[4*l+3];
	
		for (j=0;j<2;j++)
		{
			p = a & 0x7fc;
			b += key->t[p/4];
			a = ROT9(a);
	
			p = b & 0x7fc;
			c += key->t[p/4];
			b = ROT9(b);
	
			p = c & 0x7fc;
			d += key->t[p/4];
			c = ROT9(c);
	
			p = d & 0x7fc;
			a += key->t[p/4];
			d = ROT9(d);
	
		}
		n1 = d;
		n2 = b;
		n3 = a;
		n4 = c;
	
		p = a & 0x7fc;
		b += key->t[p/4];
		a = ROT9(a);
	
		p = b & 0x7fc;
		c += key->t[p/4];
		b = ROT9(b);
	
		p = c & 0x7fc;
		d += key->t[p/4];
		c = ROT9(c);
	
		p = d & 0x7fc;
		a += key->t[p/4];
		d = ROT9(d);
		
		for (i=0;i<64;i++)
		{
			p = a & 0x7fc;
			b += key->t[p/4];
			a = ROT9(a);
			b ^= a;
	
			q = b & 0x7fc;
			c ^= key->t[q/4];
			b = ROT9(b);
			c += b;
	
			p = (p+c) & 0x7fc;
			d += key->t[p/4];
			c = ROT9(c);
			d ^= c;
	
			q = (q+d) & 0x7fc;
			a ^= key->t[q/4];
			d = ROT9(d);
			a += d;
	
			p = (p+a) & 0x7fc;
			b ^= key->t[p/4];
			a = ROT9(a);
	
			q = (q+b) & 0x7fc;
			c += key->t[q/4];
			b = ROT9(b);
	
			p = (p+c) & 0x7fc;
			d ^= key->t[p/4];
			c = ROT9(c);
	
			q = (q+d) & 0x7fc;
			a += key->t[q/4];
			d = ROT9(d);
	
			*wp = b + key->s[4*i];
			wp++;
			*wp = c ^ key->s[4*i+1];
			wp++;
			*wp = d + key->s[4*i+2];
			wp++;
			*wp = a ^ key->s[4*i+3];
			wp++;
	
			if (i & 1)
			{
				a += n3;
				c += n4;
			}
			else
			{
				a += n1;
				c += n2;
			}
				
		}
	}
	return (ALG_OK);
}

/* end of file */