/*
 * The GOST 28147-89 cipher
 *
 * This is based on the 25 Movember 1993 draft translation
 * by Aleksandr Malchik, with Whitfield Diffie, of the Government
 * Standard of the U.S.S.R. GOST 28149-89, "Cryptographic Transformation
 * Algorithm", effective 1 July 1990.  (Whitfield.Diffie@eng.sun.com)
 *
 * That is a draft, and may contain errors, which will be faithfully
 * reflected here, along with possible exciting new bugs.
 *
 * Some details have been cleared up by the paper "Soviet Encryption
 * Algorithm" by Josef Pieprzyk and Leonid Tombak of the University
 * of Wollongong, New South Wales.  (josef/leo@cs.adfa.oz.au)
 *
 * The standard is written by A. Zabotin (project leader), G.P. Glazkov,
 * and V.B. Isaeva.  It was accepted and introduced into use by the
 * action of the State Standards Committee of the USSR on 2 June 89 as
 * No. 1409.  It was to be reviewed in 1993, but whether anyone wishes
 * to take on this obligation from the USSR is questionable.
 *
 * This code is placed in the public domain.
 */

/* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos 
 * All modifications are placed under the license of libmcrypt.
 */

/* $Id: gost.c,v 1.1 2007/07/19 06:11:48 pizon Exp $ */

/*
 * If you read the standard, it belabors the point of copying corresponding
 * bits from point A to point B quite a bit.  It helps to understand that
 * the standard is uniformly little-endian, although it numbers bits from
 * 1 rather than 0, so bit n has value 2^(n-1).  The least significant bit
 * of the 32-bit words that are manipulated in the algorithm is the first,
 * lowest-numbered, in the bit string.
 */

#include <libdefs.h>

#include <mcrypt_modules.h>

#define _mcrypt_set_key gost_LTX__mcrypt_set_key
#define _mcrypt_encrypt gost_LTX__mcrypt_encrypt
#define _mcrypt_decrypt gost_LTX__mcrypt_decrypt
#define _mcrypt_get_size gost_LTX__mcrypt_get_size
#define _mcrypt_get_block_size gost_LTX__mcrypt_get_block_size
#define _is_block_algorithm gost_LTX__is_block_algorithm
#define _mcrypt_get_key_size gost_LTX__mcrypt_get_key_size
#define _mcrypt_get_supported_key_sizes gost_LTX__mcrypt_get_supported_key_sizes
#define _mcrypt_get_algorithms_name gost_LTX__mcrypt_get_algorithms_name
#define _mcrypt_self_test gost_LTX__mcrypt_self_test
#define _mcrypt_algorithm_version gost_LTX__mcrypt_algorithm_version

void _mcrypt_kboxinit(void);

/*
 * The standard does not specify the contents of the 8 4 bit->4 bit
 * substitution boxes, saying they're a parameter of the network
 * being set up.  For illustration purposes here, I have used
 * the first rows of the 8 S-boxes from the DES.  (Note that the
 * DES S-boxes are numbered starting from 1 at the msb.  In keeping
 * with the rest of the GOST, I have used little-endian numbering.
 * Thus, k8 is S-box 1.
 *
 * Obviously, a careful look at the cryptographic properties of the cipher
 * must be undertaken before "production" substitution boxes are defined.
 *
 * The standard also does not specify a standard bit-string representation
 * for the contents of these blocks.
 */

/* These are NOT the original s-boxes. I replaced them with the ones
 * found in Applied Cryptography book by Bruce Schneier. These were
 * used in an application for the Central Bank of the Russian Federation
 * --Nikos
 */
static int init = 0;

static unsigned char const gost_k1[16] = {
	1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 2
};
static unsigned char const gost_k2[16] = {
	13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12
};
static unsigned char const gost_k3[16] = {
	4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14
};
static unsigned char const gost_k4[16] = {
	6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2
};
static unsigned char const gost_k5[16] = {
	7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3
};
static unsigned char const gost_k6[16] = {
	5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11
};
static unsigned char const gost_k7[16] = {
	14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9
};
static unsigned char const gost_k8[16] = {
	4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3
};

/* Byte-at-a-time substitution boxes */
static unsigned char gost_k87[256];
static unsigned char gost_k65[256];
static unsigned char gost_k43[256];
static unsigned char gost_k21[256];

/*
 * Build byte-at-a-time subtitution tables.
 * This must be called once for global setup.
 */

