xref: /PHP-8.4/ext/hash/hash_gost.c (revision 01b3fc03)
1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Michael Wallner <mike@php.net>                              |
14   |          Sara Golemon <pollita@php.net>                              |
15   +----------------------------------------------------------------------+
16 */
17 
18 #include "php_hash.h"
19 #include "php_hash_gost.h"
20 #include "php_hash_gost_tables.h"
21 
22 /* {{{ Gost()
23  * derived from gost_compress() by Markku-Juhani Saarinen <mjos@ssh.fi>
24  */
25 
26 #define round(tables, k1, k2) \
27 	t = (k1) + r; \
28 	l ^= tables[0][t & 0xff] ^ tables[1][(t >> 8) & 0xff] ^ \
29 		tables[2][(t >> 16) & 0xff] ^ tables[3][t >> 24]; \
30 	t = (k2) + l; \
31 	r ^= tables[0][t & 0xff] ^ tables[1][(t >> 8) & 0xff] ^ \
32 		tables[2][(t >> 16) & 0xff] ^ tables[3][t >> 24];
33 
34 #define R(tables, key, h, i, t, l, r) \
35 	r = h[i]; \
36 	l = h[i + 1]; \
37 	round(tables, key[0], key[1]) \
38 	round(tables, key[2], key[3]) \
39 	round(tables, key[4], key[5]) \
40 	round(tables, key[6], key[7]) \
41 	round(tables, key[0], key[1]) \
42 	round(tables, key[2], key[3]) \
43 	round(tables, key[4], key[5]) \
44 	round(tables, key[6], key[7]) \
45 	round(tables, key[0], key[1]) \
46 	round(tables, key[2], key[3]) \
47 	round(tables, key[4], key[5]) \
48 	round(tables, key[6], key[7]) \
49 	round(tables, key[7], key[6]) \
50 	round(tables, key[5], key[4]) \
51 	round(tables, key[3], key[2]) \
52 	round(tables, key[1], key[0]) \
53 	t = r; \
54 	r = l; \
55 	l = t; \
56 
57 #define X(w, u, v) \
58 	w[0] = u[0] ^ v[0]; \
59 	w[1] = u[1] ^ v[1]; \
60 	w[2] = u[2] ^ v[2]; \
61 	w[3] = u[3] ^ v[3]; \
62 	w[4] = u[4] ^ v[4]; \
63 	w[5] = u[5] ^ v[5]; \
64 	w[6] = u[6] ^ v[6]; \
65 	w[7] = u[7] ^ v[7];
66 
67 #define P(key, w) \
68 	key[0] = (w[0]  & 0x000000ff) | ((w[2] & 0x000000ff) << 8) | \
69 		((w[4] & 0x000000ff) << 16) | ((w[6] & 0x000000ff) << 24); \
70 	key[1] = ((w[0] & 0x0000ff00) >> 8)  | (w[2]  & 0x0000ff00) | \
71 		((w[4] & 0x0000ff00) << 8) | ((w[6] & 0x0000ff00) << 16); \
72 	key[2] = ((w[0] & 0x00ff0000) >> 16) | ((w[2] & 0x00ff0000) >> 8) | \
73 		(w[4] & 0x00ff0000) | ((w[6] & 0x00ff0000) << 8); \
74 	key[3] = ((w[0] & 0xff000000) >> 24) | ((w[2] & 0xff000000) >> 16) | \
75 		((w[4] & 0xff000000) >> 8) | (w[6] & 0xff000000); \
76 	key[4] = (w[1] & 0x000000ff) | ((w[3] & 0x000000ff) << 8) | \
77 		((w[5] & 0x000000ff) << 16) | ((w[7] & 0x000000ff) << 24); \
78 	key[5] = ((w[1] & 0x0000ff00) >> 8) | (w[3]  & 0x0000ff00) | \
79 		((w[5] & 0x0000ff00) << 8) | ((w[7] & 0x0000ff00) << 16); \
80 	key[6] = ((w[1] & 0x00ff0000) >> 16) | ((w[3] & 0x00ff0000) >> 8) | \
81 		(w[5] & 0x00ff0000) | ((w[7] & 0x00ff0000) << 8); \
82 	key[7] = ((w[1] & 0xff000000) >> 24) | ((w[3] & 0xff000000) >> 16) | \
83 		((w[5] & 0xff000000) >> 8) | (w[7] & 0xff000000);
84 
85 #define A(x, l, r) \
86 	l = x[0] ^ x[2]; \
87 	r = x[1] ^ x[3]; \
88 	x[0] = x[2]; \
89 	x[1] = x[3]; \
90 	x[2] = x[4]; \
91 	x[3] = x[5]; \
92 	x[4] = x[6]; \
93 	x[5] = x[7]; \
94 	x[6] = l; \
95 	x[7] = r;
96 
97 #define AA(x, l, r) \
98 	l = x[0]; \
99 	r = x[2]; \
100 	x[0] = x[4]; \
101 	x[2] = x[6]; \
102 	x[4] = l ^ r; \
103 	x[6] = x[0] ^ r; \
104 	l = x[1]; \
105 	r = x[3]; \
106 	x[1] = x[5]; \
107 	x[3] = x[7]; \
108 	x[5] = l ^ r; \
109 	x[7] = x[1] ^ r;
110 
111 #define C(x) \
112 	x[0] ^= 0xff00ff00; \
113 	x[1] ^= 0xff00ff00; \
114 	x[2] ^= 0x00ff00ff; \
115 	x[3] ^= 0x00ff00ff; \
116 	x[4] ^= 0x00ffff00; \
117 	x[5] ^= 0xff0000ff; \
118 	x[6] ^= 0x000000ff; \
119 	x[7] ^= 0xff00ffff;
120 
121 #define S(s, l, r) \
122 		s[i] = r; \
123 		s[i + 1] = l;
124 
125 #define SHIFT12(u, m, s) \
126 	u[0] = m[0] ^ s[6]; \
127 	u[1] = m[1] ^ s[7]; \
128 	u[2] = m[2] ^ (s[0] << 16) ^ (s[0] >> 16) ^ (s[0] & 0xffff) ^ \
129 		(s[1] & 0xffff) ^ (s[1] >> 16) ^ (s[2] << 16) ^ s[6] ^ (s[6] << 16) ^ \
130 		(s[7] & 0xffff0000) ^ (s[7] >> 16); \
131 	u[3] = m[3] ^ (s[0] & 0xffff) ^ (s[0] << 16) ^ (s[1] & 0xffff) ^ \
132 		(s[1] << 16) ^ (s[1] >> 16) ^ (s[2] << 16) ^ (s[2] >> 16) ^ \
133 		(s[3] << 16) ^ s[6] ^ (s[6] << 16) ^ (s[6] >> 16) ^ (s[7] & 0xffff) ^ \
134 		(s[7] << 16) ^ (s[7] >> 16); \
135 	u[4] = m[4] ^ \
136 		(s[0] & 0xffff0000) ^ (s[0] << 16) ^ (s[0] >> 16) ^ \
137 		(s[1] & 0xffff0000) ^ (s[1] >> 16) ^ (s[2] << 16) ^ (s[2] >> 16) ^ \
138 		(s[3] << 16) ^ (s[3] >> 16) ^ (s[4] << 16) ^ (s[6] << 16) ^ \
139 		(s[6] >> 16) ^(s[7] & 0xffff) ^ (s[7] << 16) ^ (s[7] >> 16); \
140 	u[5] = m[5] ^ (s[0] << 16) ^ (s[0] >> 16) ^ (s[0] & 0xffff0000) ^ \
141 		(s[1] & 0xffff) ^ s[2] ^ (s[2] >> 16) ^ (s[3] << 16) ^ (s[3] >> 16) ^ \
142 		(s[4] << 16) ^ (s[4] >> 16) ^ (s[5] << 16) ^  (s[6] << 16) ^ \
143 		(s[6] >> 16) ^ (s[7] & 0xffff0000) ^ (s[7] << 16) ^ (s[7] >> 16); \
144 	u[6] = m[6] ^ s[0] ^ (s[1] >> 16) ^ (s[2] << 16) ^ s[3] ^ (s[3] >> 16) ^ \
145 		(s[4] << 16) ^ (s[4] >> 16) ^ (s[5] << 16) ^ (s[5] >> 16) ^ s[6] ^ \
146 		(s[6] << 16) ^ (s[6] >> 16) ^ (s[7] << 16); \
147 	u[7] = m[7] ^ (s[0] & 0xffff0000) ^ (s[0] << 16) ^ (s[1] & 0xffff) ^ \
148 		(s[1] << 16) ^ (s[2] >> 16) ^ (s[3] << 16) ^ s[4] ^ (s[4] >> 16) ^ \
149 		(s[5] << 16) ^ (s[5] >> 16) ^ (s[6] >> 16) ^ (s[7] & 0xffff) ^ \
150 		(s[7] << 16) ^ (s[7] >> 16);
151 
152 #define SHIFT16(h, v, u) \
153 	v[0] = h[0] ^ (u[1] << 16) ^ (u[0] >> 16); \
154 	v[1] = h[1] ^ (u[2] << 16) ^ (u[1] >> 16); \
155 	v[2] = h[2] ^ (u[3] << 16) ^ (u[2] >> 16); \
156 	v[3] = h[3] ^ (u[4] << 16) ^ (u[3] >> 16); \
157 	v[4] = h[4] ^ (u[5] << 16) ^ (u[4] >> 16); \
158 	v[5] = h[5] ^ (u[6] << 16) ^ (u[5] >> 16); \
159 	v[6] = h[6] ^ (u[7] << 16) ^ (u[6] >> 16); \
160 	v[7] = h[7] ^ (u[0] & 0xffff0000) ^ (u[0] << 16) ^ (u[7] >> 16) ^ \
161 		(u[1] & 0xffff0000) ^ (u[1] << 16) ^ (u[6] << 16) ^ (u[7] & 0xffff0000);
162 
163 #define SHIFT61(h, v) \
164 	h[0] = (v[0] & 0xffff0000) ^ (v[0] << 16) ^ (v[0] >> 16) ^ (v[1] >> 16) ^ \
165 		(v[1] & 0xffff0000) ^ (v[2] << 16) ^ (v[3] >> 16) ^ (v[4] << 16) ^ \
166 		(v[5] >> 16) ^ v[5] ^ (v[6] >> 16) ^ (v[7] << 16) ^ (v[7] >> 16) ^ \
167 		(v[7] & 0xffff); \
168 	h[1] = (v[0] << 16) ^ (v[0] >> 16) ^ (v[0] & 0xffff0000) ^ (v[1] & 0xffff) ^ \
169 	v[2] ^ (v[2] >> 16) ^ (v[3] << 16) ^ (v[4] >> 16) ^ (v[5] << 16) ^ \
170 		(v[6] << 16) ^ v[6] ^ (v[7] & 0xffff0000) ^ (v[7] >> 16); \
171 	h[2] = (v[0] & 0xffff) ^ (v[0] << 16) ^ (v[1] << 16) ^ (v[1] >> 16) ^ \
172 		(v[1] & 0xffff0000) ^ (v[2] << 16) ^ (v[3] >> 16) ^ v[3] ^ (v[4] << 16) ^ \
173 		(v[5] >> 16) ^ v[6] ^ (v[6] >> 16) ^ (v[7] & 0xffff) ^ (v[7] << 16) ^ \
174 		(v[7] >> 16); \
175 	h[3] = (v[0] << 16) ^ (v[0] >> 16) ^ (v[0] & 0xffff0000) ^ \
176 		(v[1] & 0xffff0000) ^ (v[1] >> 16) ^ (v[2] << 16) ^ (v[2] >> 16) ^ v[2] ^ \
177 		(v[3] << 16) ^ (v[4] >> 16) ^ v[4] ^ (v[5] << 16) ^ (v[6] << 16) ^ \
178 		(v[7] & 0xffff) ^ (v[7] >> 16); \
179 	h[4] = (v[0] >> 16) ^ (v[1] << 16) ^ v[1] ^ (v[2] >> 16) ^ v[2] ^ \
180 		(v[3] << 16) ^ (v[3] >> 16) ^ v[3] ^ (v[4] << 16) ^ (v[5] >> 16) ^ \
181 	v[5] ^ (v[6] << 16) ^ (v[6] >> 16) ^ (v[7] << 16); \
182 	h[5] = (v[0] << 16) ^ (v[0] & 0xffff0000) ^ (v[1] << 16) ^ (v[1] >> 16) ^ \
183 		(v[1] & 0xffff0000) ^ (v[2] << 16) ^ v[2] ^ (v[3] >> 16) ^ v[3] ^ \
184 		(v[4] << 16) ^ (v[4] >> 16) ^ v[4] ^ (v[5] << 16) ^ (v[6] << 16) ^ \
185 		(v[6] >> 16) ^ v[6] ^ (v[7] << 16) ^ (v[7] >> 16) ^ (v[7] & 0xffff0000); \
186 	h[6] = v[0] ^ v[2] ^ (v[2] >> 16) ^ v[3] ^ (v[3] << 16) ^ v[4] ^ \
187 		(v[4] >> 16) ^ (v[5] << 16) ^ (v[5] >> 16) ^ v[5] ^ (v[6] << 16) ^ \
188 		(v[6] >> 16) ^ v[6] ^ (v[7] << 16) ^ v[7]; \
189 	h[7] = v[0] ^ (v[0] >> 16) ^ (v[1] << 16) ^ (v[1] >> 16) ^ (v[2] << 16) ^ \
190 		(v[3] >> 16) ^ v[3] ^ (v[4] << 16) ^ v[4] ^ (v[5] >> 16) ^ v[5] ^ \
191 		(v[6] << 16) ^ (v[6] >> 16) ^ (v[7] << 16) ^ v[7];
192 
193 #define PASS(tables) \
194 	X(w, u, v); \
195 	P(key, w); \
196 	R((tables), key, h, i, t, l, r); \
197 	S(s, l, r); \
198 	if (i != 6) { \
199 		A(u, l, r); \
200 		if (i == 2)	{ \
201 			C(u); \
202 		} \
203 		AA(v, l, r); \
204 	}
205 
Gost(PHP_GOST_CTX * context,uint32_t data[8])206 static inline void Gost(PHP_GOST_CTX *context, uint32_t data[8])
207 {
208 	int i;
209 	uint32_t l, r, t, key[8], u[8], v[8], w[8], s[8], *h = context->state, *m = data;
210 
211 	memcpy(u, context->state, sizeof(u));
212 	memcpy(v, data, sizeof(v));
213 
214 	for (i = 0; i < 8; i += 2) {
215 		PASS(*context->tables);
216 	}
217 	SHIFT12(u, m, s);
218 	SHIFT16(h, v, u);
219 	SHIFT61(h, v);
220 }
221 /* }}} */
222 
GostTransform(PHP_GOST_CTX * context,const unsigned char input[32])223 static inline void GostTransform(PHP_GOST_CTX *context, const unsigned char input[32])
224 {
225 	int i, j;
226 	uint32_t data[8], temp = 0;
227 
228 	for (i = 0, j = 0; i < 8; ++i, j += 4) {
229 		data[i] =	((uint32_t) input[j]) | (((uint32_t) input[j + 1]) << 8) |
230 					(((uint32_t) input[j + 2]) << 16) | (((uint32_t) input[j + 3]) << 24);
231 		context->state[i + 8] += data[i] + temp;
232 		temp = context->state[i + 8] < data[i] ? 1 : (context->state[i + 8] == data[i] ? temp : 0);
233 	}
234 
235 	Gost(context, data);
236 }
237 
PHP_GOSTInit(PHP_GOST_CTX * context,ZEND_ATTRIBUTE_UNUSED HashTable * args)238 PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
239 {
240 	memset(context, 0, sizeof(*context));
241 	context->tables = &tables_test;
242 }
243 
PHP_GOSTInitCrypto(PHP_GOST_CTX * context,ZEND_ATTRIBUTE_UNUSED HashTable * args)244 PHP_HASH_API void PHP_GOSTInitCrypto(PHP_GOST_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
245 {
246 	PHP_GOSTInit(context, NULL);
247 	context->tables = &tables_crypto;
248 }
249 
250 static const uint32_t MAX32 = 0xffffffffLU;
251 
PHP_GOSTUpdate(PHP_GOST_CTX * context,const unsigned char * input,size_t len)252 PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *context, const unsigned char *input, size_t len)
253 {
254 	if ((MAX32 - context->count[0]) < (len * 8)) {
255 		context->count[1]++;
256 		context->count[0] = MAX32 - context->count[0];
257 		context->count[0] = (len * 8) - context->count[0];
258 	} else {
259 		context->count[0] += len * 8;
260 	}
261 
262 	if (context->length + len < 32) {
263 		memcpy(&context->buffer[context->length], input, len);
264 		context->length += (unsigned char)len;
265 	} else {
266 		size_t i = 0, r = (context->length + len) % 32;
267 
268 		if (context->length) {
269 			i = 32 - context->length;
270 			memcpy(&context->buffer[context->length], input, i);
271 			GostTransform(context, context->buffer);
272 		}
273 
274 		for (; i + 32 <= len; i += 32) {
275 			GostTransform(context, input + i);
276 		}
277 
278 		memcpy(context->buffer, input + i, r);
279 		ZEND_SECURE_ZERO(&context->buffer[r], 32 - r);
280 		context->length = (unsigned char)r;
281 	}
282 }
283 
PHP_GOSTFinal(unsigned char digest[32],PHP_GOST_CTX * context)284 PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context)
285 {
286 	uint32_t i, j, l[8] = {0};
287 
288 	if (context->length) {
289 		GostTransform(context, context->buffer);
290 	}
291 
292 	memcpy(l, context->count, sizeof(context->count));
293 	Gost(context, l);
294 	memcpy(l, &context->state[8], sizeof(l));
295 	Gost(context, l);
296 
297 	for (i = 0, j = 0; j < 32; i++, j += 4) {
298 		digest[j] = (unsigned char) (context->state[i] & 0xff);
299 		digest[j + 1] = (unsigned char) ((context->state[i] >> 8) & 0xff);
300 		digest[j + 2] = (unsigned char) ((context->state[i] >> 16) & 0xff);
301 		digest[j + 3] = (unsigned char) ((context->state[i] >> 24) & 0xff);
302 	}
303 
304 	ZEND_SECURE_ZERO(context, sizeof(*context));
305 }
306 
php_gost_unserialize(php_hashcontext_object * hash,zend_long magic,const zval * zv)307 static int php_gost_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
308 {
309 	PHP_GOST_CTX *ctx = (PHP_GOST_CTX *) hash->context;
310 	int r = FAILURE;
311 	if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
312 		&& (r = php_hash_unserialize_spec(hash, zv, PHP_GOST_SPEC)) == SUCCESS
313 		&& ctx->length < sizeof(ctx->buffer)) {
314 		return SUCCESS;
315 	} else {
316 		return r != SUCCESS ? r : -2000;
317 	}
318 }
319 
320 const php_hash_ops php_hash_gost_ops = {
321 	"gost",
322 	(php_hash_init_func_t) PHP_GOSTInit,
323 	(php_hash_update_func_t) PHP_GOSTUpdate,
324 	(php_hash_final_func_t) PHP_GOSTFinal,
325 	php_hash_copy,
326 	php_hash_serialize,
327 	php_gost_unserialize,
328 	PHP_GOST_SPEC,
329 	32,
330 	32,
331 	sizeof(PHP_GOST_CTX),
332 	1
333 };
334 
335 const php_hash_ops php_hash_gost_crypto_ops = {
336 	"gost-crypto",
337 	(php_hash_init_func_t) PHP_GOSTInitCrypto,
338 	(php_hash_update_func_t) PHP_GOSTUpdate,
339 	(php_hash_final_func_t) PHP_GOSTFinal,
340 	php_hash_copy,
341 	php_hash_serialize,
342 	php_gost_unserialize,
343 	PHP_GOST_SPEC,
344 	32,
345 	32,
346 	sizeof(PHP_GOST_CTX),
347 	1
348 };
349