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, mesh=False):
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
294 :param bool mesh: enable key meshing
298 34.13-2015 padding method 2 is used.
304 raise ValueError("No data supplied")
306 data = pad2(data, BLOCKSIZE)
307 if len(data) % BLOCKSIZE != 0:
308 raise ValueError("Data is not blocksize aligned")
310 for i in xrange(0, len(data), BLOCKSIZE):
311 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
312 key, _ = meshing(key, iv, sbox=sbox)
313 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
314 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
316 return b"".join(ciphertext)
319 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
320 """ CBC decryption mode of operation
322 :param bytes key: encryption key
323 :param bytes data: ciphertext
324 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
325 :param sbox: S-box parameters to use
326 :type sbox: str, SBOXES'es key
327 :param bool mesh: enable key meshing
333 if not data or len(data) % BLOCKSIZE != 0:
334 raise ValueError("Data is not blocksize aligned")
335 if len(data) < 2 * BLOCKSIZE:
336 raise ValueError("There is no either data, or IV in ciphertext")
337 iv = data[:BLOCKSIZE]
339 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
342 (i - BLOCKSIZE) >= MESH_MAX_DATA and
343 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
345 key, _ = meshing(key, iv, sbox=sbox)
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)