2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2018 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 "Gost28147_tc26_ParamZ": (
128 (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
129 (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
130 (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
131 (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
132 (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
133 (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
134 (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
135 (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
138 (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
139 (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
140 (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
141 (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
142 (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
143 (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
144 (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
145 (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
148 SBOXES["AppliedCryptography"] = SBOXES["GostR3411_94_TestParamSet"]
152 """ S-box substitution
155 :param _in: 32-bit word
156 :returns: substituted 32-bit word
159 (s[0][(_in >> 0) & 0x0F] << 0) +
160 (s[1][(_in >> 4) & 0x0F] << 4) +
161 (s[2][(_in >> 8) & 0x0F] << 8) +
162 (s[3][(_in >> 12) & 0x0F] << 12) +
163 (s[4][(_in >> 16) & 0x0F] << 16) +
164 (s[5][(_in >> 20) & 0x0F] << 20) +
165 (s[6][(_in >> 24) & 0x0F] << 24) +
166 (s[7][(_in >> 28) & 0x0F] << 28)
171 """ Convert block to N1 and N2 integers
173 data = bytearray(data)
175 data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
176 data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
181 """ Convert N1 and N2 integers to 8-byte block
184 return bytes(bytearray((
185 (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
186 (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
190 def addmod(x, y, mod=2 ** 32):
191 """ Modulo adding of two integers
194 return r if r < mod else r - mod
198 """ 11-bit cyclic shift
200 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
203 def validate_key(key):
204 if len(key) != KEYSIZE:
205 raise ValueError("Invalid key size")
209 if len(iv) != BLOCKSIZE:
210 raise ValueError("Invalid IV size")
213 def validate_sbox(sbox):
214 if sbox not in SBOXES:
215 raise ValueError("Unknown sbox supplied")
218 def xcrypt(seq, sbox, key, ns):
219 """ Perform full-round single-block operation
221 :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
222 :param sbox: S-box parameters to use
223 :type sbox: str, SBOXES'es key
224 :param bytes key: 256-bit encryption key
225 :param ns: N1 and N2 integers
227 :returns: resulting N1 and N2
236 w[3 + i * 4] << 24 for i in range(8)
240 n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
244 def encrypt(sbox, key, ns):
245 """ Encrypt single block
247 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
250 def decrypt(sbox, key, ns):
251 """ Decrypt single block
253 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
256 def ecb(key, data, action, sbox=DEFAULT_SBOX):
257 """ ECB mode of operation
259 :param bytes key: encryption key
260 :param data: plaintext
261 :type data: bytes, multiple of BLOCKSIZE
262 :param func action: "encrypt"/"decrypt"
263 :param sbox: S-box parameters to use
264 :type sbox: str, SBOXES'es key
270 if not data or len(data) % BLOCKSIZE != 0:
271 raise ValueError("Data is not blocksize aligned")
273 for i in xrange(0, len(data), BLOCKSIZE):
274 result.append(ns2block(action(
275 sbox, key, block2ns(data[i:i + BLOCKSIZE])
277 return b"".join(result)
280 ecb_encrypt = partial(ecb, action=encrypt)
281 ecb_decrypt = partial(ecb, action=decrypt)
284 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
285 """ CBC encryption mode of operation
287 :param bytes key: encryption key
288 :param bytes data: plaintext
289 :param iv: initialization vector
290 :type iv: bytes, BLOCKSIZE length
291 :type bool pad: perform ISO/IEC 7816-4 padding
292 :param sbox: S-box parameters to use
293 :type sbox: str, SBOXES'es key
297 34.13-2015 padding method 2 is used.
303 raise ValueError("No data supplied")
305 data = pad2(data, BLOCKSIZE)
306 if len(data) % BLOCKSIZE != 0:
307 raise ValueError("Data is not blocksize aligned")
309 for i in xrange(0, len(data), BLOCKSIZE):
310 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
311 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
313 return b"".join(ciphertext)
316 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
317 """ CBC decryption mode of operation
319 :param bytes key: encryption key
320 :param bytes data: ciphertext
321 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
322 :param sbox: S-box parameters to use
323 :type sbox: str, SBOXES'es key
329 if not data or len(data) % BLOCKSIZE != 0:
330 raise ValueError("Data is not blocksize aligned")
331 if len(data) < 2 * BLOCKSIZE:
332 raise ValueError("There is no either data, or IV in ciphertext")
334 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
335 plaintext.append(strxor(
336 ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
337 data[i - BLOCKSIZE:i],
340 plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
341 return b"".join(plaintext)
344 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
345 """ Counter mode of operation
347 :param bytes key: encryption key
348 :param bytes data: plaintext
349 :param iv: initialization vector
350 :type iv: bytes, BLOCKSIZE length
351 :param sbox: S-box parameters to use
352 :type sbox: str, SBOXES'es key
356 For decryption you use the same function again.
362 raise ValueError("No data supplied")
363 n2, n1 = encrypt(sbox, key, block2ns(iv))
365 for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
366 n1 = addmod(n1, C2, 2 ** 32)
367 n2 = addmod(n2, C1, 2 ** 32 - 1)
368 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
369 return strxor(b"".join(gamma), data)
372 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
376 def meshing(key, iv, sbox=DEFAULT_SBOX):
377 """:rfc:`4357` key meshing
379 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
380 iv = ecb_encrypt(key, iv, sbox=sbox)
384 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
385 """ CFB encryption mode of operation
387 :param bytes key: encryption key
388 :param bytes data: plaintext
389 :param iv: initialization vector
390 :type iv: bytes, BLOCKSIZE length
391 :param sbox: S-box parameters to use
392 :type sbox: str, SBOXES'es key
393 :param bool mesh: enable key meshing
401 raise ValueError("No data supplied")
403 for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
404 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
405 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
406 ciphertext.append(strxor(
407 data[i:i + BLOCKSIZE],
408 ns2block(encrypt(sbox, key, block2ns(iv))),
411 ciphertext.append(strxor(
412 data[i:i + BLOCKSIZE],
413 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
415 return b"".join(ciphertext[1:])
418 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
419 """ CFB decryption mode of operation
421 :param bytes key: encryption key
422 :param bytes data: plaintext
423 :param iv: initialization vector
424 :type iv: bytes, BLOCKSIZE length
425 :param sbox: S-box parameters to use
426 :type sbox: str, SBOXES'es key
427 :param bool mesh: enable key meshing
435 raise ValueError("No data supplied")
438 for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
441 (i - BLOCKSIZE) >= MESH_MAX_DATA and
442 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
444 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
445 plaintext.append(strxor(
446 data[i:i + BLOCKSIZE],
447 ns2block(encrypt(sbox, key, block2ns(iv))),
450 plaintext.append(strxor(
451 data[i:i + BLOCKSIZE],
452 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
454 return b"".join(plaintext)