2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2020 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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 R 34.11-94 hash function
18 This is implementation of :rfc:`5831`. Most function and variable names are
19 taken according to specification's terminology.
23 from functools import partial
24 from struct import pack
26 from pygost.gost28147 import addmod
27 from pygost.gost28147 import block2ns
28 from pygost.gost28147 import encrypt
29 from pygost.gost28147 import ns2block
30 from pygost.gost28147 import validate_sbox
31 from pygost.iface import PEP247
32 from pygost.pbkdf2 import pbkdf2 as pbkdf2_base
33 from pygost.utils import hexdec
34 from pygost.utils import hexenc
35 from pygost.utils import strxor
36 from pygost.utils import xrange # pylint: disable=redefined-builtin
39 DEFAULT_SBOX = "id-GostR3411-94-CryptoProParamSet"
42 C3 = hexdec(b"ff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00")
48 x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32]
49 return b"".join((strxor(x1, x2), x4, x3, x2))
54 x[0], x[8], x[16], x[24], x[1], x[9], x[17], x[25], x[2],
55 x[10], x[18], x[26], x[3], x[11], x[19], x[27], x[4], x[12],
56 x[20], x[28], x[5], x[13], x[21], x[29], x[6], x[14], x[22],
57 x[30], x[7], x[15], x[23], x[31],
64 This is some kind of LFSR.
66 (y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2, y1) = (
67 Y[0:2], Y[2:4], Y[4:6], Y[6:8], Y[8:10], Y[10:12], Y[12:14],
68 Y[14:16], Y[16:18], Y[18:20], Y[20:22], Y[22:24], Y[24:26],
69 Y[26:28], Y[28:30], Y[30:32],
71 by1, by2, by3, by4, by13, by16, byx = (
72 bytearray(y1), bytearray(y2), bytearray(y3), bytearray(y4),
73 bytearray(y13), bytearray(y16), bytearray(2),
75 byx[0] = by1[0] ^ by2[0] ^ by3[0] ^ by4[0] ^ by13[0] ^ by16[0]
76 byx[1] = by1[1] ^ by2[1] ^ by3[1] ^ by4[1] ^ by13[1] ^ by16[1]
78 bytes(byx), y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2
82 def _step(hin, m, sbox):
109 h4, h3, h2, h1 = hin[0:8], hin[8:16], hin[16:24], hin[24:32]
110 s1 = ns2block(encrypt(sbox, k1[::-1], block2ns(h1[::-1])))[::-1]
111 s2 = ns2block(encrypt(sbox, k2[::-1], block2ns(h2[::-1])))[::-1]
112 s3 = ns2block(encrypt(sbox, k3[::-1], block2ns(h3[::-1])))[::-1]
113 s4 = ns2block(encrypt(sbox, k4[::-1], block2ns(h4[::-1])))[::-1]
114 s = b"".join((s4, s3, s2, s1))
117 # H_out = chi^61(H_in XOR chi(m XOR chi^12(S)))
129 class GOST341194(PEP247):
130 """ GOST 34.11-94 big-endian hash
136 '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
137 >>> GOST341194("foobar").hexdigest()
138 '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
140 block_size = BLOCKSIZE
141 digest_size = digest_size
143 def __init__(self, data=b"", sbox=DEFAULT_SBOX):
145 :param bytes data: provide initial data
146 :param bytes sbox: S-box to use
153 return GOST341194(copy(self.data), self.sbox)
155 def update(self, data):
156 """ Append data that has to be hashed
161 """ Get hash of the provided data
167 for i in xrange(0, len(m), BLOCKSIZE):
168 part = m[i:i + BLOCKSIZE][::-1]
169 _len += len(part) * 8
170 checksum = addmod(checksum, int(hexenc(part), 16), 2 ** 256)
171 if len(part) < BLOCKSIZE:
172 part = b"\x00" * (BLOCKSIZE - len(part)) + part
173 h = _step(h, part, self.sbox)
174 h = _step(h, 24 * b"\x00" + pack(">Q", _len), self.sbox)
176 checksum = hex(checksum)[2:].rstrip("L")
177 if len(checksum) % 2 != 0:
178 checksum = "0" + checksum
179 checksum = hexdec(checksum)
180 checksum = b"\x00" * (BLOCKSIZE - len(checksum)) + checksum
181 h = _step(h, checksum, self.sbox)
185 def new(data=b"", sbox=DEFAULT_SBOX):
186 return GOST341194(data, sbox)
189 PBKDF2_HASHER = partial(GOST341194, sbox="id-GostR3411-94-CryptoProParamSet")
192 def pbkdf2(password, salt, iterations, dklen):
193 return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen)