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