Logo Search packages:      
Sourcecode: uif2iso version File versions  Download package

gost.c

/*
    Copyright (C) 2008 Luigi Auriemma

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    http://www.gnu.org/licenses/gpl-2.0.txt
*/
// modified by Luigi Auriemma for emulating the shameful customizations of MagicISO
/*
 * 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.
 */

/*
 * 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 <stdio.h>
#include <stdint.h>

/* A 32-bit data type */
#ifdef __alpha  /* Any other 64-bit machines? */
typedef unsigned int word32;
#else
typedef uint32_t word32;
#endif

/*
 * 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.
 */
static unsigned char const k8[16] = {
      14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7 };
static unsigned char const k7[16] = {
      15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10 };
static unsigned char const k6[16] = {
      10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8 };
static unsigned char const k5[16] = {
       7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15 };
static unsigned char const k4[16] = {
       2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9 };
static unsigned char const k3[16] = {
      12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11 };
static unsigned char const k2[16] = {
       4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1 };
static unsigned char const k1[16] = {
      13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7 };

/* Byte-at-a-time substitution boxes */
static unsigned char k87[256];
static unsigned char k65[256];
static unsigned char k43[256];
static unsigned char k21[256];

/*
 * Build byte-at-a-time subtitution tables.
 * This must be called once for global setup.
 */
void
kboxinit(void)
{
      int i;
      for (i = 0; i < 256; i++) {
            k87[i] = k8[i >> 4] << 4 | k7[i & 15];
            k65[i] = k6[i >> 4] << 4 | k5[i & 15];
            k43[i] = k4[i >> 4] << 4 | k3[i & 15];
            k21[i] = k2[i >> 4] << 4 | 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
 */
#if __GNUC__
__inline__
#endif
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 = (char)k87[x>>24 & 255] << 24 | (char)k65[x>>16 & 255] << 16 |
          (char)k43[x>> 8 & 255] <<  8 | (char)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].
 */
void
gostcrypt(word32 const in[2], word32 out[2], word32 const key[8])
{
      register word32 n1, n2; /* As named in the GOST */

      n1 = in[0];
      n2 = in[1];

      /* 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 */
      out[0] = n2;
      out[1] = n1;
}


/*
 * 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.
 */
void
gostdecrypt(word32 const in[2], word32 out[2], word32 const key[8])
{
      register word32 n1, n2; /* As named in the GOST */

      n1 = in[0];
      n2 = in[1];

      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]);

      out[0] = n2;
      out[1] = n1;
}

/*
 * The GOST "Output feedback" standard.  It seems closer morally
 * to the counter feedback mode some people have proposed for DES.
 * The avoidance of the short cycles that are possible in OFB seems
 * like a Good Thing.
 *
 * Calling it the stream mode makes more sense.
 *
 * The IV is encrypted with the key to produce the initial counter value.
 * Then, for each output block, a constant is added, modulo 2^32-1
 * (0 is represented as all-ones, not all-zeros), to each half of
 * the counter, and the counter is encrypted to produce the value
 * to XOR with the output.
 *
 * Len is the number of blocks.  Sub-block encryption is
 * left as an exercise for the user.  Remember that the
 * standard defines everything in a little-endian manner,
 * so you want to use the low bit of gamma[0] first.
 *
 * OFB is, of course, self-inverse, so there is only one function.
 */

/* The constants for addition */
#define C1 0x01010104
#define C2 0x01010101

void
gostofb(word32 const *in, word32 *out, int len,
      word32 const iv[2], word32 const key[8])
{
      word32 temp[2];         /* Counter */
      word32 gamma[2];        /* Output XOR value */

      /* Compute starting value for counter */
      gostcrypt(iv, temp, key);

      while (len--) {
            temp[0] += C2;
            if (temp[0] < C2)       /* Wrap modulo 2^32? */
                  temp[0]++;      /* Make it modulo 2^32-1 */
            temp[1] += C1;
            if (temp[1] < C1)       /* Wrap modulo 2^32? */
                  temp[1]++;      /* Make it modulo 2^32-1 */

            gostcrypt(temp, gamma, key);

            *out++ = *in++ ^ gamma[0];
            *out++ = *in++ ^ gamma[1];
      }
}

/*
 * The CFB mode is just what you'd expect.  Each block of ciphertext y[] is
 * derived from the input x[] by the following pseudocode:
 * y[i] = x[i] ^ gostcrypt(y[i-1])
 * x[i] = y[i] ^ gostcrypt(y[i-1])
 * Where y[-1] is the IV.
 *
 * The IV is modified in place.  Again, len is in *blocks*.
 */

void
gostcfbencrypt(word32 const *in, word32 *out, int len,
             word32 iv[2], word32 const key[8])
{
      while (len--) {
            gostcrypt(iv, iv, key);
            iv[0] = *out++ ^= iv[0];
            iv[1] = *out++ ^= iv[1];
      }
}

void
gostcfbdecrypt(word32 const *in, word32 *out, int len,
             word32 iv[2], word32 const key[8])
{
      word32 t;
      while (len--) {
            gostcrypt(iv, iv, key);
            t = *out;
            *out++ ^= iv[0];
            iv[0] = t;
            t = *out;
            *out++ ^= iv[1];
            iv[1] = t;
      }
}


/*
 * The message suthetication code uses only 16 of the 32 rounds.
 * There *is* a swap after the 16th round.
 * The last block should be padded to 64 bits with zeros.
 * len is the number of *blocks* in the input.
 */
void
gostmac(word32 const *in, int len, word32 out[2], word32 const key[8])
{
      register word32 n1, n2; /* As named in the GOST */

      n1 = 0;
      n2 = 0;

      while (len--) {
            n1 ^= *in++;
            n2 = *in++;

            /* 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]);
      }

      out[0] = n1;
      out[1] = n2;
}

#ifdef TEST

#include <stdio.h>
#include <stdlib.h>

/* Designed to cope with 15-bit rand() implementations */
#define RAND32 ((word32)rand() << 17 ^ (word32)rand() << 9 ^ rand())

int
main(void)
{
      word32 key[8];
      word32 plain[2];
      word32 cipher[2];
      int i, j;

      kboxinit();

      printf("GOST 21847-89 test driver.\n");

      for (i = 0; i < 1000; i++) {
            for (j = 0; j < 8; j++)
                  key[j] = RAND32;
            plain[0] = RAND32;
            plain[1] = RAND32;

            printf("%3d\r", i);
            fflush(stdout);

            gostcrypt(plain, cipher, key);
            for (j = 0; j < 99; j++)
                  gostcrypt(cipher, cipher, key);
            for (j = 0; j < 100; j++)
                  gostdecrypt(cipher, cipher, key);

            if (plain[0] != cipher[0] || plain[1] != cipher[1]) {
                  fprintf(stderr, "\nError! i = %d\n", i);
                  return 1;
            }
      }
      printf("All tests passed.\n");
      return 0;
}

#endif /* TEST */


Generated by  Doxygen 1.6.0   Back to index