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
196 """ 11-bit cyclic shift
198 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
201 def validate_key(key):
202 if len(key) != KEYSIZE:
203 raise ValueError("Invalid key size")
207 if len(iv) != BLOCKSIZE:
208 raise ValueError("Invalid IV size")
211 def validate_sbox(sbox):
212 if sbox not in SBOXES:
213 raise ValueError("Unknown sbox supplied")
216 def xcrypt(seq, sbox, key, ns):
217 """ Perform full-round single-block operation
219 :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
220 :param sbox: S-box parameters to use
221 :type sbox: str, SBOXES'es key
222 :param bytes key: 256-bit encryption key
223 :param ns: N1 and N2 integers
225 :returns: resulting N1 and N2
234 w[3 + i * 4] << 24 for i in range(8)
238 n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
242 def encrypt(sbox, key, ns):
243 """ Encrypt single block
245 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
248 def decrypt(sbox, key, ns):
249 """ Decrypt single block
251 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
254 def ecb(key, data, action, sbox=DEFAULT_SBOX):
255 """ ECB mode of operation
257 :param bytes key: encryption key
258 :param data: plaintext
259 :type data: bytes, multiple of BLOCKSIZE
260 :param func action: "encrypt"/"decrypt"
261 :param sbox: S-box parameters to use
262 :type sbox: str, SBOXES'es key
268 if not data or len(data) % BLOCKSIZE != 0:
269 raise ValueError("Data is not blocksize aligned")
271 for i in xrange(0, len(data), BLOCKSIZE):
272 result.append(ns2block(action(
273 sbox, key, block2ns(data[i:i + BLOCKSIZE])
275 return b"".join(result)
278 ecb_encrypt = partial(ecb, action=encrypt)
279 ecb_decrypt = partial(ecb, action=decrypt)
282 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False):
283 """ CBC encryption mode of operation
285 :param bytes key: encryption key
286 :param bytes data: plaintext
287 :param iv: initialization vector
288 :type iv: bytes, BLOCKSIZE length
289 :type bool pad: perform ISO/IEC 7816-4 padding
290 :param sbox: S-box parameters to use
291 :type sbox: str, SBOXES'es key
292 :param bool mesh: enable key meshing
296 34.13-2015 padding method 2 is used.
302 raise ValueError("No data supplied")
304 data = pad2(data, BLOCKSIZE)
305 if len(data) % BLOCKSIZE != 0:
306 raise ValueError("Data is not blocksize aligned")
308 for i in xrange(0, len(data), BLOCKSIZE):
309 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
310 key, _ = meshing(key, iv, sbox=sbox)
311 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
312 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
314 return b"".join(ciphertext)
317 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
318 """ CBC decryption mode of operation
320 :param bytes key: encryption key
321 :param bytes data: ciphertext
322 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
323 :param sbox: S-box parameters to use
324 :type sbox: str, SBOXES'es key
325 :param bool mesh: enable key meshing
331 if not data or len(data) % BLOCKSIZE != 0:
332 raise ValueError("Data is not blocksize aligned")
333 if len(data) < 2 * BLOCKSIZE:
334 raise ValueError("There is no either data, or IV in ciphertext")
335 iv = data[:BLOCKSIZE]
337 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
340 (i - BLOCKSIZE) >= MESH_MAX_DATA and
341 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
343 key, _ = meshing(key, iv, sbox=sbox)
344 plaintext.append(strxor(
345 ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
346 data[i - BLOCKSIZE:i],
349 plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
350 return b"".join(plaintext)
353 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
354 """ Counter mode of operation
356 :param bytes key: encryption key
357 :param bytes data: plaintext
358 :param iv: initialization vector
359 :type iv: bytes, BLOCKSIZE length
360 :param sbox: S-box parameters to use
361 :type sbox: str, SBOXES'es key
365 For decryption you use the same function again.
371 raise ValueError("No data supplied")
372 n2, n1 = encrypt(sbox, key, block2ns(iv))
374 for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
375 n1 = addmod(n1, C2, 2 ** 32)
376 n2 = addmod(n2, C1, 2 ** 32 - 1)
377 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
378 return strxor(b"".join(gamma), data)
381 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
385 def meshing(key, iv, sbox=DEFAULT_SBOX):
386 """:rfc:`4357` key meshing
388 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
389 iv = ecb_encrypt(key, iv, sbox=sbox)
393 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
394 """ CFB encryption mode of operation
396 :param bytes key: encryption key
397 :param bytes data: plaintext
398 :param iv: initialization vector
399 :type iv: bytes, BLOCKSIZE length
400 :param sbox: S-box parameters to use
401 :type sbox: str, SBOXES'es key
402 :param bool mesh: enable key meshing
410 raise ValueError("No data supplied")
412 for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
413 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
414 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
415 ciphertext.append(strxor(
416 data[i:i + BLOCKSIZE],
417 ns2block(encrypt(sbox, key, block2ns(iv))),
420 ciphertext.append(strxor(
421 data[i:i + BLOCKSIZE],
422 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
424 return b"".join(ciphertext[1:])
427 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
428 """ CFB decryption mode of operation
430 :param bytes key: encryption key
431 :param bytes data: plaintext
432 :param iv: initialization vector
433 :type iv: bytes, BLOCKSIZE length
434 :param sbox: S-box parameters to use
435 :type sbox: str, SBOXES'es key
436 :param bool mesh: enable key meshing
444 raise ValueError("No data supplied")
447 for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
450 (i - BLOCKSIZE) >= MESH_MAX_DATA and
451 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
453 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
454 plaintext.append(strxor(
455 data[i:i + BLOCKSIZE],
456 ns2block(encrypt(sbox, key, block2ns(iv))),
459 plaintext.append(strxor(
460 data[i:i + BLOCKSIZE],
461 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
463 return b"".join(plaintext)