424 lines
11 KiB
C
424 lines
11 KiB
C
/*
|
|
* 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 <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
|