]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
Fix gost28147.addmod with bigger than modulo values
[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     r = x + y
193     return r if r < mod else r % mod
194
195
196 def _shift11(x):
197     """ 11-bit cyclic shift
198     """
199     return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
200
201
202 def validate_key(key):
203     if len(key) != KEYSIZE:
204         raise ValueError("Invalid key size")
205
206
207 def validate_iv(iv):
208     if len(iv) != BLOCKSIZE:
209         raise ValueError("Invalid IV size")
210
211
212 def validate_sbox(sbox):
213     if sbox not in SBOXES:
214         raise ValueError("Unknown sbox supplied")
215
216
217 def xcrypt(seq, sbox, key, ns):
218     """ Perform full-round single-block operation
219
220     :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
221     :param sbox: S-box parameters to use
222     :type sbox: str, SBOXES'es key
223     :param bytes key: 256-bit encryption key
224     :param ns: N1 and N2 integers
225     :type ns: (int, int)
226     :returns: resulting N1 and N2
227     :rtype: (int, int)
228     """
229     s = SBOXES[sbox]
230     w = bytearray(key)
231     x = [
232         w[0 + i * 4] |
233         w[1 + i * 4] << 8 |
234         w[2 + i * 4] << 16 |
235         w[3 + i * 4] << 24 for i in range(8)
236     ]
237     n1, n2 = ns
238     for i in seq:
239         n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
240     return n1, n2
241
242
243 def encrypt(sbox, key, ns):
244     """ Encrypt single block
245     """
246     return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
247
248
249 def decrypt(sbox, key, ns):
250     """ Decrypt single block
251     """
252     return xcrypt(SEQ_DECRYPT, sbox, key, ns)
253
254
255 def ecb(key, data, action, sbox=DEFAULT_SBOX):
256     """ ECB mode of operation
257
258     :param bytes key: encryption key
259     :param data: plaintext
260     :type data: bytes, multiple of BLOCKSIZE
261     :param func action: "encrypt"/"decrypt"
262     :param sbox: S-box parameters to use
263     :type sbox: str, SBOXES'es key
264     :returns: ciphertext
265     :rtype: bytes
266     """
267     validate_key(key)
268     validate_sbox(sbox)
269     if not data or len(data) % BLOCKSIZE != 0:
270         raise ValueError("Data is not blocksize aligned")
271     result = []
272     for i in xrange(0, len(data), BLOCKSIZE):
273         result.append(ns2block(action(
274             sbox, key, block2ns(data[i:i + BLOCKSIZE])
275         )))
276     return b"".join(result)
277
278
279 ecb_encrypt = partial(ecb, action=encrypt)
280 ecb_decrypt = partial(ecb, action=decrypt)
281
282
283 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False):
284     """ CBC encryption mode of operation
285
286     :param bytes key: encryption key
287     :param bytes data: plaintext
288     :param iv: initialization vector
289     :type iv: bytes, BLOCKSIZE length
290     :type bool pad: perform ISO/IEC 7816-4 padding
291     :param sbox: S-box parameters to use
292     :type sbox: str, SBOXES'es key
293     :param bool mesh: enable key meshing
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         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
311             key, _ = meshing(key, iv, sbox=sbox)
312         ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
313             strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
314         ))))
315     return b"".join(ciphertext)
316
317
318 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False):
319     """ CBC decryption mode of operation
320
321     :param bytes key: encryption key
322     :param bytes data: ciphertext
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     :param bool mesh: enable key meshing
327     :returns: plaintext
328     :rtype: bytes
329     """
330     validate_key(key)
331     validate_sbox(sbox)
332     if not data or len(data) % BLOCKSIZE != 0:
333         raise ValueError("Data is not blocksize aligned")
334     if len(data) < 2 * BLOCKSIZE:
335         raise ValueError("There is no either data, or IV in ciphertext")
336     iv = data[:BLOCKSIZE]
337     plaintext = []
338     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
339         if (
340                 mesh and
341                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
342                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
343         ):
344             key, _ = meshing(key, iv, sbox=sbox)
345         plaintext.append(strxor(
346             ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
347             data[i - BLOCKSIZE:i],
348         ))
349     if pad:
350         plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE)
351     return b"".join(plaintext)
352
353
354 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
355     """ Counter mode of operation
356
357     :param bytes key: encryption key
358     :param bytes data: plaintext
359     :param iv: initialization vector
360     :type iv: bytes, BLOCKSIZE length
361     :param sbox: S-box parameters to use
362     :type sbox: str, SBOXES'es key
363     :returns: ciphertext
364     :rtype: bytes
365
366     For decryption you use the same function again.
367     """
368     validate_key(key)
369     validate_iv(iv)
370     validate_sbox(sbox)
371     if not data:
372         raise ValueError("No data supplied")
373     n2, n1 = encrypt(sbox, key, block2ns(iv))
374     gamma = []
375     for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
376         n1 = addmod(n1, C2, 2 ** 32)
377         n2 = addmod(n2, C1, 2 ** 32 - 1)
378         gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
379     return strxor(b"".join(gamma), data)
380
381
382 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
383 MESH_MAX_DATA = 1024
384
385
386 def meshing(key, iv, sbox=DEFAULT_SBOX):
387     """:rfc:`4357` key meshing
388     """
389     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
390     iv = ecb_encrypt(key, iv, sbox=sbox)
391     return key, iv
392
393
394 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
395     """ CFB encryption mode of operation
396
397     :param bytes key: encryption key
398     :param bytes data: plaintext
399     :param iv: initialization vector
400     :type iv: bytes, BLOCKSIZE length
401     :param sbox: S-box parameters to use
402     :type sbox: str, SBOXES'es key
403     :param bool mesh: enable key meshing
404     :returns: ciphertext
405     :rtype: bytes
406     """
407     validate_key(key)
408     validate_iv(iv)
409     validate_sbox(sbox)
410     if not data:
411         raise ValueError("No data supplied")
412     ciphertext = [iv]
413     for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
414         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
415             key, iv = meshing(key, ciphertext[-1], sbox=sbox)
416             ciphertext.append(strxor(
417                 data[i:i + BLOCKSIZE],
418                 ns2block(encrypt(sbox, key, block2ns(iv))),
419             ))
420             continue
421         ciphertext.append(strxor(
422             data[i:i + BLOCKSIZE],
423             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
424         ))
425     return b"".join(ciphertext[1:])
426
427
428 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
429     """ CFB decryption mode of operation
430
431     :param bytes key: encryption key
432     :param bytes data: plaintext
433     :param iv: initialization vector
434     :type iv: bytes, BLOCKSIZE length
435     :param sbox: S-box parameters to use
436     :type sbox: str, SBOXES'es key
437     :param bool mesh: enable key meshing
438     :returns: ciphertext
439     :rtype: bytes
440     """
441     validate_key(key)
442     validate_iv(iv)
443     validate_sbox(sbox)
444     if not data:
445         raise ValueError("No data supplied")
446     plaintext = []
447     data = iv + data
448     for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
449         if (
450                 mesh and
451                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
452                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
453         ):
454             key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
455             plaintext.append(strxor(
456                 data[i:i + BLOCKSIZE],
457                 ns2block(encrypt(sbox, key, block2ns(iv))),
458             ))
459             continue
460         plaintext.append(strxor(
461             data[i:i + BLOCKSIZE],
462             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
463         ))
464     return b"".join(plaintext)