]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
Raise copyright years
[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     :param iv: initialization vector
322     :type iv: bytes, BLOCKSIZE length
323     :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
324     :param sbox: S-box parameters to use
325     :type sbox: str, SBOXES'es key
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     plaintext = []
336     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
337         plaintext.append(strxor(
338             ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
339             data[i - BLOCKSIZE:i],
340         ))
341     if pad:
342         plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
343     return b"".join(plaintext)
344
345
346 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
347     """ Counter mode of operation
348
349     :param bytes key: encryption key
350     :param bytes data: plaintext
351     :param iv: initialization vector
352     :type iv: bytes, BLOCKSIZE length
353     :param sbox: S-box parameters to use
354     :type sbox: str, SBOXES'es key
355     :returns: ciphertext
356     :rtype: bytes
357
358     For decryption you use the same function again.
359     """
360     validate_key(key)
361     validate_iv(iv)
362     validate_sbox(sbox)
363     if not data:
364         raise ValueError("No data supplied")
365     n2, n1 = encrypt(sbox, key, block2ns(iv))
366     gamma = []
367     for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
368         n1 = addmod(n1, C2, 2 ** 32)
369         n2 = addmod(n2, C1, 2 ** 32 - 1)
370         gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
371     return strxor(b"".join(gamma), data)
372
373
374 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
375 MESH_MAX_DATA = 1024
376
377
378 def meshing(key, iv, sbox=DEFAULT_SBOX):
379     """:rfc:`4357` key meshing
380     """
381     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
382     iv = ecb_encrypt(key, iv, sbox=sbox)
383     return key, iv
384
385
386 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
387     """ CFB encryption mode of operation
388
389     :param bytes key: encryption key
390     :param bytes data: plaintext
391     :param iv: initialization vector
392     :type iv: bytes, BLOCKSIZE length
393     :param sbox: S-box parameters to use
394     :type sbox: str, SBOXES'es key
395     :param bool mesh: enable key meshing
396     :returns: ciphertext
397     :rtype: bytes
398     """
399     validate_key(key)
400     validate_iv(iv)
401     validate_sbox(sbox)
402     if not data:
403         raise ValueError("No data supplied")
404     ciphertext = [iv]
405     for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
406         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
407             key, iv = meshing(key, ciphertext[-1], sbox=sbox)
408             ciphertext.append(strxor(
409                 data[i:i + BLOCKSIZE],
410                 ns2block(encrypt(sbox, key, block2ns(iv))),
411             ))
412             continue
413         ciphertext.append(strxor(
414             data[i:i + BLOCKSIZE],
415             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
416         ))
417     return b"".join(ciphertext[1:])
418
419
420 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
421     """ CFB decryption mode of operation
422
423     :param bytes key: encryption key
424     :param bytes data: plaintext
425     :param iv: initialization vector
426     :type iv: bytes, BLOCKSIZE length
427     :param sbox: S-box parameters to use
428     :type sbox: str, SBOXES'es key
429     :param bool mesh: enable key meshing
430     :returns: ciphertext
431     :rtype: bytes
432     """
433     validate_key(key)
434     validate_iv(iv)
435     validate_sbox(sbox)
436     if not data:
437         raise ValueError("No data supplied")
438     plaintext = []
439     data = iv + data
440     for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
441         if (
442                 mesh and
443                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
444                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
445         ):
446             key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
447             plaintext.append(strxor(
448                 data[i:i + BLOCKSIZE],
449                 ns2block(encrypt(sbox, key, block2ns(iv))),
450             ))
451             continue
452         plaintext.append(strxor(
453             data[i:i + BLOCKSIZE],
454             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
455         ))
456     return b"".join(plaintext)