language: C++ 4.7.2 (gcc-4.7.2)
date: 684 days 17 hours ago
link:
visibility: public
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/*
 * 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.
 */
 
 
/* A 32-bit data type */
#ifdef __alpha  /* Any other 64-bit machines? */
typedef unsigned int word32;
#else
typedef unsigned long 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 = k87[x>>24 & 255] << 24 | k65[x>>16 & 255] << 16 |
            k43[x>> 8 & 255] <<  8 | 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(int argc, char *argv[])
{
        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 */
 
/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/../../../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status