]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
b6f3cf49b6fa80a78918e7c411fab8b72f4effbd
[pygost.git] / pygost / gost28147.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2020 Sergey Matveev <stargrave@stargrave.org>
4 #
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.
8 #
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.
13 #
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
17
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
21 data lengths.
22 """
23
24 from functools import partial
25
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
32
33
34 KEYSIZE = 32
35 BLOCKSIZE = 8
36 C1 = 0x01010104
37 C2 = 0x01010101
38
39 # Sequence of K_i S-box applying for encryption and decryption
40 SEQ_ENCRYPT = (
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,
45 )
46 SEQ_DECRYPT = (
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,
51 )
52
53 # S-box parameters
54 DEFAULT_SBOX = "id-Gost28147-89-CryptoPro-A-ParamSet"
55 SBOXES = {
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),
65     ),
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),
75     ),
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),
85     ),
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),
95     ),
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),
105     ),
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),
115     ),
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),
125     ),
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),
135     ),
136     "EACParamSet": (
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),
145     ),
146 }
147 SBOXES["AppliedCryptography"] = SBOXES["id-GostR3411-94-TestParamSet"]
148
149
150 def _K(s, _in):
151     """ S-box substitution
152
153     :param s: S-box
154     :param _in: 32-bit word
155     :returns: substituted 32-bit word
156     """
157     return (
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)
166     )
167
168
169 def block2ns(data):
170     """ Convert block to N1 and N2 integers
171     """
172     data = bytearray(data)
173     return (
174         data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
175         data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
176     )
177
178
179 def ns2block(ns):
180     """ Convert N1 and N2 integers to 8-byte block
181     """
182     n1, n2 = ns
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,
186     )))
187
188
189 def addmod(x, y, mod=2 ** 32):
190     """ Modulo adding of two integers
191     """
192     return (x + y) % mod
193
194
195 def _shift11(x):
196     """ 11-bit cyclic shift
197     """
198     return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
199
200
201 def validate_key(key):
202     if len(key) != KEYSIZE:
203         raise ValueError("Invalid key size")
204
205
206 def validate_iv(iv):
207     if len(iv) != BLOCKSIZE:
208         raise ValueError("Invalid IV size")
209
210
211 def validate_sbox(sbox):
212     if sbox not in SBOXES:
213         raise ValueError("Unknown sbox supplied")
214
215
216 def xcrypt(seq, sbox, key, ns):
217     """ Perform full-round single-block operation
218
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
224     :type ns: (int, int)
225     :returns: resulting N1 and N2
226     :rtype: (int, int)
227     """
228     s = SBOXES[sbox]
229     w = bytearray(key)
230     x = [
231         w[0 + i * 4] |
232         w[1 + i * 4] << 8 |
233         w[2 + i * 4] << 16 |
234         w[3 + i * 4] << 24 for i in range(8)
235     ]
236     n1, n2 = ns
237     for i in seq:
238         n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
239     return n1, n2
240
241
242 def encrypt(sbox, key, ns):
243     """ Encrypt single block
244     """
245     return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
246
247
248 def decrypt(sbox, key, ns):
249     """ Decrypt single block
250     """
251     return xcrypt(SEQ_DECRYPT, sbox, key, ns)
252
253
254 def ecb(key, data, action, sbox=DEFAULT_SBOX):
255     """ ECB mode of operation
256
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
263     :returns: ciphertext
264     :rtype: bytes
265     """
266     validate_key(key)
267     validate_sbox(sbox)
268     if not data or len(data) % BLOCKSIZE != 0:
269         raise ValueError("Data is not blocksize aligned")
270     result = []
271     for i in xrange(0, len(data), BLOCKSIZE):
272         result.append(ns2block(action(
273             sbox, key, block2ns(data[i:i + BLOCKSIZE])
274         )))
275     return b"".join(result)
276
277
278 ecb_encrypt = partial(ecb, action=encrypt)
279 ecb_decrypt = partial(ecb, action=decrypt)
280
281
282 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False):
283     """ CBC encryption mode of operation
284
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
293     :returns: ciphertext
294     :rtype: bytes
295
296     34.13-2015 padding method 2 is used.
297     """
298     validate_key(key)
299     validate_iv(iv)
300     validate_sbox(sbox)
301     if not data:
302         raise ValueError("No data supplied")
303     if pad:
304         data = pad2(data, BLOCKSIZE)
305     if len(data) % BLOCKSIZE != 0:
306         raise ValueError("Data is not blocksize aligned")
307     ciphertext = [iv]
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])
313         ))))
314     return b"".join(ciphertext)
315
316
317 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
318     """ CBC decryption mode of operation
319
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
326     :returns: plaintext
327     :rtype: bytes
328     """
329     validate_key(key)
330     validate_sbox(sbox)
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]
336     plaintext = []
337     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
338         if (
339                 mesh and
340                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
341                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
342         ):
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],
347         ))
348     if pad:
349         plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
350     return b"".join(plaintext)
351
352
353 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
354     """ Counter mode of operation
355
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
362     :returns: ciphertext
363     :rtype: bytes
364
365     For decryption you use the same function again.
366     """
367     validate_key(key)
368     validate_iv(iv)
369     validate_sbox(sbox)
370     if not data:
371         raise ValueError("No data supplied")
372     n2, n1 = encrypt(sbox, key, block2ns(iv))
373     gamma = []
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)
379
380
381 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
382 MESH_MAX_DATA = 1024
383
384
385 def meshing(key, iv, sbox=DEFAULT_SBOX):
386     """:rfc:`4357` key meshing
387     """
388     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
389     iv = ecb_encrypt(key, iv, sbox=sbox)
390     return key, iv
391
392
393 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
394     """ CFB encryption mode of operation
395
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
403     :returns: ciphertext
404     :rtype: bytes
405     """
406     validate_key(key)
407     validate_iv(iv)
408     validate_sbox(sbox)
409     if not data:
410         raise ValueError("No data supplied")
411     ciphertext = [iv]
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))),
418             ))
419             continue
420         ciphertext.append(strxor(
421             data[i:i + BLOCKSIZE],
422             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
423         ))
424     return b"".join(ciphertext[1:])
425
426
427 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
428     """ CFB decryption mode of operation
429
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
437     :returns: ciphertext
438     :rtype: bytes
439     """
440     validate_key(key)
441     validate_iv(iv)
442     validate_sbox(sbox)
443     if not data:
444         raise ValueError("No data supplied")
445     plaintext = []
446     data = iv + data
447     for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
448         if (
449                 mesh and
450                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
451                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
452         ):
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))),
457             ))
458             continue
459         plaintext.append(strxor(
460             data[i:i + BLOCKSIZE],
461             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
462         ))
463     return b"".join(plaintext)