/* * 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.13 2003/01/19 17:48:27 nmav 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 #include #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