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