1 /**
2 * GOST 28147-89
3 * 
4 * Copyright:
5 * (C) 1999-2009 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.block.gost_28147;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_GOST_28147_89):
15 
16 import botan.block.block_cipher;
17 import botan.utils.loadstor;
18 import botan.utils.rotate;
19 import botan.utils.exceptn;
20 import botan.utils.get_byte;
21 import botan.utils.mem_ops;
22 import std.format : format;
23 
24 /**
25 * The GOST 28147-89 block cipher uses a set of 4 bit Sboxes, however
26 * the standard does not actually define these Sboxes; they are
27 * considered a local configuration issue. Several different sets are
28 * used.
29 */
30 final class GOST_28147_89_Params
31 {
32 public:
33     /**
34     * Params:
35     *  row = the row
36     *  col = the column
37     * Returns: sbox entry at this row/column
38     */
39     ubyte sboxEntry(size_t row, size_t col) const
40     {
41         ubyte x = m_sboxes[4 * col + (row / 2)];
42         
43         return (row % 2 == 0) ? (x >> 4) : (x & 0x0F);
44     }
45 
46     /**
47     * Returns: name of this parameter set
48     */
49     string paramName() const { return m_name; }
50 
51     /**
52     * Default GOST parameters are the ones given in GOST R 34.11 for
53     * testing purposes; these sboxes are also used by Crypto++, and,
54     * at least according to Wikipedia, the Central Bank of Russian
55     * Federation
56     * 
57     * Params:
58     *  name = of the parameter set
59     */
60     this(in string name = "R3411_94_TestParam") 
61     {
62         m_name = name;
63         // Encoded in the packed fromat from RFC 4357
64         
65         // GostR3411_94_TestParamSet (OID 1.2.643.2.2.31.0)
66         __gshared immutable ubyte[64] GOST_R_3411_TEST_PARAMS = [
67             0x4E, 0x57, 0x64, 0xD1, 0xAB, 0x8D, 0xCB, 0xBF, 0x94, 0x1A, 0x7A,
68             0x4D, 0x2C, 0xD1, 0x10, 0x10, 0xD6, 0xA0, 0x57, 0x35, 0x8D, 0x38,
69             0xF2, 0xF7, 0x0F, 0x49, 0xD1, 0x5A, 0xEA, 0x2F, 0x8D, 0x94, 0x62,
70             0xEE, 0x43, 0x09, 0xB3, 0xF4, 0xA6, 0xA2, 0x18, 0xC6, 0x98, 0xE3,
71             0xC1, 0x7C, 0xE5, 0x7E, 0x70, 0x6B, 0x09, 0x66, 0xF7, 0x02, 0x3C,
72             0x8B, 0x55, 0x95, 0xBF, 0x28, 0x39, 0xB3, 0x2E, 0xCC ];
73         
74         // GostR3411-94-CryptoProParamSet (OID 1.2.643.2.2.31.1)
75         __gshared immutable ubyte[64] GOST_R_3411_CRYPTOPRO_PARAMS = [
76             0xA5, 0x74, 0x77, 0xD1, 0x4F, 0xFA, 0x66, 0xE3, 0x54, 0xC7, 0x42,
77             0x4A, 0x60, 0xEC, 0xB4, 0x19, 0x82, 0x90, 0x9D, 0x75, 0x1D, 0x4F,
78             0xC9, 0x0B, 0x3B, 0x12, 0x2F, 0x54, 0x79, 0x08, 0xA0, 0xAF, 0xD1,
79             0x3E, 0x1A, 0x38, 0xC7, 0xB1, 0x81, 0xC6, 0xE6, 0x56, 0x05, 0x87,
80             0x03, 0x25, 0xEB, 0xFE, 0x9C, 0x6D, 0xF8, 0x6D, 0x2E, 0xAB, 0xDE,
81             0x20, 0xBA, 0x89, 0x3C, 0x92, 0xF8, 0xD3, 0x53, 0xBC ];
82         
83         if (m_name == "R3411_94_TestParam")
84             m_sboxes = GOST_R_3411_TEST_PARAMS;
85         else if (m_name == "R3411_CryptoPro")
86             m_sboxes = GOST_R_3411_CRYPTOPRO_PARAMS;
87         else
88             throw new InvalidArgument("GOST_28147_89_Params: Unknown " ~ m_name);
89     }
90 private:
91     const ubyte[64] m_sboxes;
92     string m_name;
93 }
94 
95 /**
96 * GOST 28147-89
97 */
98 final class GOST_28147_89 : BlockCipherFixedParams!(8, 32), BlockCipher, SymmetricAlgorithm
99 {
100 public:
101 
102     /*
103     * GOST Encryption
104     */
105     override void encryptN(const(ubyte)* input, ubyte* output, size_t blocks)
106     {
107         foreach (size_t i; 0 .. blocks)
108         {
109             uint N1 = loadLittleEndian!uint(input, 0);
110             uint N2 = loadLittleEndian!uint(input, 1);
111             
112             foreach (size_t j; 0 .. 3)
113             {
114                 mixin(GOST_2ROUND!(N1, N2, 0, 1));
115                 mixin(GOST_2ROUND!(N1, N2, 2, 3));
116                 mixin(GOST_2ROUND!(N1, N2, 4, 5));
117                 mixin(GOST_2ROUND!(N1, N2, 6, 7));
118             }
119             
120             mixin(GOST_2ROUND!(N1, N2, 7, 6));
121             mixin(GOST_2ROUND!(N1, N2, 5, 4));
122             mixin(GOST_2ROUND!(N1, N2, 3, 2));
123             mixin(GOST_2ROUND!(N1, N2, 1, 0));
124             
125             storeLittleEndian(output, N2, N1);
126             
127             input += BLOCK_SIZE;
128             output += BLOCK_SIZE;
129         }
130     }
131 
132     /*
133     * GOST Decryption
134     */
135     override void decryptN(const(ubyte)* input, ubyte* output, size_t blocks)
136     {
137         foreach (size_t i; 0 .. blocks)
138         {
139             uint N1 = loadLittleEndian!uint(input, 0);
140             uint N2 = loadLittleEndian!uint(input, 1);
141             
142             mixin(GOST_2ROUND!(N1, N2, 0, 1));
143             mixin(GOST_2ROUND!(N1, N2, 2, 3));
144             mixin(GOST_2ROUND!(N1, N2, 4, 5));
145             mixin(GOST_2ROUND!(N1, N2, 6, 7));
146             
147             foreach (size_t j; 0 .. 3)
148             {
149                 mixin(GOST_2ROUND!(N1, N2, 7, 6));
150                 mixin(GOST_2ROUND!(N1, N2, 5, 4));
151                 mixin(GOST_2ROUND!(N1, N2, 3, 2));
152                 mixin(GOST_2ROUND!(N1, N2, 1, 0));
153             }
154             
155             storeLittleEndian(output, N2, N1);
156             input += BLOCK_SIZE;
157             output += BLOCK_SIZE;
158         }
159     }
160 
161     override void clear()
162     {
163         zap(m_EK);
164     }
165 
166     @property string name() const
167     {
168         /*
169         'Guess' the right name for the sbox on the basis of the values.
170         This would need to be updated if support for other sbox parameters
171         is added. Preferably, we would just store the string value in the
172         constructor, but can't break binary compat.
173         */
174         string sbox_name = "";
175         if (m_SBOX[0] == 0x00072000)
176             sbox_name = "R3411_94_TestParam";
177         else if (m_SBOX[0] == 0x0002D000)
178             sbox_name = "R3411_CryptoPro";
179         else
180             throw new InternalError("GOST-28147 unrecognized sbox value");
181         
182         return "GOST-28147-89(" ~ sbox_name ~ ")";
183     }
184 
185     override @property size_t parallelism() const { return 1; }
186     override BlockCipher clone() const { return new GOST_28147_89(m_SBOX); }
187     override size_t blockSize() const { return super.blockSize(); }
188     override KeyLengthSpecification keySpec() const { return super.keySpec(); }
189 
190     this(in string params) {
191         this(scoped!GOST_28147_89_Params(params).Scoped_payload);
192     }
193 
194     /**
195     * Params:
196     *  param = the sbox parameters to use
197     */
198     this(in GOST_28147_89_Params param)
199     {
200         m_SBOX = Vector!uint(1024);
201         // Convert the parallel 4x4 sboxes into larger word-based sboxes
202         foreach (size_t i; 0 .. 4)
203         {
204             foreach (size_t j; 0 .. 256)
205             {
206                 const uint T = (param.sboxEntry(2*i  , j % 16)) |
207                                (param.sboxEntry(2*i+1, j / 16) << 4);
208                 m_SBOX[256*i+j] = rotateLeft(T, (11+8*i) % 32);
209             }
210         }
211     }
212 protected:
213     this(const ref Vector!uint other_SBOX) {
214         m_SBOX = other_SBOX.clone; 
215         m_EK = 8;
216     }
217 
218     /*
219     * GOST Key Schedule
220     */
221     override void keySchedule(const(ubyte)* key, size_t)
222     {
223         m_EK.resize(8);
224         foreach (size_t i; 0 .. 8)
225             m_EK[i] = loadLittleEndian!uint(key, i);
226     }
227 
228     /*
229     * The sbox is not secret, this is just a larger expansion of it
230     * which we generate at runtime for faster execution
231     */
232     Vector!uint m_SBOX;
233 
234     SecureVector!uint m_EK;
235 }
236 
237 protected:
238 
239 /*
240 * Two rounds of GOST
241 */
242 enum string GOST_2ROUND(alias N1, alias N2, ubyte R1, ubyte R2) = q{
243     {
244         uint T0 = %1$s + m_EK[%3$s];
245         N2 ^= m_SBOX[get_byte(3, T0)] |
246             m_SBOX[get_byte(2, T0)+256] | 
247             m_SBOX[get_byte(1, T0)+512] | 
248             m_SBOX[get_byte(0, T0)+768];
249 
250         uint T1 = %2$s + m_EK[%4$s];
251         N1 ^= m_SBOX[get_byte(3, T1)] |
252             m_SBOX[get_byte(2, T1)+256] |
253             m_SBOX[get_byte(1, T1)+512] |
254             m_SBOX[get_byte(0, T1)+768];
255     }
256 }.format(__traits(identifier, N1), __traits(identifier, N2), R1, R2);