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