WIN32DLL_DEFINE int _mcrypt_set_key(word32 * inst, word32 * key, int len)
{

	_mcrypt_kboxinit();
	inst[0] = 0;
	inst[1] = 0;
	inst[2] = 0;
	inst[3] = 0;
	inst[4] = 0;
	inst[5] = 0;
	inst[6] = 0;
	inst[7] = 0;
	memmove(inst, key, len);
#ifdef WORDS_BIGENDIAN
	inst[0] = byteswap32(inst[0]);
	inst[1] = byteswap32(inst[1]);
	inst[2] = byteswap32(inst[2]);
	inst[3] = byteswap32(inst[3]);
	inst[4] = byteswap32(inst[4]);
	inst[5] = byteswap32(inst[5]);
	inst[6] = byteswap32(inst[6]);
	inst[7] = byteswap32(inst[7]);
#endif
	return 0;
}

void _mcrypt_kboxinit(void)
{
	int i;

	if (init == 0) {
		init = 1;
		for (i = 0; i < 256; i++) {
			gost_k87[i] = gost_k8[i >> 4] << 4 | gost_k7[i & 15];
			gost_k65[i] = gost_k6[i >> 4] << 4 | gost_k5[i & 15];
			gost_k43[i] = gost_k4[i >> 4] << 4 | gost_k3[i & 15];
			gost_k21[i] = gost_k2[i >> 4] << 4 | gost_k1[i & 15];
		}
	}

}

/*
 * Do the substitution and rotation that are the core of the operation,
 * like the expansion, substitution and permutation of the DES.
 * It would be possible to perform DES-like optimisations and store
 * the table entries as 32-bit words, already rotated, but the
 * efficiency gain is questionable.
 *
 * This should be inlined for maximum speed
 */
static word32 f(word32 x)
{
	/* Do substitutions */
# if 0
	/* This is annoyingly slow */
	x = k8[x >> 28 & 15] << 28 | k7[x >> 24 & 15] << 24 |
	    k6[x >> 20 & 15] << 20 | k5[x >> 16 & 15] << 16 |
	    k4[x >> 12 & 15] << 12 | k3[x >> 8 & 15] << 8 |
	    k2[x >> 4 & 15] << 4 | k1[x & 15];
# else
	/* This is faster */
	x = gost_k87[x >> 24 & 255] << 24 | gost_k65[x >> 16 & 255] << 16 |
	    gost_k43[x >> 8 & 255] << 8 | gost_k21[x & 255];
# endif

	/* Rotate left 11 bits */
	return x << 11 | x >> (32 - 11);

}

/*
 * The GOST standard defines the input in terms of bits 1..64, with
 * bit 1 being the lsb of in[0] and bit 64 being the msb of in[1].
 *
 * The keys are defined similarly, with bit 256 being the msb of key[7].
 */
WIN32DLL_DEFINE void _mcrypt_encrypt(word32 const key[8], word32 * in)
{
	register word32 n1, n2;	/* As named in the GOST */

/* Added to make it compatible with bigendian machines 
 * --nikos 
 */

#ifndef WORDS_BIGENDIAN
	n1 = byteswap32(in[0]);
	n2 = byteswap32(in[1]);
#else
	n1 = in[0];
	n2 = in[1];
#endif

	/* Instead of swapping halves, swap names each round */
	n2 ^= f(n1 + key[0]);
	n1 ^= f(n2 + key[1]);
	n2 ^= f(n1 + key[2]);
	n1 ^= f(n2 + key[3]);
	n2 ^= f(n1 + key[4]);
	n1 ^= f(n2 + key[5]);
	n2 ^= f(n1 + key[6]);
	n1 ^= f(n2 + key[7]);

	n2 ^= f(n1 + key[0]);
	n1 ^= f(n2 + key[1]);
	n2 ^= f(n1 + key[2]);
	n1 ^= f(n2 + key[3]);
	n2 ^= f(n1 + key[4]);
	n1 ^= f(n2 + key[5]);
	n2 ^= f(n1 + key[6]);
	n1 ^= f(n2 + key[7]);

	n2 ^= f(n1 + key[0]);
	n1 ^= f(n2 + key[1]);
	n2 ^= f(n1 + key[2]);
	n1 ^= f(n2 + key[3]);
	n2 ^= f(n1 + key[4]);
	n1 ^= f(n2 + key[5]);
	n2 ^= f(n1 + key[6]);
	n1 ^= f(n2 + key[7]);

	n2 ^= f(n1 + key[7]);
	n1 ^= f(n2 + key[6]);
	n2 ^= f(n1 + key[5]);
	n1 ^= f(n2 + key[4]);
	n2 ^= f(n1 + key[3]);
	n1 ^= f(n2 + key[2]);
	n2 ^= f(n1 + key[1]);
	n1 ^= f(n2 + key[0]);

	/* There is no swap after the last round */

#ifndef WORDS_BIGENDIAN
	in[0] = byteswap32(n2);
	in[1] = byteswap32(n1);
#else
	in[0] = n2;
	in[1] = n1;
#endif
}


/*
 * The key schedule is somewhat different for decryption.
 * (The key table is used once forward and three times backward.)
 * You could define an expanded key, or just write the code twice,
 * as done here.
 */
