
24 June 1998

----------------------------------------------------------------------
/*
  Skipjack, as defined in the document "Skipjack and KEA Algorithm
  dated 29 May 1998, and located at 
	http://csrc.nist.gov/encryption/skipjack-1.pdf
	http://csrc.nist.gov/encryption/skipjack-2.pdf
  Note that there is no author, copyright, or other attribution on the 
  PDF file.
  This implementation seems to successfully fit the test vectors
  listed in the documents. In fact, this version of the code simply
  prints the test vector out.
  The code is not optimized at all. It is written to be obvious, not
  necessarily to be fast.
  This code is hereby placed in the public domain. I would _like_ it
  if you gave me some credit, but you are under no obligation to do
  so.
  Perry E. Metzger, Piermont Information Systems Inc., 24 June 1998
  You can reach me via email at perry@piermont.com
*/

#include <stdio.h>
#include <sys/types.h>

#define PRINTDUMP
/* #define TIMING */

typedef u_int8_t byte;
typedef u_int16_t word;

byte key[10];

const byte ftable[256] = {
/*         x0    x1    x2    x3    x4    x5    x6    x7    x8    x9    xA    xB    xC    xD    xE    xF*/
/* 0x */ 0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
/* 1x */ 0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
/* 2x */ 0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
/* 3x */ 0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
/* 4x */ 0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
/* 5x */ 0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
/* 6x */ 0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
/* 7x */ 0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
/* 8x */ 0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
/* 9x */ 0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,

/* Ax */ 0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
/* Bx */ 0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
/* Cx */ 0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
/* Dx */ 0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
/* Ex */ 0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
/* Fx */ 0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46
};


#define HIGH(x)      (((x) >> 8) & 0xff)
#define LOW(x)       ((x) & 0xff)
#define CONCAT(h, l) ((((word)(h)) << 8) | ((word)(l)))

#define CV(x) (key[((x) % 10)])

#define f(x) (ftable[(x)])

static word g(int k, word w)
{
	byte g1, g2, g3, g4, g5, g6;
	word ret;

	g1 = HIGH(w);
	g2 = LOW(w);

	g3 = f(g2 ^ CV(4*k  )) ^ g1;
	g4 = f(g3 ^ CV(4*k+1)) ^ g2;
	g5 = f(g4 ^ CV(4*k+2)) ^ g3;
	g6 = f(g5 ^ CV(4*k+3)) ^ g4;

	ret = CONCAT(g5, g6);

	return(ret);
}

static word inv_g(int k, word w)
{
	byte g1, g2, g3, g4, g5, g6;
	word ret;

	g6 = LOW(w);
	g5 = HIGH(w);
	g4 = f(g5 ^ CV(4*k+3)) ^ g6;
	g3 = f(g4 ^ CV(4*k+2)) ^ g5;
	g2 = f(g3 ^ CV(4*k+1)) ^ g4;
	g1 = f(g2 ^ CV(4*k  )) ^ g3;

	ret = CONCAT(g1, g2);
	return(ret);
}

static void dump(int i, word w1, word w2, word w3, word w4)
{
	printf("round %2d: %4.4x %4.4x %4.4x %4.4x\n", i, w1, w2, w3, w4);
}

static void ruleA(word *w1, word *w2, word *w3, word *w4, int k)
{
	word c, t1, t2, t3, t4, tmp;

	c = k + 1;
	
	t1 = *w1;
	t2 = *w2;
	t3 = *w3;
	t4 = *w4;

	tmp = g(k, t1);
	*w1 = tmp ^ t4 ^ c;
	*w2 = tmp;
	*w3 = t2;
	*w4 = t3;
}

static void ruleB(word *w1, word *w2, word *w3, word *w4, int k)
{
	word c, t1, t2, t3, t4;

	c = k + 1;
	
	t1 = *w1;
	t2 = *w2;
	t3 = *w3;
	t4 = *w4;

	*w1 = t4;
	*w2 = g(k, t1);
	*w3 = t1 ^ t2 ^ c;
	*w4 = t3;
}

static void inv_ruleA(word *w1, word *w2, word *w3, word *w4, int k)
{
	word c, t1, t2, t3, t4;

	c = k;
	
	t1 = *w1;
	t2 = *w2;
	t3 = *w3;
	t4 = *w4;

	*w1 = inv_g((k-1), t2);
	*w2 = t3;
	*w3 = t4;
	*w4 = t1 ^ t2 ^ c;
}

static void inv_ruleB(word *w1, word *w2, word *w3, word *w4, int k)
{
	word c, t1, t2, t3, t4, tmp;

	c = k;
	
	t1 = *w1;
	t2 = *w2;
	t3 = *w3;
	t4 = *w4;

	tmp = inv_g((k-1), t2);
	*w1 = tmp;
	*w2 = tmp ^ t3 ^ c;
	*w3 = t4;
	*w4 = t1;
}

void encrypt(word *w1, word *w2, word *w3, word *w4)
{
	int i, k;

	k = 0;

	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		ruleA(w1, w2, w3, w4, k++);
	}

	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		ruleB(w1, w2, w3, w4, k++);
	}
	
	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		ruleA(w1, w2, w3, w4, k++);
	}
	
	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		ruleB(w1, w2, w3, w4, k++);
	}
		
}

void decrypt(word *w1, word *w2, word *w3, word *w4)
{
	int i, k;

	k = 32;

	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		inv_ruleB(w1, w2, w3, w4, k--);
	}

	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		inv_ruleA(w1, w2, w3, w4, k--);
	}

	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		inv_ruleB(w1, w2, w3, w4, k--);
	}

	for (i = 0; i < 8; i++) {
#ifdef PRINTDUMP
		dump(k, *w1, *w2, *w3, *w4);
#endif
		inv_ruleA(w1, w2, w3, w4, k--);
	}

}

int main(int argc, char **argv)
{
	int i;
	word w1, w2, w3, w4;

	/* plaintext 33221100ddccbbaa */
	w1=0x3322;
	w2=0x1100;
	w3=0xddcc;
	w4=0xbbaa;

	/* key       00998877665544332211 */
	key[0] = 0x00;
	key[1] = 0x99;
	key[2] = 0x88;
	key[3] = 0x77;
	key[4] = 0x66;
	key[5] = 0x55;
	key[6] = 0x44;
	key[7] = 0x33;
	key[8] = 0x22;
	key[9] = 0x11;

#ifdef TIMING
	for (i = 0; i < 65536; i++)
#endif
		encrypt(&w1, &w2, &w3, &w4);

	dump(32, w1, w2, w3, w4);

#ifdef TIMING
	for (i = 0; i < 65536; i++)
#endif
		decrypt(&w1, &w2, &w3, &w4);

	dump(00, w1, w2, w3, w4);
}
