2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2017 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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
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
25 from functools import partial
27 from pygost.gost3413 import pad2
28 from pygost.gost3413 import pad_size
29 from pygost.utils import hexdec
30 from pygost.utils import strxor
31 from pygost.utils import xrange # pylint: disable=redefined-builtin
39 # Sequence of K_i S-box applying for encryption and decryption
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,
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,
54 DEFAULT_SBOX = "Gost28147_CryptoProParamSetA"
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),
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),
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),
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),
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),
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),
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),
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),
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),
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),
160 """ S-box substitution
163 :param _in: 32-bit word
164 :returns: substituted 32-bit word
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)
179 """ Convert block to N1 and N2 integers
181 data = bytearray(data)
183 data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
184 data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
189 """ Convert N1 and N2 integers to 8-byte block
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,
198 def addmod(x, y, mod=2 ** 32):
199 """ Modulo adding of two integers
202 return r if r < mod else r - mod
206 """ 11-bit cyclic shift
208 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
211 def validate_key(key):
212 if len(key) != KEYSIZE:
213 raise ValueError("Invalid key size")
217 if len(iv) != BLOCKSIZE:
218 raise ValueError("Invalid IV size")
221 def validate_sbox(sbox):
222 if sbox not in SBOXES:
223 raise ValueError("Unknown sbox supplied")
226 def xcrypt(seq, sbox, key, ns):
227 """ Perform full-round single-block operation
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
235 :returns: resulting N1 and N2
244 w[3 + i * 4] << 24 for i in range(8)
248 n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
252 def encrypt(sbox, key, ns):
253 """ Encrypt single block
255 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
258 def decrypt(sbox, key, ns):
259 """ Decrypt single block
261 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
264 def ecb(key, data, action, sbox=DEFAULT_SBOX):
265 """ ECB mode of operation
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
278 if not data or len(data) % BLOCKSIZE != 0:
279 raise ValueError("Data is not blocksize aligned")
281 for i in xrange(0, len(data), BLOCKSIZE):
282 result.append(ns2block(action(
283 sbox, key, block2ns(data[i:i + BLOCKSIZE])
285 return b"".join(result)
288 ecb_encrypt = partial(ecb, action=encrypt)
289 ecb_decrypt = partial(ecb, action=decrypt)
292 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
293 """ CBC encryption mode of operation
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
305 34.13-2015 padding method 2 is used.
311 raise ValueError("No data supplied")
313 data = pad2(data, BLOCKSIZE)
314 if len(data) % BLOCKSIZE != 0:
315 raise ValueError("Data is not blocksize aligned")
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])
321 return b"".join(ciphertext)
324 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
325 """ CBC decryption mode of operation
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
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")
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],
350 last_block = bytearray(plaintext[-1])
351 pad_index = last_block.rfind(b"\x80")
353 raise ValueError("Invalid padding")
354 for c in last_block[pad_index + 1:]:
356 raise ValueError("Invalid padding")
357 plaintext[-1] = bytes(last_block[:pad_index])
358 return b"".join(plaintext)
361 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
362 """ Counter mode of operation
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
373 For decryption you use the same function again.
379 raise ValueError("No data supplied")
380 n2, n1 = encrypt(sbox, key, block2ns(iv))
382 for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
383 n1 = addmod(n1, C2, 2 ** 32)
384 n2 = addmod(n2, C1, 2 ** 32 - 1)
385 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
386 return strxor(b"".join(gamma), data)
389 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
393 def meshing(key, iv, sbox=DEFAULT_SBOX):
394 """:rfc:`4357` key meshing
396 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
397 iv = ecb_encrypt(key, iv, sbox=sbox)
401 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
402 """ CFB encryption mode of operation
404 :param bytes key: encryption key
405 :param bytes data: plaintext
406 :param iv: initialization vector
407 :type iv: bytes, BLOCKSIZE length
408 :param sbox: S-box parameters to use
409 :type sbox: str, SBOXES'es key
410 :param bool mesh: enable key meshing
418 raise ValueError("No data supplied")
420 for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
421 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
422 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
423 ciphertext.append(strxor(
424 data[i:i + BLOCKSIZE],
425 ns2block(encrypt(sbox, key, block2ns(iv))),
428 ciphertext.append(strxor(
429 data[i:i + BLOCKSIZE],
430 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
432 return b"".join(ciphertext[1:])
435 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
436 """ CFB decryption mode of operation
438 :param bytes key: encryption key
439 :param bytes data: plaintext
440 :param iv: initialization vector
441 :type iv: bytes, BLOCKSIZE length
442 :param sbox: S-box parameters to use
443 :type sbox: str, SBOXES'es key
444 :param bool mesh: enable key meshing
452 raise ValueError("No data supplied")
455 for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
458 (i - BLOCKSIZE) >= MESH_MAX_DATA and
459 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
461 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
462 plaintext.append(strxor(
463 data[i:i + BLOCKSIZE],
464 ns2block(encrypt(sbox, key, block2ns(iv))),
467 plaintext.append(strxor(
468 data[i:i + BLOCKSIZE],
469 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
471 return b"".join(plaintext)