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.gost3413 import unpad2
30 from pygost.utils import hexdec
31 from pygost.utils import strxor
32 from pygost.utils import xrange # pylint: disable=redefined-builtin
40 # Sequence of K_i S-box applying for encryption and decryption
42 0, 1, 2, 3, 4, 5, 6, 7,
43 0, 1, 2, 3, 4, 5, 6, 7,
44 0, 1, 2, 3, 4, 5, 6, 7,
45 7, 6, 5, 4, 3, 2, 1, 0,
48 0, 1, 2, 3, 4, 5, 6, 7,
49 7, 6, 5, 4, 3, 2, 1, 0,
50 7, 6, 5, 4, 3, 2, 1, 0,
51 7, 6, 5, 4, 3, 2, 1, 0,
55 DEFAULT_SBOX = "Gost28147_CryptoProParamSetA"
57 "Gost2814789_TestParamSet": (
58 (4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6),
59 (12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5),
60 (13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11),
61 (14, 9, 11, 2, 5, 15, 7, 1, 0, 13, 12, 6, 10, 4, 3, 8),
62 (3, 14, 5, 9, 6, 8, 0, 13, 10, 11, 7, 12, 2, 1, 15, 4),
63 (8, 15, 6, 11, 1, 9, 12, 5, 13, 3, 7, 10, 0, 14, 2, 4),
64 (9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13),
65 (12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8),
67 "Gost28147_CryptoProParamSetA": (
68 (9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5),
69 (3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1),
70 (14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9),
71 (14, 7, 10, 12, 13, 1, 3, 9, 0, 2, 11, 4, 15, 8, 5, 6),
72 (11, 5, 1, 9, 8, 13, 15, 0, 14, 4, 2, 3, 12, 7, 10, 6),
73 (3, 10, 13, 12, 1, 2, 0, 11, 7, 5, 9, 4, 8, 15, 14, 6),
74 (1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14),
75 (11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4),
77 "Gost28147_CryptoProParamSetB": (
78 (8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15),
79 (0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14),
80 (14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4),
81 (7, 5, 0, 13, 11, 6, 1, 2, 3, 10, 12, 15, 4, 14, 9, 8),
82 (2, 7, 12, 15, 9, 5, 10, 11, 1, 4, 0, 13, 6, 8, 14, 3),
83 (8, 3, 2, 6, 4, 13, 14, 11, 12, 1, 7, 15, 10, 0, 9, 5),
84 (5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14),
85 (0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12),
87 "Gost28147_CryptoProParamSetC": (
88 (1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3),
89 (0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3),
90 (8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11),
91 (3, 6, 0, 1, 5, 13, 10, 8, 11, 2, 9, 7, 14, 15, 12, 4),
92 (8, 13, 11, 0, 4, 5, 1, 2, 9, 3, 12, 14, 6, 15, 10, 7),
93 (12, 9, 11, 1, 8, 14, 2, 4, 7, 3, 6, 5, 10, 0, 15, 13),
94 (10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7),
95 (7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8),
97 "Gost28147_CryptoProParamSetD": (
98 (15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3),
99 (11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1),
100 (1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2),
101 (1, 5, 14, 12, 10, 7, 0, 13, 6, 2, 11, 4, 9, 3, 15, 8),
102 (0, 12, 8, 9, 13, 2, 10, 11, 7, 3, 6, 5, 4, 14, 15, 1),
103 (8, 0, 15, 3, 2, 5, 14, 11, 1, 10, 4, 7, 12, 9, 13, 6),
104 (3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7),
105 (1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14),
107 "GostR3411_94_TestParamSet": (
108 (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
109 (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
110 (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
111 (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
112 (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
113 (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
114 (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
115 (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
117 "GostR3411_94_CryptoProParamSet": (
118 (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15),
119 (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8),
120 (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13),
121 (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3),
122 (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5),
123 (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3),
124 (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11),
125 (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12),
127 "AppliedCryptography": (
128 (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
129 (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
130 (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
131 (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
132 (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
133 (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
134 (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
135 (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
137 "Gost28147_tc26_ParamZ": (
138 (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
139 (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
140 (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
141 (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
142 (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
143 (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
144 (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
145 (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
148 (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
149 (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
150 (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
151 (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
152 (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
153 (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
154 (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
155 (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
161 """ S-box substitution
164 :param _in: 32-bit word
165 :returns: substituted 32-bit word
168 (s[0][(_in >> 0) & 0x0F] << 0) +
169 (s[1][(_in >> 4) & 0x0F] << 4) +
170 (s[2][(_in >> 8) & 0x0F] << 8) +
171 (s[3][(_in >> 12) & 0x0F] << 12) +
172 (s[4][(_in >> 16) & 0x0F] << 16) +
173 (s[5][(_in >> 20) & 0x0F] << 20) +
174 (s[6][(_in >> 24) & 0x0F] << 24) +
175 (s[7][(_in >> 28) & 0x0F] << 28)
180 """ Convert block to N1 and N2 integers
182 data = bytearray(data)
184 data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
185 data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
190 """ Convert N1 and N2 integers to 8-byte block
193 return bytes(bytearray((
194 (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
195 (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
199 def addmod(x, y, mod=2 ** 32):
200 """ Modulo adding of two integers
203 return r if r < mod else r - mod
207 """ 11-bit cyclic shift
209 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
212 def validate_key(key):
213 if len(key) != KEYSIZE:
214 raise ValueError("Invalid key size")
218 if len(iv) != BLOCKSIZE:
219 raise ValueError("Invalid IV size")
222 def validate_sbox(sbox):
223 if sbox not in SBOXES:
224 raise ValueError("Unknown sbox supplied")
227 def xcrypt(seq, sbox, key, ns):
228 """ Perform full-round single-block operation
230 :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
231 :param sbox: S-box parameters to use
232 :type sbox: str, SBOXES'es key
233 :param bytes key: 256-bit encryption key
234 :param ns: N1 and N2 integers
236 :returns: resulting N1 and N2
245 w[3 + i * 4] << 24 for i in range(8)
249 n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
253 def encrypt(sbox, key, ns):
254 """ Encrypt single block
256 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
259 def decrypt(sbox, key, ns):
260 """ Decrypt single block
262 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
265 def ecb(key, data, action, sbox=DEFAULT_SBOX):
266 """ ECB mode of operation
268 :param bytes key: encryption key
269 :param data: plaintext
270 :type data: bytes, multiple of BLOCKSIZE
271 :param func action: "encrypt"/"decrypt"
272 :param sbox: S-box parameters to use
273 :type sbox: str, SBOXES'es key
279 if not data or len(data) % BLOCKSIZE != 0:
280 raise ValueError("Data is not blocksize aligned")
282 for i in xrange(0, len(data), BLOCKSIZE):
283 result.append(ns2block(action(
284 sbox, key, block2ns(data[i:i + BLOCKSIZE])
286 return b"".join(result)
289 ecb_encrypt = partial(ecb, action=encrypt)
290 ecb_decrypt = partial(ecb, action=decrypt)
293 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
294 """ CBC encryption mode of operation
296 :param bytes key: encryption key
297 :param bytes data: plaintext
298 :param iv: initialization vector
299 :type iv: bytes, BLOCKSIZE length
300 :type bool pad: perform ISO/IEC 7816-4 padding
301 :param sbox: S-box parameters to use
302 :type sbox: str, SBOXES'es key
306 34.13-2015 padding method 2 is used.
312 raise ValueError("No data supplied")
314 data = pad2(data, BLOCKSIZE)
315 if len(data) % BLOCKSIZE != 0:
316 raise ValueError("Data is not blocksize aligned")
318 for i in xrange(0, len(data), BLOCKSIZE):
319 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
320 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
322 return b"".join(ciphertext)
325 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
326 """ CBC decryption mode of operation
328 :param bytes key: encryption key
329 :param bytes data: ciphertext
330 :param iv: initialization vector
331 :type iv: bytes, BLOCKSIZE length
332 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
333 :param sbox: S-box parameters to use
334 :type sbox: str, SBOXES'es key
340 if not data or len(data) % BLOCKSIZE != 0:
341 raise ValueError("Data is not blocksize aligned")
342 if len(data) < 2 * BLOCKSIZE:
343 raise ValueError("There is no either data, or IV in ciphertext")
345 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
346 plaintext.append(strxor(
347 ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
348 data[i - BLOCKSIZE:i],
351 plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
352 return b"".join(plaintext)
355 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
356 """ Counter mode of operation
358 :param bytes key: encryption key
359 :param bytes data: plaintext
360 :param iv: initialization vector
361 :type iv: bytes, BLOCKSIZE length
362 :param sbox: S-box parameters to use
363 :type sbox: str, SBOXES'es key
367 For decryption you use the same function again.
373 raise ValueError("No data supplied")
374 n2, n1 = encrypt(sbox, key, block2ns(iv))
376 for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
377 n1 = addmod(n1, C2, 2 ** 32)
378 n2 = addmod(n2, C1, 2 ** 32 - 1)
379 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
380 return strxor(b"".join(gamma), data)
383 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
387 def meshing(key, iv, sbox=DEFAULT_SBOX):
388 """:rfc:`4357` key meshing
390 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
391 iv = ecb_encrypt(key, iv, sbox=sbox)
395 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
396 """ CFB encryption mode of operation
398 :param bytes key: encryption key
399 :param bytes data: plaintext
400 :param iv: initialization vector
401 :type iv: bytes, BLOCKSIZE length
402 :param sbox: S-box parameters to use
403 :type sbox: str, SBOXES'es key
404 :param bool mesh: enable key meshing
412 raise ValueError("No data supplied")
414 for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
415 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
416 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
417 ciphertext.append(strxor(
418 data[i:i + BLOCKSIZE],
419 ns2block(encrypt(sbox, key, block2ns(iv))),
422 ciphertext.append(strxor(
423 data[i:i + BLOCKSIZE],
424 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
426 return b"".join(ciphertext[1:])
429 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
430 """ CFB decryption mode of operation
432 :param bytes key: encryption key
433 :param bytes data: plaintext
434 :param iv: initialization vector
435 :type iv: bytes, BLOCKSIZE length
436 :param sbox: S-box parameters to use
437 :type sbox: str, SBOXES'es key
438 :param bool mesh: enable key meshing
446 raise ValueError("No data supplied")
449 for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
452 (i - BLOCKSIZE) >= MESH_MAX_DATA and
453 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
455 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
456 plaintext.append(strxor(
457 data[i:i + BLOCKSIZE],
458 ns2block(encrypt(sbox, key, block2ns(iv))),
461 plaintext.append(strxor(
462 data[i:i + BLOCKSIZE],
463 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
465 return b"".join(plaintext)