]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
78a0e370c1edbafa1185c653359cd9c5b3933b74
[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, mesh=False):
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     :param bool mesh: enable key meshing
295     :returns: ciphertext
296     :rtype: bytes
297
298     34.13-2015 padding method 2 is used.
299     """
300     validate_key(key)
301     validate_iv(iv)
302     validate_sbox(sbox)
303     if not data:
304         raise ValueError("No data supplied")
305     if pad:
306         data = pad2(data, BLOCKSIZE)
307     if len(data) % BLOCKSIZE != 0:
308         raise ValueError("Data is not blocksize aligned")
309     ciphertext = [iv]
310     for i in xrange(0, len(data), BLOCKSIZE):
311         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
312             key, _ = meshing(key, iv, sbox=sbox)
313         ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
314             strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
315         ))))
316     return b"".join(ciphertext)
317
318
319 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
320     """ CBC decryption mode of operation
321
322     :param bytes key: encryption key
323     :param bytes data: ciphertext
324     :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
325     :param sbox: S-box parameters to use
326     :type sbox: str, SBOXES'es key
327     :param bool mesh: enable key meshing
328     :returns: plaintext
329     :rtype: bytes
330     """
331     validate_key(key)
332     validate_sbox(sbox)
333     if not data or len(data) % BLOCKSIZE != 0:
334         raise ValueError("Data is not blocksize aligned")
335     if len(data) < 2 * BLOCKSIZE:
336         raise ValueError("There is no either data, or IV in ciphertext")
337     iv = data[:BLOCKSIZE]
338     plaintext = []
339     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
340         if (
341                 mesh and
342                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
343                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
344         ):
345             key, _ = meshing(key, iv, sbox=sbox)
346         plaintext.append(strxor(
347             ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
348             data[i - BLOCKSIZE:i],
349         ))
350     if pad:
351         plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
352     return b"".join(plaintext)
353
354
355 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
356     """ Counter mode of operation
357
358     :param bytes key: encryption key
359     :param bytes data: plaintext
360     :param iv: initialization vector
361     :type iv: bytes, BLOCKSIZE length
362     :param sbox: S-box parameters to use
363     :type sbox: str, SBOXES'es key
364     :returns: ciphertext
365     :rtype: bytes
366
367     For decryption you use the same function again.
368     """
369     validate_key(key)
370     validate_iv(iv)
371     validate_sbox(sbox)
372     if not data:
373         raise ValueError("No data supplied")
374     n2, n1 = encrypt(sbox, key, block2ns(iv))
375     gamma = []
376     for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
377         n1 = addmod(n1, C2, 2 ** 32)
378         n2 = addmod(n2, C1, 2 ** 32 - 1)
379         gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
380     return strxor(b"".join(gamma), data)
381
382
383 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
384 MESH_MAX_DATA = 1024
385
386
387 def meshing(key, iv, sbox=DEFAULT_SBOX):
388     """:rfc:`4357` key meshing
389     """
390     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
391     iv = ecb_encrypt(key, iv, sbox=sbox)
392     return key, iv
393
394
395 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
396     """ CFB encryption mode of operation
397
398     :param bytes key: encryption key
399     :param bytes data: plaintext
400     :param iv: initialization vector
401     :type iv: bytes, BLOCKSIZE length
402     :param sbox: S-box parameters to use
403     :type sbox: str, SBOXES'es key
404     :param bool mesh: enable key meshing
405     :returns: ciphertext
406     :rtype: bytes
407     """
408     validate_key(key)
409     validate_iv(iv)
410     validate_sbox(sbox)
411     if not data:
412         raise ValueError("No data supplied")
413     ciphertext = [iv]
414     for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
415         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
416             key, iv = meshing(key, ciphertext[-1], sbox=sbox)
417             ciphertext.append(strxor(
418                 data[i:i + BLOCKSIZE],
419                 ns2block(encrypt(sbox, key, block2ns(iv))),
420             ))
421             continue
422         ciphertext.append(strxor(
423             data[i:i + BLOCKSIZE],
424             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
425         ))
426     return b"".join(ciphertext[1:])
427
428
429 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
430     """ CFB decryption mode of operation
431
432     :param bytes key: encryption key
433     :param bytes data: plaintext
434     :param iv: initialization vector
435     :type iv: bytes, BLOCKSIZE length
436     :param sbox: S-box parameters to use
437     :type sbox: str, SBOXES'es key
438     :param bool mesh: enable key meshing
439     :returns: ciphertext
440     :rtype: bytes
441     """
442     validate_key(key)
443     validate_iv(iv)
444     validate_sbox(sbox)
445     if not data:
446         raise ValueError("No data supplied")
447     plaintext = []
448     data = iv + data
449     for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
450         if (
451                 mesh and
452                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
453                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
454         ):
455             key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
456             plaintext.append(strxor(
457                 data[i:i + BLOCKSIZE],
458                 ns2block(encrypt(sbox, key, block2ns(iv))),
459             ))
460             continue
461         plaintext.append(strxor(
462             data[i:i + BLOCKSIZE],
463             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
464         ))
465     return b"".join(plaintext)