2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2016 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 pad1
28 from pygost.gost3413 import pad2
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 = "Gost28147_CryptoProParamSetA"
56 "Gost2814789_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 "Gost28147_CryptoProParamSetA": (
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 "Gost28147_CryptoProParamSetB": (
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 "Gost28147_CryptoProParamSetC": (
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 "Gost28147_CryptoProParamSetD": (
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 "GostR3411_94_TestParamSet": (
107 (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
108 (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
109 (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
110 (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
111 (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
112 (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
113 (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
114 (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
116 "GostR3411_94_CryptoProParamSet": (
117 (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15),
118 (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8),
119 (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13),
120 (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3),
121 (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5),
122 (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3),
123 (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11),
124 (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12),
126 "AppliedCryptography": (
127 (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
128 (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
129 (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
130 (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
131 (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
132 (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
133 (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
134 (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
136 "Gost28147_tc26_ParamZ": (
137 (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
138 (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
139 (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
140 (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
141 (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
142 (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
143 (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
144 (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
147 (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
148 (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
149 (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
150 (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
151 (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
152 (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
153 (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
154 (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
160 """ S-box substitution
163 :param _in: 32-bit word
164 :returns: substituted 32-bit word
167 (s[0][(_in >> 0) & 0x0F] << 0) +
168 (s[1][(_in >> 4) & 0x0F] << 4) +
169 (s[2][(_in >> 8) & 0x0F] << 8) +
170 (s[3][(_in >> 12) & 0x0F] << 12) +
171 (s[4][(_in >> 16) & 0x0F] << 16) +
172 (s[5][(_in >> 20) & 0x0F] << 20) +
173 (s[6][(_in >> 24) & 0x0F] << 24) +
174 (s[7][(_in >> 28) & 0x0F] << 28)
179 """ Convert block to N1 and N2 integers
181 data = bytearray(data)
183 data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
184 data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
189 """ Convert N1 and N2 integers to 8-byte block
192 return bytes(bytearray((
193 (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
194 (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
198 def addmod(x, y, mod=2 ** 32):
199 """ Modulo adding of two integers
202 return r if r < mod else r - mod
206 """ 11-bit cyclic shift
208 return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
211 def validate_key(key):
212 if len(key) != KEYSIZE:
213 raise ValueError("Invalid key size")
217 if len(iv) != BLOCKSIZE:
218 raise ValueError("Invalid IV size")
221 def validate_sbox(sbox):
222 if sbox not in SBOXES:
223 raise ValueError("Unknown sbox supplied")
226 def xcrypt(seq, sbox, key, ns):
227 """ Perform full-round single-block operation
229 :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
230 :param sbox: S-box parameters to use
231 :type sbox: str, SBOXES'es key
232 :param bytes key: 256-bit encryption key
233 :param ns: N1 and N2 integers
235 :returns: resulting N1 and N2
244 w[3 + i * 4] << 24 for i in range(8)
248 n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
252 def encrypt(sbox, key, ns):
253 """ Encrypt single block
255 return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
258 def decrypt(sbox, key, ns):
259 """ Decrypt single block
261 return xcrypt(SEQ_DECRYPT, sbox, key, ns)
264 def ecb(key, data, action, sbox=DEFAULT_SBOX):
265 """ ECB mode of operation
267 :param bytes key: encryption key
268 :param data: plaintext
269 :type data: bytes, multiple of BLOCKSIZE
270 :param func action: "encrypt"/"decrypt"
271 :param sbox: S-box parameters to use
272 :type sbox: str, SBOXES'es key
278 if not data or len(data) % BLOCKSIZE != 0:
279 raise ValueError("Data is not blocksize aligned")
281 for i in xrange(0, len(data), BLOCKSIZE):
282 result.append(ns2block(action(
283 sbox, key, block2ns(data[i:i + BLOCKSIZE])
285 return b"".join(result)
288 ecb_encrypt = partial(ecb, action=encrypt)
289 ecb_decrypt = partial(ecb, action=decrypt)
292 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
293 """ CBC encryption mode of operation
295 :param bytes key: encryption key
296 :param bytes data: plaintext
297 :param iv: initialization vector
298 :type iv: bytes, BLOCKSIZE length
299 :type bool pad: perform ISO/IEC 7816-4 padding
300 :param sbox: S-box parameters to use
301 :type sbox: str, SBOXES'es key
305 34.13-2015 padding method 2 is used.
311 raise ValueError("No data supplied")
313 data = pad2(data, BLOCKSIZE)
314 if len(data) % BLOCKSIZE != 0:
315 raise ValueError("Data is not blocksize aligned")
317 for i in xrange(0, len(data), BLOCKSIZE):
318 ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
319 strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
321 return b"".join(ciphertext)
324 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
325 """ CBC decryption mode of operation
327 :param bytes key: encryption key
328 :param bytes data: ciphertext
329 :param iv: initialization vector
330 :type iv: bytes, BLOCKSIZE length
331 :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
332 :param sbox: S-box parameters to use
333 :type sbox: str, SBOXES'es key
339 if not data or len(data) % BLOCKSIZE != 0:
340 raise ValueError("Data is not blocksize aligned")
341 if len(data) < 2 * BLOCKSIZE:
342 raise ValueError("There is no either data, or IV in ciphertext")
344 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
345 plaintext.append(strxor(
346 ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
347 data[i - BLOCKSIZE:i],
350 last_block = bytearray(plaintext[-1])
351 pad_index = last_block.rfind(b"\x80")
353 raise ValueError("Invalid padding")
354 for c in last_block[pad_index + 1:]:
356 raise ValueError("Invalid padding")
357 plaintext[-1] = bytes(last_block[:pad_index])
358 return b"".join(plaintext)
361 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
362 """ Counter mode of operation
364 :param bytes key: encryption key
365 :param bytes data: plaintext
366 :param iv: initialization vector
367 :type iv: bytes, BLOCKSIZE length
368 :param sbox: S-box parameters to use
369 :type sbox: str, SBOXES'es key
373 For decryption you use the same function again.
379 raise ValueError("No data supplied")
380 n2, n1 = encrypt(sbox, key, block2ns(iv))
382 data = pad1(data, BLOCKSIZE)
384 for _ in xrange(0, len(data), BLOCKSIZE):
385 n1 = addmod(n1, C2, 2 ** 32)
386 n2 = addmod(n2, C1, 2 ** 32 - 1)
387 gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
388 return strxor(b"".join(gamma), data[:size])
391 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
395 def meshing(key, iv, sbox=DEFAULT_SBOX):
396 """:rfc:`4357` key meshing
398 key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
399 iv = ecb_encrypt(key, iv, sbox=sbox)
403 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
404 """ CFB encryption mode of operation
406 :param bytes key: encryption key
407 :param bytes data: plaintext
408 :param iv: initialization vector
409 :type iv: bytes, BLOCKSIZE length
410 :param sbox: S-box parameters to use
411 :type sbox: str, SBOXES'es key
412 :param bool mesh: enable key meshing
420 raise ValueError("No data supplied")
422 data = pad1(data, BLOCKSIZE)
424 for i in xrange(0, len(data), BLOCKSIZE):
425 if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
426 key, iv = meshing(key, ciphertext[-1], sbox=sbox)
427 ciphertext.append(strxor(
428 data[i:i + BLOCKSIZE],
429 ns2block(encrypt(sbox, key, block2ns(iv))),
432 ciphertext.append(strxor(
433 data[i:i + BLOCKSIZE],
434 ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
436 return b"".join(ciphertext[1:])[:size]
439 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
440 """ CFB decryption mode of operation
442 :param bytes key: encryption key
443 :param bytes data: plaintext
444 :param iv: initialization vector
445 :type iv: bytes, BLOCKSIZE length
446 :param sbox: S-box parameters to use
447 :type sbox: str, SBOXES'es key
448 :param bool mesh: enable key meshing
456 raise ValueError("No data supplied")
458 data = pad1(data, BLOCKSIZE)
461 for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
464 (i - BLOCKSIZE) >= MESH_MAX_DATA and
465 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
467 key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
468 plaintext.append(strxor(
469 data[i:i + BLOCKSIZE],
470 ns2block(encrypt(sbox, key, block2ns(iv))),
473 plaintext.append(strxor(
474 data[i:i + BLOCKSIZE],
475 ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
477 return b"".join(plaintext)[:size]