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 block2ns
27 from pygost.gost28147 import encrypt
28 from pygost.gost28147 import ns2block
29 from pygost.gost28147 import validate_sbox
30 from pygost.iface import PEP247
31 from pygost.pbkdf2 import pbkdf2 as pbkdf2_base
32 from pygost.utils import hexdec
33 from pygost.utils import hexenc
34 from pygost.utils import strxor
35 from pygost.utils import xrange
38 DEFAULT_SBOX = "id-GostR3411-94-CryptoProParamSet"
41 C3 = hexdec(b"ff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00")
47 x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32]
48 return b"".join((strxor(x1, x2), x4, x3, x2))
53 x[0], x[8], x[16], x[24], x[1], x[9], x[17], x[25], x[2],
54 x[10], x[18], x[26], x[3], x[11], x[19], x[27], x[4], x[12],
55 x[20], x[28], x[5], x[13], x[21], x[29], x[6], x[14], x[22],
56 x[30], x[7], x[15], x[23], x[31],
63 This is some kind of LFSR.
65 (y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2, y1) = (
66 Y[0:2], Y[2:4], Y[4:6], Y[6:8], Y[8:10], Y[10:12], Y[12:14],
67 Y[14:16], Y[16:18], Y[18:20], Y[20:22], Y[22:24], Y[24:26],
68 Y[26:28], Y[28:30], Y[30:32],
70 by1, by2, by3, by4, by13, by16, byx = (
71 bytearray(y1), bytearray(y2), bytearray(y3), bytearray(y4),
72 bytearray(y13), bytearray(y16), bytearray(2),
74 byx[0] = by1[0] ^ by2[0] ^ by3[0] ^ by4[0] ^ by13[0] ^ by16[0]
75 byx[1] = by1[1] ^ by2[1] ^ by3[1] ^ by4[1] ^ by13[1] ^ by16[1]
77 bytes(byx), y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2
81 def _step(hin, m, sbox):
108 h4, h3, h2, h1 = hin[0:8], hin[8:16], hin[16:24], hin[24:32]
109 s1 = ns2block(encrypt(sbox, k1[::-1], block2ns(h1[::-1])))[::-1]
110 s2 = ns2block(encrypt(sbox, k2[::-1], block2ns(h2[::-1])))[::-1]
111 s3 = ns2block(encrypt(sbox, k3[::-1], block2ns(h3[::-1])))[::-1]
112 s4 = ns2block(encrypt(sbox, k4[::-1], block2ns(h4[::-1])))[::-1]
113 s = b"".join((s4, s3, s2, s1))
116 # H_out = chi^61(H_in XOR chi(m XOR chi^12(S)))
128 class GOST341194(PEP247):
129 """GOST 34.11-94 big-endian hash
135 '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
136 >>> GOST341194("foobar").hexdigest()
137 '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
139 block_size = BLOCKSIZE
140 digest_size = digest_size
142 def __init__(self, data=b"", sbox=DEFAULT_SBOX):
144 :param bytes data: provide initial data
145 :param bytes sbox: S-box to use
152 return GOST341194(copy(self.data), self.sbox)
154 def update(self, data):
155 """Append data that has to be hashed
160 """Get hash of the provided data
166 for i in xrange(0, len(m), BLOCKSIZE):
167 part = m[i:i + BLOCKSIZE][::-1]
168 _len += len(part) * 8
169 checksum = (checksum + int(hexenc(part), 16)) % (2 ** 256)
170 if len(part) < BLOCKSIZE:
171 part = b"\x00" * (BLOCKSIZE - len(part)) + part
172 h = _step(h, part, self.sbox)
173 h = _step(h, 24 * b"\x00" + pack(">Q", _len), self.sbox)
175 checksum = hex(checksum)[2:].rstrip("L")
176 if len(checksum) % 2 != 0:
177 checksum = "0" + checksum
178 checksum = hexdec(checksum)
179 checksum = b"\x00" * (BLOCKSIZE - len(checksum)) + checksum
180 h = _step(h, checksum, self.sbox)
184 def new(data=b"", sbox=DEFAULT_SBOX):
185 return GOST341194(data, sbox)
188 PBKDF2_HASHER = partial(GOST341194, sbox="id-GostR3411-94-CryptoProParamSet")
191 def pbkdf2(password, salt, iterations, dklen):
192 return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen)