]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost341194.py
Move hexdigest implementation to common PEP247 class
[pygost.git] / pygost / gost341194.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2016 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 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.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 = "GostR3411_94_TestParamSet"
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         l = 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             l += len(part) * 8
169             checksum = addmod(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", l), 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)