]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
Raise copyright years
[pygost.git] / pygost / gost28147.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2017 Sergey Matveev <stargrave@stargrave.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 """ GOST 28147-89 block cipher
18
19 This is implementation of :rfc:`5830` ECB, CNT, CFB and :rfc:`4357`
20 CBC modes of operation. N1, N2, K names are taken according to
21 specification's terminology. CNT and CFB modes can work with arbitrary
22 data lengths.
23 """
24
25 from functools import partial
26
27 from pygost.gost3413 import pad1
28 from pygost.gost3413 import pad2
29 from pygost.utils import hexdec
30 from pygost.utils import strxor
31 from pygost.utils import xrange  # pylint: disable=redefined-builtin
32
33
34 KEYSIZE = 32
35 BLOCKSIZE = 8
36 C1 = 0x01010104
37 C2 = 0x01010101
38
39 # Sequence of K_i S-box applying for encryption and decryption
40 SEQ_ENCRYPT = (
41     0, 1, 2, 3, 4, 5, 6, 7,
42     0, 1, 2, 3, 4, 5, 6, 7,
43     0, 1, 2, 3, 4, 5, 6, 7,
44     7, 6, 5, 4, 3, 2, 1, 0,
45 )
46 SEQ_DECRYPT = (
47     0, 1, 2, 3, 4, 5, 6, 7,
48     7, 6, 5, 4, 3, 2, 1, 0,
49     7, 6, 5, 4, 3, 2, 1, 0,
50     7, 6, 5, 4, 3, 2, 1, 0,
51 )
52
53 # S-box parameters
54 DEFAULT_SBOX = "Gost28147_CryptoProParamSetA"
55 SBOXES = {
56     "Gost2814789_TestParamSet": (
57         (4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6),
58         (12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5),
59         (13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11),
60         (14, 9, 11, 2, 5, 15, 7, 1, 0, 13, 12, 6, 10, 4, 3, 8),
61         (3, 14, 5, 9, 6, 8, 0, 13, 10, 11, 7, 12, 2, 1, 15, 4),
62         (8, 15, 6, 11, 1, 9, 12, 5, 13, 3, 7, 10, 0, 14, 2, 4),
63         (9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13),
64         (12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8),
65     ),
66     "Gost28147_CryptoProParamSetA": (
67         (9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5),
68         (3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1),
69         (14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9),
70         (14, 7, 10, 12, 13, 1, 3, 9, 0, 2, 11, 4, 15, 8, 5, 6),
71         (11, 5, 1, 9, 8, 13, 15, 0, 14, 4, 2, 3, 12, 7, 10, 6),
72         (3, 10, 13, 12, 1, 2, 0, 11, 7, 5, 9, 4, 8, 15, 14, 6),
73         (1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14),
74         (11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4),
75     ),
76     "Gost28147_CryptoProParamSetB": (
77         (8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15),
78         (0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14),
79         (14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4),
80         (7, 5, 0, 13, 11, 6, 1, 2, 3, 10, 12, 15, 4, 14, 9, 8),
81         (2, 7, 12, 15, 9, 5, 10, 11, 1, 4, 0, 13, 6, 8, 14, 3),
82         (8, 3, 2, 6, 4, 13, 14, 11, 12, 1, 7, 15, 10, 0, 9, 5),
83         (5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14),
84         (0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12),
85     ),
86     "Gost28147_CryptoProParamSetC": (
87         (1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3),
88         (0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3),
89         (8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11),
90         (3, 6, 0, 1, 5, 13, 10, 8, 11, 2, 9, 7, 14, 15, 12, 4),
91         (8, 13, 11, 0, 4, 5, 1, 2, 9, 3, 12, 14, 6, 15, 10, 7),
92         (12, 9, 11, 1, 8, 14, 2, 4, 7, 3, 6, 5, 10, 0, 15, 13),
93         (10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7),
94         (7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8),
95     ),
96     "Gost28147_CryptoProParamSetD": (
97         (15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3),
98         (11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1),
99         (1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2),
100         (1, 5, 14, 12, 10, 7, 0, 13, 6, 2, 11, 4, 9, 3, 15, 8),
101         (0, 12, 8, 9, 13, 2, 10, 11, 7, 3, 6, 5, 4, 14, 15, 1),
102         (8, 0, 15, 3, 2, 5, 14, 11, 1, 10, 4, 7, 12, 9, 13, 6),
103         (3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7),
104         (1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14),
105     ),
106     "GostR3411_94_TestParamSet": (
107         (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
108         (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
109         (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
110         (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
111         (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
112         (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
113         (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
114         (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
115     ),
116     "GostR3411_94_CryptoProParamSet": (
117         (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15),
118         (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8),
119         (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13),
120         (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3),
121         (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5),
122         (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3),
123         (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11),
124         (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12),
125     ),
126     "AppliedCryptography": (
127         (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
128         (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
129         (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
130         (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
131         (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
132         (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
133         (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
134         (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
135     ),
136     "Gost28147_tc26_ParamZ": (
137         (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
138         (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
139         (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
140         (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
141         (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
142         (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
143         (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
144         (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
145     ),
146     "EACParamSet": (
147         (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
148         (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
149         (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
150         (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
151         (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
152         (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
153         (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
154         (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
155     ),
156 }
157
158
159 def _K(s, _in):
160     """ S-box substitution
161
162     :param s: S-box
163     :param _in: 32-bit word
164     :returns: substituted 32-bit word
165     """
166     return (
167         (s[0][(_in >> 0) & 0x0F] << 0) +
168         (s[1][(_in >> 4) & 0x0F] << 4) +
169         (s[2][(_in >> 8) & 0x0F] << 8) +
170         (s[3][(_in >> 12) & 0x0F] << 12) +
171         (s[4][(_in >> 16) & 0x0F] << 16) +
172         (s[5][(_in >> 20) & 0x0F] << 20) +
173         (s[6][(_in >> 24) & 0x0F] << 24) +
174         (s[7][(_in >> 28) & 0x0F] << 28)
175     )
176
177
178 def block2ns(data):
179     """ Convert block to N1 and N2 integers
180     """
181     data = bytearray(data)
182     return (
183         data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
184         data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
185     )
186
187
188 def ns2block(ns):
189     """ Convert N1 and N2 integers to 8-byte block
190     """
191     n1, n2 = ns
192     return bytes(bytearray((
193         (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
194         (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
195     )))
196
197
198 def addmod(x, y, mod=2 ** 32):
199     """ Modulo adding of two integers
200     """
201     r = x + y
202     return r if r < mod else r - mod
203
204
205 def _shift11(x):
206     """ 11-bit cyclic shift
207     """
208     return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
209
210
211 def validate_key(key):
212     if len(key) != KEYSIZE:
213         raise ValueError("Invalid key size")
214
215
216 def validate_iv(iv):
217     if len(iv) != BLOCKSIZE:
218         raise ValueError("Invalid IV size")
219
220
221 def validate_sbox(sbox):
222     if sbox not in SBOXES:
223         raise ValueError("Unknown sbox supplied")
224
225
226 def xcrypt(seq, sbox, key, ns):
227     """ Perform full-round single-block operation
228
229     :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
230     :param sbox: S-box parameters to use
231     :type sbox: str, SBOXES'es key
232     :param bytes key: 256-bit encryption key
233     :param ns: N1 and N2 integers
234     :type ns: (int, int)
235     :returns: resulting N1 and N2
236     :rtype: (int, int)
237     """
238     s = SBOXES[sbox]
239     w = bytearray(key)
240     x = [
241         w[0 + i * 4] |
242         w[1 + i * 4] << 8 |
243         w[2 + i * 4] << 16 |
244         w[3 + i * 4] << 24 for i in range(8)
245     ]
246     n1, n2 = ns
247     for i in seq:
248         n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
249     return n1, n2
250
251
252 def encrypt(sbox, key, ns):
253     """ Encrypt single block
254     """
255     return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
256
257
258 def decrypt(sbox, key, ns):
259     """ Decrypt single block
260     """
261     return xcrypt(SEQ_DECRYPT, sbox, key, ns)
262
263
264 def ecb(key, data, action, sbox=DEFAULT_SBOX):
265     """ ECB mode of operation
266
267     :param bytes key: encryption key
268     :param data: plaintext
269     :type data: bytes, multiple of BLOCKSIZE
270     :param func action: "encrypt"/"decrypt"
271     :param sbox: S-box parameters to use
272     :type sbox: str, SBOXES'es key
273     :returns: ciphertext
274     :rtype: bytes
275     """
276     validate_key(key)
277     validate_sbox(sbox)
278     if not data or len(data) % BLOCKSIZE != 0:
279         raise ValueError("Data is not blocksize aligned")
280     result = []
281     for i in xrange(0, len(data), BLOCKSIZE):
282         result.append(ns2block(action(
283             sbox, key, block2ns(data[i:i + BLOCKSIZE])
284         )))
285     return b"".join(result)
286
287
288 ecb_encrypt = partial(ecb, action=encrypt)
289 ecb_decrypt = partial(ecb, action=decrypt)
290
291
292 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
293     """ CBC encryption mode of operation
294
295     :param bytes key: encryption key
296     :param bytes data: plaintext
297     :param iv: initialization vector
298     :type iv: bytes, BLOCKSIZE length
299     :type bool pad: perform ISO/IEC 7816-4 padding
300     :param sbox: S-box parameters to use
301     :type sbox: str, SBOXES'es key
302     :returns: ciphertext
303     :rtype: bytes
304
305     34.13-2015 padding method 2 is used.
306     """
307     validate_key(key)
308     validate_iv(iv)
309     validate_sbox(sbox)
310     if not data:
311         raise ValueError("No data supplied")
312     if pad:
313         data = pad2(data, BLOCKSIZE)
314     if len(data) % BLOCKSIZE != 0:
315         raise ValueError("Data is not blocksize aligned")
316     ciphertext = [iv]
317     for i in xrange(0, len(data), BLOCKSIZE):
318         ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
319             strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
320         ))))
321     return b"".join(ciphertext)
322
323
324 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
325     """ CBC decryption mode of operation
326
327     :param bytes key: encryption key
328     :param bytes data: ciphertext
329     :param iv: initialization vector
330     :type iv: bytes, BLOCKSIZE length
331     :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
332     :param sbox: S-box parameters to use
333     :type sbox: str, SBOXES'es key
334     :returns: plaintext
335     :rtype: bytes
336     """
337     validate_key(key)
338     validate_sbox(sbox)
339     if not data or len(data) % BLOCKSIZE != 0:
340         raise ValueError("Data is not blocksize aligned")
341     if len(data) < 2 * BLOCKSIZE:
342         raise ValueError("There is no either data, or IV in ciphertext")
343     plaintext = []
344     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
345         plaintext.append(strxor(
346             ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
347             data[i - BLOCKSIZE:i],
348         ))
349     if pad:
350         last_block = bytearray(plaintext[-1])
351         pad_index = last_block.rfind(b"\x80")
352         if pad_index == -1:
353             raise ValueError("Invalid padding")
354         for c in last_block[pad_index + 1:]:
355             if c != 0:
356                 raise ValueError("Invalid padding")
357         plaintext[-1] = bytes(last_block[:pad_index])
358     return b"".join(plaintext)
359
360
361 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
362     """ Counter mode of operation
363
364     :param bytes key: encryption key
365     :param bytes data: plaintext
366     :param iv: initialization vector
367     :type iv: bytes, BLOCKSIZE length
368     :param sbox: S-box parameters to use
369     :type sbox: str, SBOXES'es key
370     :returns: ciphertext
371     :rtype: bytes
372
373     For decryption you use the same function again.
374     """
375     validate_key(key)
376     validate_iv(iv)
377     validate_sbox(sbox)
378     if not data:
379         raise ValueError("No data supplied")
380     n2, n1 = encrypt(sbox, key, block2ns(iv))
381     size = len(data)
382     data = pad1(data, BLOCKSIZE)
383     gamma = []
384     for _ in xrange(0, len(data), BLOCKSIZE):
385         n1 = addmod(n1, C2, 2 ** 32)
386         n2 = addmod(n2, C1, 2 ** 32 - 1)
387         gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
388     return strxor(b"".join(gamma), data[:size])
389
390
391 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
392 MESH_MAX_DATA = 1024
393
394
395 def meshing(key, iv, sbox=DEFAULT_SBOX):
396     """:rfc:`4357` key meshing
397     """
398     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
399     iv = ecb_encrypt(key, iv, sbox=sbox)
400     return key, iv
401
402
403 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
404     """ CFB encryption mode of operation
405
406     :param bytes key: encryption key
407     :param bytes data: plaintext
408     :param iv: initialization vector
409     :type iv: bytes, BLOCKSIZE length
410     :param sbox: S-box parameters to use
411     :type sbox: str, SBOXES'es key
412     :param bool mesh: enable key meshing
413     :returns: ciphertext
414     :rtype: bytes
415     """
416     validate_key(key)
417     validate_iv(iv)
418     validate_sbox(sbox)
419     if not data:
420         raise ValueError("No data supplied")
421     size = len(data)
422     data = pad1(data, BLOCKSIZE)
423     ciphertext = [iv]
424     for i in xrange(0, len(data), BLOCKSIZE):
425         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
426             key, iv = meshing(key, ciphertext[-1], sbox=sbox)
427             ciphertext.append(strxor(
428                 data[i:i + BLOCKSIZE],
429                 ns2block(encrypt(sbox, key, block2ns(iv))),
430             ))
431             continue
432         ciphertext.append(strxor(
433             data[i:i + BLOCKSIZE],
434             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
435         ))
436     return b"".join(ciphertext[1:])[:size]
437
438
439 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
440     """ CFB decryption mode of operation
441
442     :param bytes key: encryption key
443     :param bytes data: plaintext
444     :param iv: initialization vector
445     :type iv: bytes, BLOCKSIZE length
446     :param sbox: S-box parameters to use
447     :type sbox: str, SBOXES'es key
448     :param bool mesh: enable key meshing
449     :returns: ciphertext
450     :rtype: bytes
451     """
452     validate_key(key)
453     validate_iv(iv)
454     validate_sbox(sbox)
455     if not data:
456         raise ValueError("No data supplied")
457     size = len(data)
458     data = pad1(data, BLOCKSIZE)
459     plaintext = []
460     data = iv + data
461     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
462         if (
463                 mesh and
464                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
465                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
466         ):
467             key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
468             plaintext.append(strxor(
469                 data[i:i + BLOCKSIZE],
470                 ns2block(encrypt(sbox, key, block2ns(iv))),
471             ))
472             continue
473         plaintext.append(strxor(
474             data[i:i + BLOCKSIZE],
475             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
476         ))
477     return b"".join(plaintext)[:size]