summaryrefslogtreecommitdiff
path: root/cipher/gost.c
diff options
context:
space:
mode:
Diffstat (limited to 'cipher/gost.c')
-rw-r--r--cipher/gost.c309
1 files changed, 309 insertions, 0 deletions
diff --git a/cipher/gost.c b/cipher/gost.c
new file mode 100644
index 00000000..21b63cbc
--- /dev/null
+++ b/cipher/gost.c
@@ -0,0 +1,309 @@
+/* gost.c - GOST encryption
+ * Copyright (c) 1997 by Werner Koch (dd9jn)
+ *
+ * The description of GOST (and the used S-boxes) are taken from:
+ * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996.
+ * ISBN 0-471-11709-9. .
+ *
+ * This file is part of G10.
+ *
+ * G10 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.
+ *
+ * G10 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+#include "types.h"
+#include "gost.h"
+
+
+
+static u16
+mul_inv( u16 x )
+{
+ u16 t0, t1;
+ u16 q, y;
+
+ if( x < 2 )
+ return x;
+ t1 = 0x10001L / x;
+ y = 0x10001L % x;
+ if( y == 1 )
+ return (1-t1) & 0xffff;
+
+ t0 = 1;
+ do {
+ q = x / y;
+ x = x % y;
+ t0 += q * t1;
+ if( x == 1 )
+ return t0;
+ q = y / x;
+ y = y % x;
+ t1 += q * t0;
+ } while( y != 1 );
+ return (1-t1) & 0xffff;
+}
+
+
+
+static void
+expand_key( byte *userkey, u16 *ek )
+{
+ int i,j;
+
+ for(j=0; j < 8; j++ ) {
+ ek[j] = (*userkey << 8) + userkey[1];
+ userkey += 2;
+ }
+ for(i=0; j < GOST_KEYLEN; j++ ) {
+ i++;
+ ek[i+7] = ek[i&7] << 9 | ek[(i+1)&7] >> 7;
+ ek += i & 8;
+ i &= 7;
+ }
+}
+
+
+static void
+invert_key( u16 *ek, u16 dk[GOST_KEYLEN] )
+{
+ int i;
+ u16 t1, t2, t3;
+ u16 temp[GOST_KEYLEN];
+ u16 *p = temp + GOST_KEYLEN;
+
+ t1 = mul_inv( *ek++ );
+ t2 = -*ek++;
+ t3 = -*ek++;
+ *--p = mul_inv( *ek++ );
+ *--p = t3;
+ *--p = t2;
+ *--p = t1;
+
+ for(i=0; i < GOST_ROUNDS-1; i++ ) {
+ t1 = *ek++;
+ *--p = *ek++;
+ *--p = t1;
+
+ t1 = mul_inv( *ek++ );
+ t2 = -*ek++;
+ t3 = -*ek++;
+ *--p = mul_inv( *ek++ );
+ *--p = t3;
+ *--p = t2;
+ *--p = t1;
+ }
+ t1 = *ek++;
+ *--p = *ek++;
+ *--p = t1;
+
+ t1 = mul_inv( *ek++ );
+ t2 = -*ek++;
+ t3 = -*ek++;
+ *--p = mul_inv( *ek++ );
+ *--p = t3;
+ *--p = t2;
+ *--p = t1;
+ memcpy(dk, temp, sizeof(temp) );
+ memset(temp, 0, sizeof(temp) ); /* burn temp */
+}
+
+
+static void
+cipher( byte *inbuf, byte *outbuf, u16 *key )
+{
+ u16 x1, x2, x3,x4, s2, s3;
+ u16 *in, *out;
+ int r = GOST_ROUNDS;
+ #define MUL(x,y) \
+ do {u16 _t16; u32 _t32; \
+ if( (_t16 = (y)) ) { \
+ if( (x = (x)&0xffff) ) { \
+ _t32 = (u32)x * _t16; \
+ x = _t32 & 0xffff; \
+ _t16 = _t32 >> 16; \
+ x = ((x)-_t16) + (x<_t16?1:0); \
+ } \
+ else { \
+ x = 1 - _t16; \
+ } \
+ } \
+ else { \
+ x = 1 - x; \
+ } \
+ } while(0)
+
+ in = (u16*)inbuf;
+ x1 = *in++;
+ x2 = *in++;
+ x3 = *in++;
+ x4 = *in;
+ #ifdef HAVE_LITTLE_ENDIAN
+ x1 = (x1>>8) | (x1<<8);
+ x2 = (x2>>8) | (x2<<8);
+ x3 = (x3>>8) | (x3<<8);
+ x4 = (x4>>8) | (x4<<8);
+ #endif
+ do {
+ MUL(x1, *key++);
+ x2 += *key++;
+ x3 += *key++;
+ MUL(x4, *key++ );
+
+ s3 = x3;
+ x3 ^= x1;
+ MUL(x3, *key++);
+ s2 = x2;
+ x2 ^=x4;
+ x2 += x3;
+ MUL(x2, *key++);
+ x3 += x2;
+
+ x1 ^= x2;
+ x4 ^= x3;
+
+ x2 ^= s3;
+ x3 ^= s2;
+ } while( --r );
+ MUL(x1, *key++);
+ x3 += *key++;
+ x2 += *key++;
+ MUL(x4, *key);
+
+ out = (u16*)outbuf;
+ #ifdef HAVE_LITTLE_ENDIAN
+ *out++ = (x1>>8) | (x1<<8);
+ *out++ = (x3>>8) | (x3<<8);
+ *out++ = (x2>>8) | (x2<<8);
+ *out = (x4>>8) | (x4<<8);
+ #else
+ *out++ = x1;
+ *out++ = x3;
+ *out++ = x2;
+ *out = x4;
+ #endif
+ #undef MUL
+}
+
+
+void
+gost_setkey( GOST_context *c, byte *key )
+{
+ expand_key( key, c->ek );
+ invert_key( c->ek, c->dk );
+}
+
+void
+gost_setiv( GOST_context *c, byte *iv )
+{
+ memcpy( c->iv, iv, GOST_BLOCKSIZE );
+}
+
+
+void
+gost_encode( GOST_context *c, byte *outbuf, byte *inbuf, unsigned nblocks )
+{
+ unsigned n;
+
+ for(n=0; n < nblocks; n++ ) {
+ cipher( inbuf, outbuf, c->ek );
+ inbuf += 8;
+ outbuf += 8;
+ }
+}
+
+
+void
+gost_decode( GOST_context *c, byte *outbuf, byte *inbuf, unsigned nblocks )
+{
+ unsigned n;
+
+ for(n=0; n < nblocks; n++ ) {
+ cipher( inbuf, outbuf, c->dk );
+ inbuf += 8;
+ outbuf += 8;
+ }
+}
+
+
+static void
+cfbshift( byte *iv, byte *buf, unsigned count)
+{
+ unsigned n;
+
+ if( count ) {
+ for( n = GOST_BLOCKSIZE - count; n; n--, iv++ )
+ *iv = iv[count];
+ for( ; count; count-- )
+ *iv++ = *buf++;
+ }
+}
+
+
+/****************
+ * FIXME: Make use of bigger chunks
+ */
+static void
+xorblock( byte *out, byte *a, byte *b, unsigned count )
+{
+ for( ; count ; count--, a++, b++ )
+ *out++ = *a ^ *b ;
+}
+
+
+void
+gost_encode_cfb( GOST_context *c, byte *outbuf, byte *inbuf, unsigned nbytes)
+{
+ byte temp[GOST_BLOCKSIZE];
+
+ while( nbytes >= GOST_BLOCKSIZE ) {
+ cipher( c->iv, temp, c->ek );
+ xorblock( outbuf, inbuf, temp, GOST_BLOCKSIZE);
+ cfbshift( c->iv, outbuf, GOST_BLOCKSIZE );
+ nbytes -= GOST_BLOCKSIZE;
+ inbuf += GOST_BLOCKSIZE;
+ outbuf += GOST_BLOCKSIZE;
+ }
+ if( nbytes ) {
+ cipher( c->iv, temp, c->ek );
+ xorblock( outbuf, inbuf, temp, nbytes );
+ cfbshift( c->iv, outbuf, nbytes );
+ }
+}
+
+
+void
+gost_decode_cfb( GOST_context *c, byte *outbuf, byte *inbuf, unsigned nbytes)
+{
+ byte temp[GOST_BLOCKSIZE];
+
+ while( nbytes >= GOST_BLOCKSIZE ) {
+ cipher( c->iv, temp, c->ek );
+ cfbshift( c->iv, inbuf, GOST_BLOCKSIZE );
+ xorblock( outbuf, inbuf, temp, GOST_BLOCKSIZE);
+ nbytes -= GOST_BLOCKSIZE;
+ inbuf += GOST_BLOCKSIZE;
+ outbuf += GOST_BLOCKSIZE;
+ }
+ if( nbytes ) {
+ cipher( c->iv, temp, c->ek );
+ cfbshift( c->iv, inbuf, nbytes );
+ xorblock( outbuf, inbuf, temp, nbytes );
+ }
+}
+