gnupg/cipher/gost.c

310 lines
5.8 KiB
C

/* gost.c - GOST encryption
* Copyright (C) 1998 Free Software Foundation, Inc.
*
* 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 GNUPG.
*
* GNUPG 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.
*
* GNUPG 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 LITTLE_ENDIAN_HOST
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 LITTLE_ENDIAN_HOST
*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 );
}
}