]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost28147.py
Remove unneeded padding, as only length matters
[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.utils import hexdec
30 from pygost.utils import strxor
31 from pygost.utils import xrange  # pylint: disable=redefined-builtin
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 = "Gost28147_CryptoProParamSetA"
55 SBOXES = {
56     "Gost2814789_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     "Gost28147_CryptoProParamSetA": (
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     "Gost28147_CryptoProParamSetB": (
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     "Gost28147_CryptoProParamSetC": (
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     "Gost28147_CryptoProParamSetD": (
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     "GostR3411_94_TestParamSet": (
107         (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
108         (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
109         (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
110         (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
111         (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
112         (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
113         (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
114         (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
115     ),
116     "GostR3411_94_CryptoProParamSet": (
117         (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15),
118         (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8),
119         (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13),
120         (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3),
121         (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5),
122         (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3),
123         (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11),
124         (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12),
125     ),
126     "AppliedCryptography": (
127         (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3),
128         (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9),
129         (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11),
130         (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3),
131         (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2),
132         (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14),
133         (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12),
134         (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12),
135     ),
136     "Gost28147_tc26_ParamZ": (
137         (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1),
138         (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15),
139         (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0),
140         (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11),
141         (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12),
142         (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0),
143         (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7),
144         (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2),
145     ),
146     "EACParamSet": (
147         (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13),
148         (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2),
149         (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14),
150         (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13),
151         (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2),
152         (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10),
153         (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15),
154         (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1),
155     ),
156 }
157
158
159 def _K(s, _in):
160     """ S-box substitution
161
162     :param s: S-box
163     :param _in: 32-bit word
164     :returns: substituted 32-bit word
165     """
166     return (
167         (s[0][(_in >> 0) & 0x0F] << 0) +
168         (s[1][(_in >> 4) & 0x0F] << 4) +
169         (s[2][(_in >> 8) & 0x0F] << 8) +
170         (s[3][(_in >> 12) & 0x0F] << 12) +
171         (s[4][(_in >> 16) & 0x0F] << 16) +
172         (s[5][(_in >> 20) & 0x0F] << 20) +
173         (s[6][(_in >> 24) & 0x0F] << 24) +
174         (s[7][(_in >> 28) & 0x0F] << 28)
175     )
176
177
178 def block2ns(data):
179     """ Convert block to N1 and N2 integers
180     """
181     data = bytearray(data)
182     return (
183         data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24,
184         data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24,
185     )
186
187
188 def ns2block(ns):
189     """ Convert N1 and N2 integers to 8-byte block
190     """
191     n1, n2 = ns
192     return bytes(bytearray((
193         (n2 >> 0) & 255, (n2 >> 8) & 255, (n2 >> 16) & 255, (n2 >> 24) & 255,
194         (n1 >> 0) & 255, (n1 >> 8) & 255, (n1 >> 16) & 255, (n1 >> 24) & 255,
195     )))
196
197
198 def addmod(x, y, mod=2 ** 32):
199     """ Modulo adding of two integers
200     """
201     r = x + y
202     return r if r < mod else r - mod
203
204
205 def _shift11(x):
206     """ 11-bit cyclic shift
207     """
208     return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1))
209
210
211 def validate_key(key):
212     if len(key) != KEYSIZE:
213         raise ValueError("Invalid key size")
214
215
216 def validate_iv(iv):
217     if len(iv) != BLOCKSIZE:
218         raise ValueError("Invalid IV size")
219
220
221 def validate_sbox(sbox):
222     if sbox not in SBOXES:
223         raise ValueError("Unknown sbox supplied")
224
225
226 def xcrypt(seq, sbox, key, ns):
227     """ Perform full-round single-block operation
228
229     :param seq: sequence of K_i S-box applying (either encrypt or decrypt)
230     :param sbox: S-box parameters to use
231     :type sbox: str, SBOXES'es key
232     :param bytes key: 256-bit encryption key
233     :param ns: N1 and N2 integers
234     :type ns: (int, int)
235     :returns: resulting N1 and N2
236     :rtype: (int, int)
237     """
238     s = SBOXES[sbox]
239     w = bytearray(key)
240     x = [
241         w[0 + i * 4] |
242         w[1 + i * 4] << 8 |
243         w[2 + i * 4] << 16 |
244         w[3 + i * 4] << 24 for i in range(8)
245     ]
246     n1, n2 = ns
247     for i in seq:
248         n1, n2 = _shift11(_K(s, addmod(n1, x[i]))) ^ n2, n1
249     return n1, n2
250
251
252 def encrypt(sbox, key, ns):
253     """ Encrypt single block
254     """
255     return xcrypt(SEQ_ENCRYPT, sbox, key, ns)
256
257
258 def decrypt(sbox, key, ns):
259     """ Decrypt single block
260     """
261     return xcrypt(SEQ_DECRYPT, sbox, key, ns)
262
263
264 def ecb(key, data, action, sbox=DEFAULT_SBOX):
265     """ ECB mode of operation
266
267     :param bytes key: encryption key
268     :param data: plaintext
269     :type data: bytes, multiple of BLOCKSIZE
270     :param func action: "encrypt"/"decrypt"
271     :param sbox: S-box parameters to use
272     :type sbox: str, SBOXES'es key
273     :returns: ciphertext
274     :rtype: bytes
275     """
276     validate_key(key)
277     validate_sbox(sbox)
278     if not data or len(data) % BLOCKSIZE != 0:
279         raise ValueError("Data is not blocksize aligned")
280     result = []
281     for i in xrange(0, len(data), BLOCKSIZE):
282         result.append(ns2block(action(
283             sbox, key, block2ns(data[i:i + BLOCKSIZE])
284         )))
285     return b"".join(result)
286
287
288 ecb_encrypt = partial(ecb, action=encrypt)
289 ecb_decrypt = partial(ecb, action=decrypt)
290
291
292 def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX):
293     """ CBC encryption mode of operation
294
295     :param bytes key: encryption key
296     :param bytes data: plaintext
297     :param iv: initialization vector
298     :type iv: bytes, BLOCKSIZE length
299     :type bool pad: perform ISO/IEC 7816-4 padding
300     :param sbox: S-box parameters to use
301     :type sbox: str, SBOXES'es key
302     :returns: ciphertext
303     :rtype: bytes
304
305     34.13-2015 padding method 2 is used.
306     """
307     validate_key(key)
308     validate_iv(iv)
309     validate_sbox(sbox)
310     if not data:
311         raise ValueError("No data supplied")
312     if pad:
313         data = pad2(data, BLOCKSIZE)
314     if len(data) % BLOCKSIZE != 0:
315         raise ValueError("Data is not blocksize aligned")
316     ciphertext = [iv]
317     for i in xrange(0, len(data), BLOCKSIZE):
318         ciphertext.append(ns2block(encrypt(sbox, key, block2ns(
319             strxor(ciphertext[-1], data[i:i + BLOCKSIZE])
320         ))))
321     return b"".join(ciphertext)
322
323
324 def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX):
325     """ CBC decryption mode of operation
326
327     :param bytes key: encryption key
328     :param bytes data: ciphertext
329     :param iv: initialization vector
330     :type iv: bytes, BLOCKSIZE length
331     :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption
332     :param sbox: S-box parameters to use
333     :type sbox: str, SBOXES'es key
334     :returns: plaintext
335     :rtype: bytes
336     """
337     validate_key(key)
338     validate_sbox(sbox)
339     if not data or len(data) % BLOCKSIZE != 0:
340         raise ValueError("Data is not blocksize aligned")
341     if len(data) < 2 * BLOCKSIZE:
342         raise ValueError("There is no either data, or IV in ciphertext")
343     plaintext = []
344     for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE):
345         plaintext.append(strxor(
346             ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))),
347             data[i - BLOCKSIZE:i],
348         ))
349     if pad:
350         last_block = bytearray(plaintext[-1])
351         pad_index = last_block.rfind(b"\x80")
352         if pad_index == -1:
353             raise ValueError("Invalid padding")
354         for c in last_block[pad_index + 1:]:
355             if c != 0:
356                 raise ValueError("Invalid padding")
357         plaintext[-1] = bytes(last_block[:pad_index])
358     return b"".join(plaintext)
359
360
361 def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX):
362     """ Counter mode of operation
363
364     :param bytes key: encryption key
365     :param bytes data: plaintext
366     :param iv: initialization vector
367     :type iv: bytes, BLOCKSIZE length
368     :param sbox: S-box parameters to use
369     :type sbox: str, SBOXES'es key
370     :returns: ciphertext
371     :rtype: bytes
372
373     For decryption you use the same function again.
374     """
375     validate_key(key)
376     validate_iv(iv)
377     validate_sbox(sbox)
378     if not data:
379         raise ValueError("No data supplied")
380     n2, n1 = encrypt(sbox, key, block2ns(iv))
381     gamma = []
382     for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
383         n1 = addmod(n1, C2, 2 ** 32)
384         n2 = addmod(n2, C1, 2 ** 32 - 1)
385         gamma.append(ns2block(encrypt(sbox, key, (n1, n2))))
386     return strxor(b"".join(gamma), data)
387
388
389 MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B")
390 MESH_MAX_DATA = 1024
391
392
393 def meshing(key, iv, sbox=DEFAULT_SBOX):
394     """:rfc:`4357` key meshing
395     """
396     key = ecb_decrypt(key, MESH_CONST, sbox=sbox)
397     iv = ecb_encrypt(key, iv, sbox=sbox)
398     return key, iv
399
400
401 def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
402     """ CFB encryption mode of operation
403
404     :param bytes key: encryption key
405     :param bytes data: plaintext
406     :param iv: initialization vector
407     :type iv: bytes, BLOCKSIZE length
408     :param sbox: S-box parameters to use
409     :type sbox: str, SBOXES'es key
410     :param bool mesh: enable key meshing
411     :returns: ciphertext
412     :rtype: bytes
413     """
414     validate_key(key)
415     validate_iv(iv)
416     validate_sbox(sbox)
417     if not data:
418         raise ValueError("No data supplied")
419     ciphertext = [iv]
420     for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
421         if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0:
422             key, iv = meshing(key, ciphertext[-1], sbox=sbox)
423             ciphertext.append(strxor(
424                 data[i:i + BLOCKSIZE],
425                 ns2block(encrypt(sbox, key, block2ns(iv))),
426             ))
427             continue
428         ciphertext.append(strxor(
429             data[i:i + BLOCKSIZE],
430             ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))),
431         ))
432     return b"".join(ciphertext[1:])
433
434
435 def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False):
436     """ CFB decryption mode of operation
437
438     :param bytes key: encryption key
439     :param bytes data: plaintext
440     :param iv: initialization vector
441     :type iv: bytes, BLOCKSIZE length
442     :param sbox: S-box parameters to use
443     :type sbox: str, SBOXES'es key
444     :param bool mesh: enable key meshing
445     :returns: ciphertext
446     :rtype: bytes
447     """
448     validate_key(key)
449     validate_iv(iv)
450     validate_sbox(sbox)
451     if not data:
452         raise ValueError("No data supplied")
453     plaintext = []
454     data = iv + data
455     for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE):
456         if (
457                 mesh and
458                 (i - BLOCKSIZE) >= MESH_MAX_DATA and
459                 (i - BLOCKSIZE) % MESH_MAX_DATA == 0
460         ):
461             key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox)
462             plaintext.append(strxor(
463                 data[i:i + BLOCKSIZE],
464                 ns2block(encrypt(sbox, key, block2ns(iv))),
465             ))
466             continue
467         plaintext.append(strxor(
468             data[i:i + BLOCKSIZE],
469             ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))),
470         ))
471     return b"".join(plaintext)