WIN32DLL_DEFINE void _mcrypt_decrypt(word32 const key[8], word32 * in)
{
	register word32 n1, n2;	/* As named in the GOST */

#ifndef WORDS_BIGENDIAN
	n1 = byteswap32(in[0]);
	n2 = byteswap32(in[1]);
#else
	n1 = in[0];
	n2 = in[1];
#endif

	n2 ^= f(n1 + key[0]);
	n1 ^= f(n2 + key[1]);
	n2 ^= f(n1 + key[2]);
	n1 ^= f(n2 + key[3]);
	n2 ^= f(n1 + key[4]);
	n1 ^= f(n2 + key[5]);
	n2 ^= f(n1 + key[6]);
	n1 ^= f(n2 + key[7]);

	n2 ^= f(n1 + key[7]);
	n1 ^= f(n2 + key[6]);
	n2 ^= f(n1 + key[5]);
	n1 ^= f(n2 + key[4]);
	n2 ^= f(n1 + key[3]);
	n1 ^= f(n2 + key[2]);
	n2 ^= f(n1 + key[1]);
	n1 ^= f(n2 + key[0]);

	n2 ^= f(n1 + key[7]);
	n1 ^= f(n2 + key[6]);
	n2 ^= f(n1 + key[5]);
	n1 ^= f(n2 + key[4]);
	n2 ^= f(n1 + key[3]);
	n1 ^= f(n2 + key[2]);
	n2 ^= f(n1 + key[1]);
	n1 ^= f(n2 + key[0]);

	n2 ^= f(n1 + key[7]);
	n1 ^= f(n2 + key[6]);
	n2 ^= f(n1 + key[5]);
	n1 ^= f(n2 + key[4]);
	n2 ^= f(n1 + key[3]);
	n1 ^= f(n2 + key[2]);
	n2 ^= f(n1 + key[1]);
	n1 ^= f(n2 + key[0]);

#ifndef WORDS_BIGENDIAN
	in[0] = byteswap32(n2);
	in[1] = byteswap32(n1);
#else
	in[0] = n2;
	in[1] = n1;
#endif

}

WIN32DLL_DEFINE int _mcrypt_get_size()
{
	return 8 * sizeof(word32);
}

WIN32DLL_DEFINE int _mcrypt_get_block_size()
{
	return 8;
}
WIN32DLL_DEFINE int _is_block_algorithm()
{
	return 1;
}
WIN32DLL_DEFINE int _mcrypt_get_key_size()
{
	return 32;
}

static const int key_sizes[] = { 32 };
WIN32DLL_DEFINE const int *_mcrypt_get_supported_key_sizes(int *len)
{
	*len = sizeof(key_sizes)/sizeof(int);
	return key_sizes;

}
WIN32DLL_DEFINE const char *_mcrypt_get_algorithms_name()
{
return "GOST";
}

#define CIPHER "e498cf78cdf1d4a5"

WIN32DLL_DEFINE int _mcrypt_self_test()
{
	char *keyword;
	unsigned char plaintext[16];
	unsigned char ciphertext[16];
	int blocksize = _mcrypt_get_block_size(), j;
	void *key;
	unsigned char cipher_tmp[200];

	keyword = calloc(1, _mcrypt_get_key_size());
	if (keyword == NULL)
		return -1;

	for (j = 0; j < _mcrypt_get_key_size(); j++) {
		keyword[j] = ((j * 2 + 10) % 256);
	}

	for (j = 0; j < blocksize; j++) {
		plaintext[j] = j % 256;
	}
	key = malloc(_mcrypt_get_size());
	if (key == NULL) {
		free(keyword);
		return -1;
	}

	memcpy(ciphertext, plaintext, blocksize);

	_mcrypt_set_key(key, (void *) keyword, _mcrypt_get_key_size());
	free(keyword);

	_mcrypt_encrypt(key, (void *) ciphertext);

	for (j = 0; j < blocksize; j++) {
		sprintf(&((char *) cipher_tmp)[2 * j], "%.2x",
			ciphertext[j]);
	}

	if (strcmp((char *) cipher_tmp, CIPHER) != 0) {
		printf("failed compatibility\n");
		printf("Expected: %s\nGot: %s\n", CIPHER,
		       (char *) cipher_tmp);
		free(key);
		return -1;
	}
	_mcrypt_decrypt(key, (void *) ciphertext);
	free(key);

	if (strcmp(ciphertext, plaintext) != 0) {
		printf("failed internally\n");
		return -1;
	}

	return 0;
}

WIN32DLL_DEFINE word32 _mcrypt_algorithm_version()
{
	return 20010801;
}

#ifdef WIN32
# ifdef USE_LTDL
WIN32DLL_DEFINE int main (void)
{
       /* empty main function to avoid linker error (see cygwin FAQ) */
}
# endif
#endif
