]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
585d0e7f6866a2215664e06246d591b8664c4e25
[pygost.git] / pygost / gost28147.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2018 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, either version 3 of the License, or
8 # (at your option) any later version.
9 #
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.
14 #
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
18
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
22 data lengths.
23 """
24
25 from functools import partial
26
27 from pygost.gost3413 import pad2
28 from pygost.gost3413 import pad_size
29 from pygost.gost3413 import unpad2
30 from pygost.utils import hexdec
31 from pygost.utils import strxor
32 from pygost.utils import xrange  # pylint: disable=redefined-builtin
33
34
35 KEYSIZE = 32
36 BLOCKSIZE = 8
37 C1 = 0x01010104
38 C2 = 0x01010101
39
40 # Sequence of K_i S-box applying for encryption and decryption
41 SEQ_ENCRYPT = (
42     0, 1, 2, 3, 4, 5, 6, 7,
43     0, 1, 2, 3, 4, 5, 6, 7,
44     0, 1, 2, 3, 4, 5, 6, 7,
45     7, 6, 5, 4, 3, 2, 1, 0,
46 )
47 SEQ_DECRYPT = (
48     0, 1, 2, 3, 4, 5, 6, 7,
49     7, 6, 5, 4, 3, 2, 1, 0,
50     7, 6, 5, 4, 3, 2, 1, 0,
51     7, 6, 5, 4, 3, 2, 1, 0,
52 )
53
54 # S-box parameters
55 DEFAULT_SBOX = "Gost28147_CryptoProParamSetA"
56 SBOXES = {
57     "Gost2814789_TestParamSet": (
58         (4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6),
59         (12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5),
60         (13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11),
61         (14, 9, 11, 2, 5, 15, 7, 1, 0, 13, 12, 6, 10, 4, 3, 8),
62         (3, 14, 5, 9, 6, 8, 0, 13, 10, 11, 7, 12, 2, 1, 15, 4),
63         (8, 15, 6, 11, 1, 9, 12, 5, 13, 3, 7, 10, 0, 14, 2, 4),
64         (9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13),
65         (12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8),
66     ),
67     "Gost28147_CryptoProParamSetA": (
68         (9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5),
69         (3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1),
70         (14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9),
71         (14, 7, 10, 12, 13, 1, 3, 9, 0, 2, 11, 4, 15, 8, 5, 6),
72         (11, 5, 1, 9, 8, 13, 15, 0, 14, 4, 2, 3, 12, 7, 10, 6),
73         (3, 10, 13, 12, 1, 2, 0, 11, 7, 5, 9, 4, 8, 15, 14, 6),
74         (1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14),
75         (11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4),
76     ),
77     "Gost28147_CryptoProParamSetB": (
78         (8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15),
79         (0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14),
80         (14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4),
81         (7, 5, 0, 13, 11, 6, 1, 2, 3, 10, 12, 15, 4, 14, 9, 8),
82         (2, 7, 12, 15, 9, 5, 10, 11, 1, 4, 0, 13, 6, 8, 14, 3),
83         (8, 3, 2, 6, 4, 13, 14, 11, 12, 1, 7, 15, 10, 0, 9, 5),
84         (5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14),
85         (0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12),
86     ),
87     "Gost28147_CryptoProParamSetC": (
88         (1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3),
89         (0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3),
90         (8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11),
91         (3, 6, 0, 1, 5, 13, 10, 8, 11, 2, 9, 7, 14, 15, 12, 4),
92         (8, 13, 11, 0, 4, 5, 1, 2, 9, 3, 12, 14, 6, 15, 10, 7),
93         (12, 9, 11, 1, 8, 14, 2, 4, 7, 3, 6, 5, 10, 0, 15, 13),
94         (10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7),
95         (7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8),
96     ),
97     "Gost28147_CryptoProParamSetD": (
98         (15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3),
99         (11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1),
100         (1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2),
101         (1, 5, 14, 12, 10, 7, 0, 13, 6, 2, 11, 4, 9, 3, 15, 8),
102         (0, 12, 8, 9, 13, 2, 10, 11, 7, 3, 6, 5, 4, 14, 15, 1),
103         (8, 0, 15, 3, 2, 5, 14, 11, 1, 10, 4, 7, 12, 9, 13, 6),
104         (3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7),
105         (1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14),
106     ),
107     "GostR3411_94_TestParamSet": (
108         (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
109         (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
110         (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
111         (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
112         (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
113         (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
114         (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
115         (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
116     ),
117     "GostR3411_94_CryptoProParamSet": (
118         (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15),
119         (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8),
120         (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13),
121         (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3),
122         (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5),
123         (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3),
124         (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11),
125         (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12),
126     ),
127     "Gost28147_tc26_ParamZ": (
128         (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
129         (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
130         (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
131         (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
132         (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
133         (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
134         (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
135         (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
136     ),
137     "EACParamSet": (
138         (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
139         (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
140         (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
141         (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
142         (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
143         (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
144         (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
145         (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
146     ),
147 }
148 SBOXES["AppliedCryptography"] = SBOXES["GostR3411_94_TestParamSet"]
149
150
151 def _K(s, _in):
152     """ S-box substitution
153
154     :param s: S-box
155     :param _in: 32-bit word
156     :returns: substituted 32-bit word
157     """
158     return (
159         (s[0][(_in >> 0) & 0x0F] << 0) +
160         (s[1][(_in >> 4) & 0x0F] << 4) +
161         (s[2][(_in >> 8) & 0x0F] << 8) +
162         (s[3][(_in >> 12) & 0x0F] << 12) +
163         (s[4][(_in >> 16) & 0x0F] << 16) +
164         (s[5][(_in >> 20) & 0x0F] << 20) +
165         (s[6][(_in >> 24) & 0x0F] << 24) +
166         (s[7][(_in >> 28) & 0x0F] << 28)
167     )
168
169
170 def block2ns(data):
171     """ Convert block to N1 and N2 integers
172     """
173     data = bytearray(data)
174     return (
175         data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
176         data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
177     )
178
179
180 def ns2block(ns):
181     """ Convert N1 and N2 integers to 8-byte block
182     """
183     n1, n2 = ns
184     return bytes(bytearray((
185         (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
186         (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
187     )))
188
189
190 def addmod(x, y, mod=2 ** 32):
191     """ Modulo adding of two integers
192     """
193     r = x + y
194     return r if r < mod else r - mod
195
196
197 def _shift11(x):
198     """ 11-bit cyclic shift
199     """
200     return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
201
202
203 def validate_key(key):
204     if len(key) != KEYSIZE:
205         raise ValueError("Invalid key size")
206
207
208 def validate_iv(iv):
209     if len(iv) != BLOCKSIZE:
210         raise ValueError("Invalid IV size")
211
212
213 def validate_sbox(sbox):
214     if sbox not in SBOXES:
215         raise ValueError("Unknown sbox supplied")
216
217
218 def xcrypt(seq, sbox, key, ns):
219     """ Perform full-round single-block operation
220
221     :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
222     :param sbox: S-box parameters to use
223     :type sbox: str, SBOXES'es key
224     :param bytes key: 256-bit encryption key
225     :param ns: N1 and N2 integers
226     :type ns: (int, int)
227     :returns: resulting N1 and N2
228     :rtype: (int, int)
229     """
230     s = SBOXES[sbox]
231     w = bytearray(key)
232     x = [
233         w[0 + i * 4] |
234         w[1 + i * 4] << 8 |
235         w[2 + i * 4] << 16 |
236         w[3 + i * 4] << 24 for i in range(8)
237     ]
238     n1, n2 = ns
239     for i in seq:
240         n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
241     return n1, n2
242
243
244 def encrypt(sbox, key, ns):
245     """ Encrypt single block
246     """
247     return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
248
249
250 def decrypt(sbox, key, ns):
251     """ Decrypt single block
252     """
253     return xcrypt(SEQ_DECRYPT, sbox, key, ns)
254
255
256 def ecb(key, data, action, sbox=DEFAULT_SBOX):
257     """ ECB mode of operation
258
259     :param bytes key: encryption key
260     :param data: plaintext
261     :type data: bytes, multiple of BLOCKSIZE
262     :param func action: "encrypt"/"decrypt"
263     :param sbox: S-box parameters to use
264     :type sbox: str, SBOXES'es key
265     :returns: ciphertext
266     :rtype: bytes
267     """
268     validate_key(key)
269     validate_sbox(sbox)
270     if not data or len(data) % BLOCKSIZE != 0:
271         raise ValueError("Data is not blocksize aligned")
272     result = []
273     for i in xrange(0, len(data), BLOCKSIZE):
274         result.append(ns2block(action(
275             sbox, key, block2ns(data[i:i + BLOCKSIZE])
276         )))
277     return b"".join(result)
278
279
280 ecb_encrypt = partial(ecb, action=encrypt)
281 ecb_decrypt = partial(ecb, action=decrypt)
282
283
284 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
285     """ CBC encryption mode of operation
286
287     :param bytes key: encryption key
288     :param bytes data: plaintext
289     :param iv: initialization vector
290     :type iv: bytes, BLOCKSIZE length
291     :type bool pad: perform ISO/IEC 7816-4 padding
292     :param sbox: S-box parameters to use
293     :type sbox: str, SBOXES'es key
294     :returns: ciphertext
295     :rtype: bytes
296
297     34.13-2015 padding method 2 is used.
298     """
299     validate_key(key)
300     validate_iv(iv)
301     validate_sbox(sbox)
302     if not data:
303         raise ValueError("No data supplied")
304     if pad:
305         data = pad2(data, BLOCKSIZE)
306     if len(data) % BLOCKSIZE != 0:
307         raise ValueError("Data is not blocksize aligned")
308     ciphertext = [iv]
309     for i in xrange(0, len(data), BLOCKSIZE):
310         ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
311             strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
312         ))))
313     return b"".join(ciphertext)
314
315
316 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
317     """ CBC decryption mode of operation
318
319     :param bytes key: encryption key
320     :param bytes data: ciphertext
321     :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
322     :param sbox: S-box parameters to use
323     :type sbox: str, SBOXES'es key
324     :returns: plaintext
325     :rtype: bytes
326     """
327     validate_key(key)
328     validate_sbox(sbox)
329     if not data or len(data) % BLOCKSIZE != 0:
330         raise ValueError("Data is not blocksize aligned")
331     if len(data) < 2 * BLOCKSIZE:
332         raise ValueError("There is no either data, or IV in ciphertext")
333     plaintext = []
334     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
335         plaintext.append(strxor(
336             ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
337             data[i - BLOCKSIZE:i],
338         ))
339     if pad:
340         plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
341     return b"".join(plaintext)
342
343
344 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
345     """ Counter mode of operation
346
347     :param bytes key: encryption key
348     :param bytes data: plaintext
349     :param iv: initialization vector
350     :type iv: bytes, BLOCKSIZE length
351     :param sbox: S-box parameters to use
352     :type sbox: str, SBOXES'es key
353     :returns: ciphertext
354     :rtype: bytes
355
356     For decryption you use the same function again.
357     """
358     validate_key(key)
359     validate_iv(iv)
360     validate_sbox(sbox)
361     if not data:
362         raise ValueError("No data supplied")
363     n2, n1 = encrypt(sbox, key, block2ns(iv))
364     gamma = []
365     for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
366         n1 = addmod(n1, C2, 2 ** 32)
367         n2 = addmod(n2, C1, 2 ** 32 - 1)
368         gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
369     return strxor(b"".join(gamma), data)
370
371
372 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
373 MESH_MAX_DATA = 1024
374
375
376 def meshing(key, iv, sbox=DEFAULT_SBOX):
377     """:rfc:`4357` key meshing
378     """
379     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
380     iv = ecb_encrypt(key, iv, sbox=sbox)
381     return key, iv
382
383
384 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
385     """ CFB encryption mode of operation
386
387     :param bytes key: encryption key
388     :param bytes data: plaintext
389     :param iv: initialization vector
390     :type iv: bytes, BLOCKSIZE length
391     :param sbox: S-box parameters to use
392     :type sbox: str, SBOXES'es key
393     :param bool mesh: enable key meshing
394     :returns: ciphertext
395     :rtype: bytes
396     """
397     validate_key(key)
398     validate_iv(iv)
399     validate_sbox(sbox)
400     if not data:
401         raise ValueError("No data supplied")
402     ciphertext = [iv]
403     for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
404         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
405             key, iv = meshing(key, ciphertext[-1], sbox=sbox)
406             ciphertext.append(strxor(
407                 data[i:i + BLOCKSIZE],
408                 ns2block(encrypt(sbox, key, block2ns(iv))),
409             ))
410             continue
411         ciphertext.append(strxor(
412             data[i:i + BLOCKSIZE],
413             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
414         ))
415     return b"".join(ciphertext[1:])
416
417
418 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
419     """ CFB decryption mode of operation
420
421     :param bytes key: encryption key
422     :param bytes data: plaintext
423     :param iv: initialization vector
424     :type iv: bytes, BLOCKSIZE length
425     :param sbox: S-box parameters to use
426     :type sbox: str, SBOXES'es key
427     :param bool mesh: enable key meshing
428     :returns: ciphertext
429     :rtype: bytes
430     """
431     validate_key(key)
432     validate_iv(iv)
433     validate_sbox(sbox)
434     if not data:
435         raise ValueError("No data supplied")
436     plaintext = []
437     data = iv + data
438     for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
439         if (
440                 mesh and
441                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
442                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
443         ):
444             key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
445             plaintext.append(strxor(
446                 data[i:i + BLOCKSIZE],
447                 ns2block(encrypt(sbox, key, block2ns(iv))),
448             ))
449             continue
450         plaintext.append(strxor(
451             data[i:i + BLOCKSIZE],
452             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
453         ))
454     return b"".join(plaintext)