]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
2b39844694a39a8a1a9864e5ea23f25132ef27c9
[pygost.git] / pygost / gost28147.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2017 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     "AppliedCryptography": (
128         (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
129         (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
130         (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
131         (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
132         (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
133         (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
134         (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
135         (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
136     ),
137     "Gost28147_tc26_ParamZ": (
138         (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
139         (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
140         (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
141         (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
142         (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
143         (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
144         (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
145         (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
146     ),
147     "EACParamSet": (
148         (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
149         (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
150         (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
151         (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
152         (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
153         (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
154         (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
155         (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
156     ),
157 }
158
159
160 def _K(s, _in):
161     """ S-box substitution
162
163     :param s: S-box
164     :param _in: 32-bit word
165     :returns: substituted 32-bit word
166     """
167     return (
168         (s[0][(_in >> 0) & 0x0F] << 0) +
169         (s[1][(_in >> 4) & 0x0F] << 4) +
170         (s[2][(_in >> 8) & 0x0F] << 8) +
171         (s[3][(_in >> 12) & 0x0F] << 12) +
172         (s[4][(_in >> 16) & 0x0F] << 16) +
173         (s[5][(_in >> 20) & 0x0F] << 20) +
174         (s[6][(_in >> 24) & 0x0F] << 24) +
175         (s[7][(_in >> 28) & 0x0F] << 28)
176     )
177
178
179 def block2ns(data):
180     """ Convert block to N1 and N2 integers
181     """
182     data = bytearray(data)
183     return (
184         data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
185         data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
186     )
187
188
189 def ns2block(ns):
190     """ Convert N1 and N2 integers to 8-byte block
191     """
192     n1, n2 = ns
193     return bytes(bytearray((
194         (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
195         (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
196     )))
197
198
199 def addmod(x, y, mod=2 ** 32):
200     """ Modulo adding of two integers
201     """
202     r = x + y
203     return r if r < mod else r - mod
204
205
206 def _shift11(x):
207     """ 11-bit cyclic shift
208     """
209     return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
210
211
212 def validate_key(key):
213     if len(key) != KEYSIZE:
214         raise ValueError("Invalid key size")
215
216
217 def validate_iv(iv):
218     if len(iv) != BLOCKSIZE:
219         raise ValueError("Invalid IV size")
220
221
222 def validate_sbox(sbox):
223     if sbox not in SBOXES:
224         raise ValueError("Unknown sbox supplied")
225
226
227 def xcrypt(seq, sbox, key, ns):
228     """ Perform full-round single-block operation
229
230     :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
231     :param sbox: S-box parameters to use
232     :type sbox: str, SBOXES'es key
233     :param bytes key: 256-bit encryption key
234     :param ns: N1 and N2 integers
235     :type ns: (int, int)
236     :returns: resulting N1 and N2
237     :rtype: (int, int)
238     """
239     s = SBOXES[sbox]
240     w = bytearray(key)
241     x = [
242         w[0 + i * 4] |
243         w[1 + i * 4] << 8 |
244         w[2 + i * 4] << 16 |
245         w[3 + i * 4] << 24 for i in range(8)
246     ]
247     n1, n2 = ns
248     for i in seq:
249         n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
250     return n1, n2
251
252
253 def encrypt(sbox, key, ns):
254     """ Encrypt single block
255     """
256     return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
257
258
259 def decrypt(sbox, key, ns):
260     """ Decrypt single block
261     """
262     return xcrypt(SEQ_DECRYPT, sbox, key, ns)
263
264
265 def ecb(key, data, action, sbox=DEFAULT_SBOX):
266     """ ECB mode of operation
267
268     :param bytes key: encryption key
269     :param data: plaintext
270     :type data: bytes, multiple of BLOCKSIZE
271     :param func action: "encrypt"/"decrypt"
272     :param sbox: S-box parameters to use
273     :type sbox: str, SBOXES'es key
274     :returns: ciphertext
275     :rtype: bytes
276     """
277     validate_key(key)
278     validate_sbox(sbox)
279     if not data or len(data) % BLOCKSIZE != 0:
280         raise ValueError("Data is not blocksize aligned")
281     result = []
282     for i in xrange(0, len(data), BLOCKSIZE):
283         result.append(ns2block(action(
284             sbox, key, block2ns(data[i:i + BLOCKSIZE])
285         )))
286     return b"".join(result)
287
288
289 ecb_encrypt = partial(ecb, action=encrypt)
290 ecb_decrypt = partial(ecb, action=decrypt)
291
292
293 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
294     """ CBC encryption mode of operation
295
296     :param bytes key: encryption key
297     :param bytes data: plaintext
298     :param iv: initialization vector
299     :type iv: bytes, BLOCKSIZE length
300     :type bool pad: perform ISO/IEC 7816-4 padding
301     :param sbox: S-box parameters to use
302     :type sbox: str, SBOXES'es key
303     :returns: ciphertext
304     :rtype: bytes
305
306     34.13-2015 padding method 2 is used.
307     """
308     validate_key(key)
309     validate_iv(iv)
310     validate_sbox(sbox)
311     if not data:
312         raise ValueError("No data supplied")
313     if pad:
314         data = pad2(data, BLOCKSIZE)
315     if len(data) % BLOCKSIZE != 0:
316         raise ValueError("Data is not blocksize aligned")
317     ciphertext = [iv]
318     for i in xrange(0, len(data), BLOCKSIZE):
319         ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
320             strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
321         ))))
322     return b"".join(ciphertext)
323
324
325 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
326     """ CBC decryption mode of operation
327
328     :param bytes key: encryption key
329     :param bytes data: ciphertext
330     :param iv: initialization vector
331     :type iv: bytes, BLOCKSIZE length
332     :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
333     :param sbox: S-box parameters to use
334     :type sbox: str, SBOXES'es key
335     :returns: plaintext
336     :rtype: bytes
337     """
338     validate_key(key)
339     validate_sbox(sbox)
340     if not data or len(data) % BLOCKSIZE != 0:
341         raise ValueError("Data is not blocksize aligned")
342     if len(data) < 2 * BLOCKSIZE:
343         raise ValueError("There is no either data, or IV in ciphertext")
344     plaintext = []
345     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
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)