2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2020 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, version 3 of the License.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 """ GOST 28147-89 block cipher
18 This is implementation of :rfc:`5830` ECB, CNT, CFB and :rfc:`4357`
19 CBC modes of operation. N1, N2, K names are taken according to
20 specification's terminology. CNT and CFB modes can work with arbitrary
24 from functools import partial
26 from pygost.gost3413 import pad2
27 from pygost.gost3413 import pad_size
28 from pygost.gost3413 import unpad2
29 from pygost.utils import hexdec
30 from pygost.utils import strxor
31 from pygost.utils import xrange
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 = "id-Gost28147-89-CryptoPro-A-ParamSet"
56 "id-Gost28147-89-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 "id-Gost28147-89-CryptoPro-A-ParamSet": (
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 "id-Gost28147-89-CryptoPro-B-ParamSet": (
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 "id-Gost28147-89-CryptoPro-C-ParamSet": (
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 "id-Gost28147-89-CryptoPro-D-ParamSet": (
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 "id-tc26-gost-28147-param-Z": (
107 (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
108 (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
109 (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
110 (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
111 (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
112 (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
113 (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
114 (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
116 "id-GostR3411-94-TestParamSet": (
117 (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
118 (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
119 (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
120 (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
121 (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
122 (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
123 (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
124 (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
126 "id-GostR3411-94-CryptoProParamSet": (
127 (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15),
128 (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8),
129 (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13),
130 (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3),
131 (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5),
132 (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3),
133 (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11),
134 (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12),
137 (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
138 (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
139 (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
140 (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
141 (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
142 (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
143 (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
144 (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
147 SBOXES["AppliedCryptography"] = SBOXES["id-GostR3411-94-TestParamSet"]
151 """ S-box substitution
154 :param _in: 32-bit word
155 :returns: substituted 32-bit word
158 (s[0][(_in >> 0) & 0x0F] << 0) +
159 (s[1][(_in >> 4) & 0x0F] << 4) +
160 (s[2][(_in >> 8) & 0x0F] << 8) +
161 (s[3][(_in >> 12) & 0x0F] << 12) +
162 (s[4][(_in >> 16) & 0x0F] << 16) +
163 (s[5][(_in >> 20) & 0x0F] << 20) +
164 (s[6][(_in >> 24) & 0x0F] << 24) +
165 (s[7][(_in >> 28) & 0x0F] << 28)
170 """ Convert block to N1 and N2 integers
172 data = bytearray(data)
174 data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
175 data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
180 """ Convert N1 and N2 integers to 8-byte block
183 return bytes(bytearray((
184 (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
185 (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
189 def addmod(x, y, mod=2 ** 32):
190 """ Modulo adding of two integers
193 return r if r < mod else r % mod
197 """ 11-bit cyclic shift
199 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
202 def validate_key(key):
203 if len(key) != KEYSIZE:
204 raise ValueError("Invalid key size")
208 if len(iv) != BLOCKSIZE:
209 raise ValueError("Invalid IV size")
212 def validate_sbox(sbox):
213 if sbox not in SBOXES:
214 raise ValueError("Unknown sbox supplied")
217 def xcrypt(seq, sbox, key, ns):
218 """ Perform full-round single-block operation
220 :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
221 :param sbox: S-box parameters to use
222 :type sbox: str, SBOXES'es key
223 :param bytes key: 256-bit encryption key
224 :param ns: N1 and N2 integers
226 :returns: resulting N1 and N2
235 w[3 + i * 4] << 24 for i in range(8)
239 n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
243 def encrypt(sbox, key, ns):
244 """ Encrypt single block
246 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
249 def decrypt(sbox, key, ns):
250 """ Decrypt single block
252 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
255 def ecb(key, data, action, sbox=DEFAULT_SBOX):
256 """ ECB mode of operation
258 :param bytes key: encryption key
259 :param data: plaintext
260 :type data: bytes, multiple of BLOCKSIZE
261 :param func action: "encrypt"/"decrypt"
262 :param sbox: S-box parameters to use
263 :type sbox: str, SBOXES'es key
269 if not data or len(data) % BLOCKSIZE != 0:
270 raise ValueError("Data is not blocksize aligned")
272 for i in xrange(0, len(data), BLOCKSIZE):
273 result.append(ns2block(action(
274 sbox, key, block2ns(data[i:i + BLOCKSIZE])
276 return b"".join(result)
279 ecb_encrypt = partial(ecb, action=encrypt)
280 ecb_decrypt = partial(ecb, action=decrypt)
283 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False):
284 """ CBC encryption mode of operation
286 :param bytes key: encryption key
287 :param bytes data: plaintext
288 :param iv: initialization vector
289 :type iv: bytes, BLOCKSIZE length
290 :type bool pad: perform ISO/IEC 7816-4 padding
291 :param sbox: S-box parameters to use
292 :type sbox: str, SBOXES'es key
293 :param bool mesh: enable key meshing
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 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
311 key, _ = meshing(key, iv, sbox=sbox)
312 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
313 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
315 return b"".join(ciphertext)
318 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
319 """ CBC decryption mode of operation
321 :param bytes key: encryption key
322 :param bytes data: ciphertext
323 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
324 :param sbox: S-box parameters to use
325 :type sbox: str, SBOXES'es key
326 :param bool mesh: enable key meshing
332 if not data or len(data) % BLOCKSIZE != 0:
333 raise ValueError("Data is not blocksize aligned")
334 if len(data) < 2 * BLOCKSIZE:
335 raise ValueError("There is no either data, or IV in ciphertext")
336 iv = data[:BLOCKSIZE]
338 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
341 (i - BLOCKSIZE) >= MESH_MAX_DATA and
342 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
344 key, _ = meshing(key, iv, sbox=sbox)
345 plaintext.append(strxor(
346 ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
347 data[i - BLOCKSIZE:i],
350 plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
351 return b"".join(plaintext)
354 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
355 """ Counter mode of operation
357 :param bytes key: encryption key
358 :param bytes data: plaintext
359 :param iv: initialization vector
360 :type iv: bytes, BLOCKSIZE length
361 :param sbox: S-box parameters to use
362 :type sbox: str, SBOXES'es key
366 For decryption you use the same function again.
372 raise ValueError("No data supplied")
373 n2, n1 = encrypt(sbox, key, block2ns(iv))
375 for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
376 n1 = addmod(n1, C2, 2 ** 32)
377 n2 = addmod(n2, C1, 2 ** 32 - 1)
378 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
379 return strxor(b"".join(gamma), data)
382 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
386 def meshing(key, iv, sbox=DEFAULT_SBOX):
387 """:rfc:`4357` key meshing
389 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
390 iv = ecb_encrypt(key, iv, sbox=sbox)
394 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
395 """ CFB encryption mode of operation
397 :param bytes key: encryption key
398 :param bytes data: plaintext
399 :param iv: initialization vector
400 :type iv: bytes, BLOCKSIZE length
401 :param sbox: S-box parameters to use
402 :type sbox: str, SBOXES'es key
403 :param bool mesh: enable key meshing
411 raise ValueError("No data supplied")
413 for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
414 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
415 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
416 ciphertext.append(strxor(
417 data[i:i + BLOCKSIZE],
418 ns2block(encrypt(sbox, key, block2ns(iv))),
421 ciphertext.append(strxor(
422 data[i:i + BLOCKSIZE],
423 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
425 return b"".join(ciphertext[1:])
428 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
429 """ CFB decryption mode of operation
431 :param bytes key: encryption key
432 :param bytes data: plaintext
433 :param iv: initialization vector
434 :type iv: bytes, BLOCKSIZE length
435 :param sbox: S-box parameters to use
436 :type sbox: str, SBOXES'es key
437 :param bool mesh: enable key meshing
445 raise ValueError("No data supplied")
448 for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
451 (i - BLOCKSIZE) >= MESH_MAX_DATA and
452 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
454 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
455 plaintext.append(strxor(
456 data[i:i + BLOCKSIZE],
457 ns2block(encrypt(sbox, key, block2ns(iv))),
460 plaintext.append(strxor(
461 data[i:i + BLOCKSIZE],
462 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
464 return b"".join(plaintext)