]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost341194.py
0994437a41002b9d44e51e631df3b5de00720762
[pygost.git] / pygost / gost341194.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2018 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 R 34.11-94 hash function
18
19 This is implementation of :rfc:`5831`. Most function and variable names are
20 taken according to specification's terminology.
21 """
22
23 from copy import copy
24 from functools import partial
25 from struct import pack
26
27 from pygost.gost28147 import addmod
28 from pygost.gost28147 import block2ns
29 from pygost.gost28147 import encrypt
30 from pygost.gost28147 import ns2block
31 from pygost.gost28147 import validate_sbox
32 from pygost.iface import PEP247
33 from pygost.pbkdf2 import pbkdf2 as pbkdf2_base
34 from pygost.utils import hexdec
35 from pygost.utils import hexenc
36 from pygost.utils import strxor
37 from pygost.utils import xrange  # pylint: disable=redefined-builtin
38
39
40 DEFAULT_SBOX = "GostR3411_94_TestParamSet"
41 BLOCKSIZE = 32
42 C2 = 32 * b"\x00"
43 C3 = hexdec(b"ff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00")
44 C4 = 32 * b"\x00"
45 digest_size = 32
46
47
48 def A(x):
49     x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32]
50     return b"".join((strxor(x1, x2), x4, x3, x2))
51
52
53 def P(x):
54     return bytearray((
55         x[0], x[8], x[16], x[24], x[1], x[9], x[17], x[25], x[2],
56         x[10], x[18], x[26], x[3], x[11], x[19], x[27], x[4], x[12],
57         x[20], x[28], x[5], x[13], x[21], x[29], x[6], x[14], x[22],
58         x[30], x[7], x[15], x[23], x[31],
59     ))
60
61
62 def _chi(Y):
63     """ Chi function
64
65     This is some kind of LFSR.
66     """
67     (y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2, y1) = (
68         Y[0:2], Y[2:4], Y[4:6], Y[6:8], Y[8:10], Y[10:12], Y[12:14],
69         Y[14:16], Y[16:18], Y[18:20], Y[20:22], Y[22:24], Y[24:26],
70         Y[26:28], Y[28:30], Y[30:32],
71     )
72     by1, by2, by3, by4, by13, by16, byx = (
73         bytearray(y1), bytearray(y2), bytearray(y3), bytearray(y4),
74         bytearray(y13), bytearray(y16), bytearray(2),
75     )
76     byx[0] = by1[0] ^ by2[0] ^ by3[0] ^ by4[0] ^ by13[0] ^ by16[0]
77     byx[1] = by1[1] ^ by2[1] ^ by3[1] ^ by4[1] ^ by13[1] ^ by16[1]
78     return b"".join((
79         bytes(byx), y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2
80     ))
81
82
83 def _step(hin, m, sbox):
84     """ Step function
85
86     H_out = f(H_in, m)
87     """
88     # Generate keys
89     u = hin
90     v = m
91     w = strxor(hin, m)
92     k1 = P(w)
93
94     u = strxor(A(u), C2)
95     v = A(A(v))
96     w = strxor(u, v)
97     k2 = P(w)
98
99     u = strxor(A(u), C3)
100     v = A(A(v))
101     w = strxor(u, v)
102     k3 = P(w)
103
104     u = strxor(A(u), C4)
105     v = A(A(v))
106     w = strxor(u, v)
107     k4 = P(w)
108
109     # Encipher
110     h4, h3, h2, h1 = hin[0:8], hin[8:16], hin[16:24], hin[24:32]
111     s1 = ns2block(encrypt(sbox, k1[::-1], block2ns(h1[::-1])))[::-1]
112     s2 = ns2block(encrypt(sbox, k2[::-1], block2ns(h2[::-1])))[::-1]
113     s3 = ns2block(encrypt(sbox, k3[::-1], block2ns(h3[::-1])))[::-1]
114     s4 = ns2block(encrypt(sbox, k4[::-1], block2ns(h4[::-1])))[::-1]
115     s = b"".join((s4, s3, s2, s1))
116
117     # Permute
118     # H_out = chi^61(H_in XOR chi(m XOR chi^12(S)))
119     x = s
120     for _ in xrange(12):
121         x = _chi(x)
122     x = strxor(x, m)
123     x = _chi(x)
124     x = strxor(hin, x)
125     for _ in xrange(61):
126         x = _chi(x)
127     return x
128
129
130 class GOST341194(PEP247):
131     """ GOST 34.11-94 big-endian hash
132
133     >>> m = GOST341194()
134     >>> m.update("foo")
135     >>> m.update("bar")
136     >>> m.hexdigest()
137     '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
138     >>> GOST341194("foobar").hexdigest()
139     '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af'
140     """
141     block_size = BLOCKSIZE
142     digest_size = digest_size
143
144     def __init__(self, data=b"", sbox=DEFAULT_SBOX):
145         """
146         :param bytes data: provide initial data
147         :param bytes sbox: S-box to use
148         """
149         validate_sbox(sbox)
150         self.data = data
151         self.sbox = sbox
152
153     def copy(self):
154         return GOST341194(copy(self.data), self.sbox)
155
156     def update(self, data):
157         """ Append data that has to be hashed
158         """
159         self.data += data
160
161     def digest(self):
162         """ Get hash of the provided data
163         """
164         _len = 0
165         checksum = 0
166         h = 32 * b"\x00"
167         m = self.data
168         for i in xrange(0, len(m), BLOCKSIZE):
169             part = m[i:i + BLOCKSIZE][::-1]
170             _len += len(part) * 8
171             checksum = addmod(checksum, int(hexenc(part), 16), 2 ** 256)
172             if len(part) < BLOCKSIZE:
173                 part = b"\x00" * (BLOCKSIZE - len(part)) + part
174             h = _step(h, part, self.sbox)
175         h = _step(h, 24 * b"\x00" + pack(">Q", _len), self.sbox)
176
177         checksum = hex(checksum)[2:].rstrip("L")
178         if len(checksum) % 2 != 0:
179             checksum = "0" + checksum
180         checksum = hexdec(checksum)
181         checksum = b"\x00" * (BLOCKSIZE - len(checksum)) + checksum
182         h = _step(h, checksum, self.sbox)
183         return h[::-1]
184
185
186 def new(data=b"", sbox=DEFAULT_SBOX):
187     return GOST341194(data, sbox)
188
189
190 PBKDF2_HASHER = partial(GOST341194, sbox="GostR3411_94_CryptoProParamSet")
191
192
193 def pbkdf2(password, salt, iterations, dklen):
194     return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen)