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) & 0xFF, (n2 >> 8) & 0xFF, (n2 >> 16) & 0xFF, (n2 >> 24) & 0xFF,
185 (n1 >> 0) & 0xFF, (n1 >> 8) & 0xFF, (n1 >> 16) & 0xFF, (n1 >> 24) & 0xFF,
190 """11-bit cyclic shift
192 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
195 def validate_key(key):
196 if len(key) != KEYSIZE:
197 raise ValueError("Invalid key size")
201 if len(iv) != BLOCKSIZE:
202 raise ValueError("Invalid IV size")
205 def validate_sbox(sbox):
206 if sbox not in SBOXES:
207 raise ValueError("Unknown sbox supplied")
210 def xcrypt(seq, sbox, key, ns):
211 """Perform full-round single-block operation
213 :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
214 :param sbox: S-box parameters to use
215 :type sbox: str, SBOXES'es key
216 :param bytes key: 256-bit encryption key
217 :param ns: N1 and N2 integers
219 :returns: resulting N1 and N2
228 w[3 + i * 4] << 24 for i in range(8)
232 n1, n2 = _shift11(_K(s, (n1 + x[i]) % (2 ** 32))) ^ n2, n1
236 def encrypt(sbox, key, ns):
237 """Encrypt single block
239 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
242 def decrypt(sbox, key, ns):
243 """Decrypt single block
245 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
248 def ecb(key, data, action, sbox=DEFAULT_SBOX):
249 """ECB mode of operation
251 :param bytes key: encryption key
252 :param data: plaintext
253 :type data: bytes, multiple of BLOCKSIZE
254 :param func action: "encrypt"/"decrypt"
255 :param sbox: S-box parameters to use
256 :type sbox: str, SBOXES'es key
262 if not data or len(data) % BLOCKSIZE != 0:
263 raise ValueError("Data is not blocksize aligned")
265 for i in xrange(0, len(data), BLOCKSIZE):
266 result.append(ns2block(action(
267 sbox, key, block2ns(data[i:i + BLOCKSIZE])
269 return b"".join(result)
272 ecb_encrypt = partial(ecb, action=encrypt)
273 ecb_decrypt = partial(ecb, action=decrypt)
276 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False):
277 """CBC encryption mode of operation
279 :param bytes key: encryption key
280 :param bytes data: plaintext
281 :param iv: initialization vector
282 :type iv: bytes, BLOCKSIZE length
283 :type bool pad: perform ISO/IEC 7816-4 padding
284 :param sbox: S-box parameters to use
285 :type sbox: str, SBOXES'es key
286 :param bool mesh: enable key meshing
290 34.13-2015 padding method 2 is used.
296 raise ValueError("No data supplied")
298 data = pad2(data, BLOCKSIZE)
299 if len(data) % BLOCKSIZE != 0:
300 raise ValueError("Data is not blocksize aligned")
302 for i in xrange(0, len(data), BLOCKSIZE):
303 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
304 key, _ = meshing(key, iv, sbox=sbox)
305 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
306 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
308 return b"".join(ciphertext)
311 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
312 """CBC decryption mode of operation
314 :param bytes key: encryption key
315 :param bytes data: ciphertext
316 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
317 :param sbox: S-box parameters to use
318 :type sbox: str, SBOXES'es key
319 :param bool mesh: enable key meshing
325 if not data or len(data) % BLOCKSIZE != 0:
326 raise ValueError("Data is not blocksize aligned")
327 if len(data) < 2 * BLOCKSIZE:
328 raise ValueError("There is no either data, or IV in ciphertext")
329 iv = data[:BLOCKSIZE]
331 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
334 (i - BLOCKSIZE) >= MESH_MAX_DATA and
335 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
337 key, _ = meshing(key, iv, sbox=sbox)
338 plaintext.append(strxor(
339 ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
340 data[i - BLOCKSIZE:i],
343 plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
344 return b"".join(plaintext)
347 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
348 """Counter mode of operation
350 :param bytes key: encryption key
351 :param bytes data: plaintext
352 :param iv: initialization vector
353 :type iv: bytes, BLOCKSIZE length
354 :param sbox: S-box parameters to use
355 :type sbox: str, SBOXES'es key
359 For decryption you use the same function again.
365 raise ValueError("No data supplied")
366 n2, n1 = encrypt(sbox, key, block2ns(iv))
368 for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
369 n1 = (n1 + C2) % (2 ** 32)
370 n2 = (n2 + C1) % (2 ** 32 - 1)
371 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
372 return strxor(b"".join(gamma), data)
375 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
379 def meshing(key, iv, sbox=DEFAULT_SBOX):
380 """:rfc:`4357` key meshing
382 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
383 iv = ecb_encrypt(key, iv, sbox=sbox)
387 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
388 """CFB encryption mode of operation
390 :param bytes key: encryption key
391 :param bytes data: plaintext
392 :param iv: initialization vector
393 :type iv: bytes, BLOCKSIZE length
394 :param sbox: S-box parameters to use
395 :type sbox: str, SBOXES'es key
396 :param bool mesh: enable key meshing
404 raise ValueError("No data supplied")
406 for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
407 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
408 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
409 ciphertext.append(strxor(
410 data[i:i + BLOCKSIZE],
411 ns2block(encrypt(sbox, key, block2ns(iv))),
414 ciphertext.append(strxor(
415 data[i:i + BLOCKSIZE],
416 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
418 return b"".join(ciphertext[1:])
421 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
422 """CFB decryption mode of operation
424 :param bytes key: encryption key
425 :param bytes data: plaintext
426 :param iv: initialization vector
427 :type iv: bytes, BLOCKSIZE length
428 :param sbox: S-box parameters to use
429 :type sbox: str, SBOXES'es key
430 :param bool mesh: enable key meshing
438 raise ValueError("No data supplied")
441 for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
444 (i - BLOCKSIZE) >= MESH_MAX_DATA and
445 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
447 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
448 plaintext.append(strxor(
449 data[i:i + BLOCKSIZE],
450 ns2block(encrypt(sbox, key, block2ns(iv))),
453 plaintext.append(strxor(
454 data[i:i + BLOCKSIZE],
455 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
457 return b"".join(plaintext)