]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost341194.py
Unify docstring's leading space presence
[pygost.git] / pygost / gost341194.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 R 34.11-94 hash function
17
18 This is implementation of :rfc:`5831`. Most function and variable names are
19 taken according to specification's terminology.
20 """
21
22 from copy import copy
23 from functools import partial
24 from struct import pack
25
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
36
37
38 DEFAULT_SBOX = "id-GostR3411-94-CryptoProParamSet"
39 BLOCKSIZE = 32
40 C2 = 32 * b"\x00"
41 C3 = hexdec(b"ff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00")
42 C4 = 32 * b"\x00"
43 digest_size = 32
44
45
46 def A(x):
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))
49
50
51 def P(x):
52     return bytearray((
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],
57     ))
58
59
60 def _chi(Y):
61     """Chi function
62
63     This is some kind of LFSR.
64     """
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],
69     )
70     by1, by2, by3, by4, by13, by16, byx = (
71         bytearray(y1), bytearray(y2), bytearray(y3), bytearray(y4),
72         bytearray(y13), bytearray(y16), bytearray(2),
73     )
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]
76     return b"".join((
77         bytes(byx), y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2
78     ))
79
80
81 def _step(hin, m, sbox):
82     """Step function
83
84     H_out = f(H_in, m)
85     """
86     # Generate keys
87     u = hin
88     v = m
89     w = strxor(hin, m)
90     k1 = P(w)
91
92     u = strxor(A(u), C2)
93     v = A(A(v))
94     w = strxor(u, v)
95     k2 = P(w)
96
97     u = strxor(A(u), C3)
98     v = A(A(v))
99     w = strxor(u, v)
100     k3 = P(w)
101
102     u = strxor(A(u), C4)
103     v = A(A(v))
104     w = strxor(u, v)
105     k4 = P(w)
106
107     # Encipher
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))
114
115     # Permute
116     # H_out = chi^61(H_in XOR chi(m XOR chi^12(S)))
117     x = s
118     for _ in xrange(12):
119         x = _chi(x)
120     x = strxor(x, m)
121     x = _chi(x)
122     x = strxor(hin, x)
123     for _ in xrange(61):
124         x = _chi(x)
125     return x
126
127
128 class GOST341194(PEP247):
129     """GOST 34.11-94 big-endian hash
130
131     >>> m = GOST341194()
132     >>> m.update("foo")
133     >>> m.update("bar")
134     >>> m.hexdigest()
135     '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
136     >>> GOST341194("foobar").hexdigest()
137     '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
138     """
139     block_size = BLOCKSIZE
140     digest_size = digest_size
141
142     def __init__(self, data=b"", sbox=DEFAULT_SBOX):
143         """
144         :param bytes data: provide initial data
145         :param bytes sbox: S-box to use
146         """
147         validate_sbox(sbox)
148         self.data = data
149         self.sbox = sbox
150
151     def copy(self):
152         return GOST341194(copy(self.data), self.sbox)
153
154     def update(self, data):
155         """Append data that has to be hashed
156         """
157         self.data += data
158
159     def digest(self):
160         """Get hash of the provided data
161         """
162         _len = 0
163         checksum = 0
164         h = 32 * b"\x00"
165         m = self.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)
174
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)
181         return h[::-1]
182
183
184 def new(data=b"", sbox=DEFAULT_SBOX):
185     return GOST341194(data, sbox)
186
187
188 PBKDF2_HASHER = partial(GOST341194, sbox="id-GostR3411-94-CryptoProParamSet")
189
190
191 def pbkdf2(password, salt, iterations, dklen):
192     return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen)