]> Cypherpunks.ru repositories - pygost.git/blob - pygost/gost34112012.py
f1a7acc84a9dc9f1e28377d2ad46548136b3f168
[pygost.git] / pygost / gost34112012.py
1 # coding: utf-8
2 # PyGOST -- Pure Python GOST cryptographic functions library
3 # Copyright (C) 2015-2022 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-2012 (Streebog) hash function common files
17
18 This is implementation of :rfc:`6986`. Most function and variable names are
19 taken according to specification's terminology.
20 """
21
22 from copy import copy
23 from struct import pack
24 from struct import unpack
25
26 from pygost.iface import PEP247
27 from pygost.utils import hexdec
28 from pygost.utils import strxor
29 from pygost.utils import xrange
30
31
32 BLOCKSIZE = 64
33 Pi = bytearray((
34     252, 238, 221,  17, 207, 110,  49,  22, 251, 196, 250,
35     218,  35, 197,   4,  77, 233, 119, 240, 219, 147,  46,
36     153, 186,  23,  54, 241, 187,  20, 205,  95, 193, 249,
37      24, 101,  90, 226,  92, 239,  33, 129,  28,  60,  66,
38     139,   1, 142,  79,   5, 132,   2, 174, 227, 106, 143,
39     160,   6,  11, 237, 152, 127, 212, 211,  31, 235,  52,
40      44,  81, 234, 200,  72, 171, 242,  42, 104, 162, 253,
41      58, 206, 204, 181, 112,  14,  86,   8,  12, 118,  18,
42     191, 114,  19,  71, 156, 183,  93, 135,  21, 161, 150,
43      41,  16, 123, 154, 199, 243, 145, 120, 111, 157, 158,
44     178, 177,  50, 117,  25,  61, 255,  53, 138, 126, 109,
45      84, 198, 128, 195, 189,  13,  87, 223, 245,  36, 169,
46      62, 168,  67, 201, 215, 121, 214, 246, 124,  34, 185,
47       3, 224,  15, 236, 222, 122, 148, 176, 188, 220, 232,
48      40,  80,  78,  51,  10,  74, 167, 151,  96, 115,  30,
49       0,  98,  68,  26, 184,  56, 130, 100, 159,  38,  65,
50     173,  69,  70, 146,  39,  94,  85,  47, 140, 163, 165,
51     125, 105, 213, 149,  59,   7,  88, 179,  64, 134, 172,
52      29, 247,  48,  55, 107, 228, 136, 217, 231, 137, 225,
53      27, 131,  73,  76,  63, 248, 254, 141,  83, 170, 144,
54     202, 216, 133,  97,  32, 113, 103, 164,  45,  43,   9,
55      91, 203, 155,  37, 208, 190, 229, 108,  82,  89, 166,
56     116, 210, 230, 244, 180, 192, 209, 102, 175, 194,  57,
57      75,  99, 182,
58 ))
59
60 A = [unpack(">Q", hexdec(s))[0] for s in (
61     "8e20faa72ba0b470", "47107ddd9b505a38", "ad08b0e0c3282d1c", "d8045870ef14980e",
62     "6c022c38f90a4c07", "3601161cf205268d", "1b8e0b0e798c13c8", "83478b07b2468764",
63     "a011d380818e8f40", "5086e740ce47c920", "2843fd2067adea10", "14aff010bdd87508",
64     "0ad97808d06cb404", "05e23c0468365a02", "8c711e02341b2d01", "46b60f011a83988e",
65     "90dab52a387ae76f", "486dd4151c3dfdb9", "24b86a840e90f0d2", "125c354207487869",
66     "092e94218d243cba", "8a174a9ec8121e5d", "4585254f64090fa0", "accc9ca9328a8950",
67     "9d4df05d5f661451", "c0a878a0a1330aa6", "60543c50de970553", "302a1e286fc58ca7",
68     "18150f14b9ec46dd", "0c84890ad27623e0", "0642ca05693b9f70", "0321658cba93c138",
69     "86275df09ce8aaa8", "439da0784e745554", "afc0503c273aa42a", "d960281e9d1d5215",
70     "e230140fc0802984", "71180a8960409a42", "b60c05ca30204d21", "5b068c651810a89e",
71     "456c34887a3805b9", "ac361a443d1c8cd2", "561b0d22900e4669", "2b838811480723ba",
72     "9bcf4486248d9f5d", "c3e9224312c8c1a0", "effa11af0964ee50", "f97d86d98a327728",
73     "e4fa2054a80b329c", "727d102a548b194e", "39b008152acb8227", "9258048415eb419d",
74     "492c024284fbaec0", "aa16012142f35760", "550b8e9e21f7a530", "a48b474f9ef5dc18",
75     "70a6a56e2440598e", "3853dc371220a247", "1ca76e95091051ad", "0edd37c48a08a6d8",
76     "07e095624504536c", "8d70c431ac02a736", "c83862965601dd1b", "641c314b2b8ee083",
77 )]
78
79 Tau = (
80     0,  8, 16, 24, 32, 40, 48, 56,
81     1,  9, 17, 25, 33, 41, 49, 57,
82     2, 10, 18, 26, 34, 42, 50, 58,
83     3, 11, 19, 27, 35, 43, 51, 59,
84     4, 12, 20, 28, 36, 44, 52, 60,
85     5, 13, 21, 29, 37, 45, 53, 61,
86     6, 14, 22, 30, 38, 46, 54, 62,
87     7, 15, 23, 31, 39, 47, 55, 63,
88 )
89
90 C = [hexdec("".join(s))[::-1] for s in (
91     (
92         "b1085bda1ecadae9ebcb2f81c0657c1f",
93         "2f6a76432e45d016714eb88d7585c4fc",
94         "4b7ce09192676901a2422a08a460d315",
95         "05767436cc744d23dd806559f2a64507",
96     ),
97     (
98         "6fa3b58aa99d2f1a4fe39d460f70b5d7",
99         "f3feea720a232b9861d55e0f16b50131",
100         "9ab5176b12d699585cb561c2db0aa7ca",
101         "55dda21bd7cbcd56e679047021b19bb7",
102     ),
103     (
104         "f574dcac2bce2fc70a39fc286a3d8435",
105         "06f15e5f529c1f8bf2ea7514b1297b7b",
106         "d3e20fe490359eb1c1c93a376062db09",
107         "c2b6f443867adb31991e96f50aba0ab2",
108     ),
109     (
110         "ef1fdfb3e81566d2f948e1a05d71e4dd",
111         "488e857e335c3c7d9d721cad685e353f",
112         "a9d72c82ed03d675d8b71333935203be",
113         "3453eaa193e837f1220cbebc84e3d12e",
114     ),
115     (
116         "4bea6bacad4747999a3f410c6ca92363",
117         "7f151c1f1686104a359e35d7800fffbd",
118         "bfcd1747253af5a3dfff00b723271a16",
119         "7a56a27ea9ea63f5601758fd7c6cfe57",
120     ),
121     (
122         "ae4faeae1d3ad3d96fa4c33b7a3039c0",
123         "2d66c4f95142a46c187f9ab49af08ec6",
124         "cffaa6b71c9ab7b40af21f66c2bec6b6",
125         "bf71c57236904f35fa68407a46647d6e",
126     ),
127     (
128         "f4c70e16eeaac5ec51ac86febf240954",
129         "399ec6c7e6bf87c9d3473e33197a93c9",
130         "0992abc52d822c3706476983284a0504",
131         "3517454ca23c4af38886564d3a14d493",
132     ),
133     (
134         "9b1f5b424d93c9a703e7aa020c6e4141",
135         "4eb7f8719c36de1e89b4443b4ddbc49a",
136         "f4892bcb929b069069d18d2bd1a5c42f",
137         "36acc2355951a8d9a47f0dd4bf02e71e",
138     ),
139     (
140         "378f5a541631229b944c9ad8ec165fde",
141         "3a7d3a1b258942243cd955b7e00d0984",
142         "800a440bdbb2ceb17b2b8a9aa6079c54",
143         "0e38dc92cb1f2a607261445183235adb",
144     ),
145     (
146         "abbedea680056f52382ae548b2e4f3f3",
147         "8941e71cff8a78db1fffe18a1b336103",
148         "9fe76702af69334b7a1e6c303b7652f4",
149         "3698fad1153bb6c374b4c7fb98459ced",
150     ),
151     (
152         "7bcd9ed0efc889fb3002c6cd635afe94",
153         "d8fa6bbbebab07612001802114846679",
154         "8a1d71efea48b9caefbacd1d7d476e98",
155         "dea2594ac06fd85d6bcaa4cd81f32d1b",
156     ),
157     (
158         "378ee767f11631bad21380b00449b17a",
159         "cda43c32bcdf1d77f82012d430219f9b",
160         "5d80ef9d1891cc86e71da4aa88e12852",
161         "faf417d5d9b21b9948bc924af11bd720",
162     ),
163 )]
164
165
166 def _lcache():
167     cache = []
168     for byteN in xrange(8):
169         cache.append([0 for _ in xrange(256)])
170     for byteN in xrange(8):
171         for byteVal in xrange(256):
172             res64 = 0
173             val = byteVal
174             for bitN in xrange(8):
175                 if val & 0x80 > 0:
176                     res64 ^= A[(7 - byteN) * 8 + bitN]
177                 val <<= 1
178             cache[byteN][byteVal] = res64
179     return cache
180
181
182 # Trade memory for CPU for part of L() calculations
183 LCache = _lcache()
184
185
186 def add512bit(a, b):
187     """Add two 512 integers
188     """
189     a = bytearray(a)
190     b = bytearray(b)
191     cb = 0
192     res = bytearray(64)
193     for i in range(64):
194         cb = a[i] + b[i] + (cb >> 8)
195         res[i] = cb & 0xff
196     return res
197
198
199 def g(n, hsh, msg):
200     res = E(LPS(strxor(hsh[:8], pack("<Q", n)) + hsh[8:]), msg)
201     return strxor(strxor(res, hsh), msg)
202
203
204 def E(k, msg):
205     for i in range(12):
206         msg = LPS(strxor(k, msg))
207         k = LPS(strxor(k, C[i]))
208     return strxor(k, msg)
209
210
211 def LPS(data):
212     return L(PS(bytearray(data)))
213
214
215 def PS(data):
216     res = bytearray(BLOCKSIZE)
217     for i in range(BLOCKSIZE):
218         res[Tau[i]] = Pi[data[i]]
219     return res
220
221
222 def L(data):
223     res = []
224     for i in range(8):
225         res64 = 0
226         for j in range(8):
227             res64 ^= LCache[j][data[8 * i + j]]
228         res.append(pack("<Q", res64))
229     return b"".join(res)
230
231
232 class GOST34112012(PEP247):
233     """GOST 34.11-2012 big-endian hash
234
235     >>> m = GOST34112012(digest_size=32)
236     >>> m.update("foo")
237     >>> m.update("bar")
238     >>> m.hexdigest()
239     'e3c9fd89226d93b489a9fe27d686806e24a514e3787bca053c698ec4616ceb78'
240     """
241     block_size = BLOCKSIZE
242
243     def __init__(self, data=b"", digest_size=64):
244         """
245         :param digest_size: hash digest size to compute
246         :type digest_size: 32 or 64 bytes
247         """
248         self._digest_size = digest_size
249         self.hsh = BLOCKSIZE * (b"\x01" if digest_size == 32 else b"\x00")
250         self.chk = bytearray(BLOCKSIZE * b"\x00")
251         self.n = 0
252         self.buf = b""
253         self.update(data)
254
255     def copy(self):
256         obj = GOST34112012()
257         obj._digest_size = self._digest_size
258         obj.hsh = self.hsh
259         obj.chk = copy(self.chk)
260         obj.n = self.n
261         obj.buf = self.buf
262         return obj
263
264     @property
265     def digest_size(self):
266         return self._digest_size
267
268     def _update_block(self, block):
269         self.hsh = g(self.n, self.hsh, block)
270         self.chk = add512bit(self.chk, block)
271         self.n += 512
272
273     def update(self, data):
274         """Update state with the new data
275         """
276         if len(self.buf) > 0:
277             self.buf += data[:BLOCKSIZE - len(self.buf)]
278             data = data[BLOCKSIZE - len(self.buf):]
279             if len(self.buf) == BLOCKSIZE:
280                 self._update_block(self.buf)
281                 self.buf = b""
282         while len(data) >= BLOCKSIZE:
283             self._update_block(data[:BLOCKSIZE])
284             data = data[BLOCKSIZE:]
285         self.buf += data
286
287     def digest(self):
288         """Get hash of the provided data
289         """
290         data = self.buf
291
292         # Padding
293         padblock_size = len(data) * 8
294         data += b"\x01"
295         padlen = BLOCKSIZE - len(data)
296         if padlen != BLOCKSIZE:
297             data += b"\x00" * padlen
298
299         hsh = g(self.n, self.hsh, data)
300         n = self.n + padblock_size
301         chk = add512bit(self.chk, data)
302         hsh = g(0, hsh, pack("<Q", n) + 56 * b"\x00")
303         hsh = g(0, hsh, chk)
304         return hsh[-self._digest_size:]