Skip to content

Commit 7759b7c

Browse files
committed
Make HashContexts serializable.
* Modify php_hash_ops to contain the algorithm name and serialize and unserialize methods. * Implement __serialize and __unserialize magic methods on HashContext. Note that serialized HashContexts are not necessarily portable between PHP versions or from architecture to architecture. (Most are, though Keccak and slow SHA3s are not.) An exception is thrown when an unsupported serialization is attempted. Because of security concerns, HASH_HMAC contexts are not currently serializable; attempting to serialize one throws an exception. Serialization exposes the state of HashContext memory, so ensure that memory is zeroed before use by allocating it with a new php_hash_alloc_context function. Performance impact is negligible. Some hash internal states have logical pointers into a buffer, or sponge, that absorbs input provided in bytes rather than chunks. The unserialize functions for these hash functions must validate that the logical pointers are all within bounds, lest future hash operations cause out-of-bounds memory accesses. * Adler32, CRC32, FNV, joaat: simple state, no buffer positions * Gost, MD2, SHA3, Snefru, Tiger, Whirlpool: buffer positions must be validated * MD4, MD5, SHA1, SHA2, haval, ripemd: buffer positions encoded bitwise, forced to within bounds on use; no need to validate
1 parent 7103c56 commit 7759b7c

37 files changed

+1511
-53
lines changed

ext/hash/hash.c

Lines changed: 377 additions & 9 deletions
Large diffs are not rendered by default.

ext/hash/hash.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@ function mhash(int $hash, string $data, string $key = UNKNOWN): string|false {}
5353
final class HashContext
5454
{
5555
private function __construct() {}
56+
57+
public function __serialize(): array {}
58+
59+
public function __unserialize(array $serialized): void {}
5660
}

ext/hash/hash_adler32.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,14 @@ PHP_HASH_API int PHP_ADLER32Copy(const php_hash_ops *ops, PHP_ADLER32_CTX *orig_
5959
}
6060

6161
const php_hash_ops php_hash_adler32_ops = {
62+
"adler32",
6263
(php_hash_init_func_t) PHP_ADLER32Init,
6364
(php_hash_update_func_t) PHP_ADLER32Update,
6465
(php_hash_final_func_t) PHP_ADLER32Final,
6566
(php_hash_copy_func_t) PHP_ADLER32Copy,
67+
php_hash_serialize,
68+
php_hash_unserialize,
69+
PHP_ADLER32_SPEC,
6670
4, /* what to say here? */
6771
4,
6872
sizeof(PHP_ADLER32_CTX),

ext/hash/hash_arginfo.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ ZEND_END_ARG_INFO()
118118
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_HashContext___construct, 0, 0, 0)
119119
ZEND_END_ARG_INFO()
120120

121+
#define arginfo_class_HashContext___serialize arginfo_hash_algos
122+
123+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_HashContext___unserialize, 0, 1, IS_VOID, 0)
124+
ZEND_ARG_TYPE_INFO(0, serialized, IS_ARRAY, 0)
125+
ZEND_END_ARG_INFO()
126+
121127

122128
ZEND_FUNCTION(hash);
123129
ZEND_FUNCTION(hash_file);
@@ -150,6 +156,8 @@ ZEND_FUNCTION(mhash_count);
150156
ZEND_FUNCTION(mhash);
151157
#endif
152158
ZEND_METHOD(HashContext, __construct);
159+
ZEND_METHOD(HashContext, __serialize);
160+
ZEND_METHOD(HashContext, __unserialize);
153161

154162

155163
static const zend_function_entry ext_functions[] = {
@@ -189,5 +197,7 @@ static const zend_function_entry ext_functions[] = {
189197

190198
static const zend_function_entry class_HashContext_methods[] = {
191199
ZEND_ME(HashContext, __construct, arginfo_class_HashContext___construct, ZEND_ACC_PRIVATE)
200+
ZEND_ME(HashContext, __serialize, arginfo_class_HashContext___serialize, ZEND_ACC_PUBLIC)
201+
ZEND_ME(HashContext, __unserialize, arginfo_class_HashContext___unserialize, ZEND_ACC_PUBLIC)
192202
ZEND_FE_END
193203
};

ext/hash/hash_crc32.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,32 +78,44 @@ PHP_HASH_API int PHP_CRC32Copy(const php_hash_ops *ops, PHP_CRC32_CTX *orig_cont
7878
}
7979

8080
const php_hash_ops php_hash_crc32_ops = {
81+
"crc32",
8182
(php_hash_init_func_t) PHP_CRC32Init,
8283
(php_hash_update_func_t) PHP_CRC32Update,
8384
(php_hash_final_func_t) PHP_CRC32LEFinal,
8485
(php_hash_copy_func_t) PHP_CRC32Copy,
86+
php_hash_serialize,
87+
php_hash_unserialize,
88+
PHP_CRC32_SPEC,
8589
4, /* what to say here? */
8690
4,
8791
sizeof(PHP_CRC32_CTX),
8892
0
8993
};
9094

9195
const php_hash_ops php_hash_crc32b_ops = {
96+
"crc32b",
9297
(php_hash_init_func_t) PHP_CRC32Init,
9398
(php_hash_update_func_t) PHP_CRC32BUpdate,
9499
(php_hash_final_func_t) PHP_CRC32BEFinal,
95100
(php_hash_copy_func_t) PHP_CRC32Copy,
101+
php_hash_serialize,
102+
php_hash_unserialize,
103+
PHP_CRC32_SPEC,
96104
4, /* what to say here? */
97105
4,
98106
sizeof(PHP_CRC32_CTX),
99107
0
100108
};
101109

102110
const php_hash_ops php_hash_crc32c_ops = {
111+
"crc32c",
103112
(php_hash_init_func_t) PHP_CRC32Init,
104113
(php_hash_update_func_t) PHP_CRC32CUpdate,
105114
(php_hash_final_func_t) PHP_CRC32BEFinal,
106115
(php_hash_copy_func_t) PHP_CRC32Copy,
116+
php_hash_serialize,
117+
php_hash_unserialize,
118+
PHP_CRC32_SPEC,
107119
4, /* what to say here? */
108120
4,
109121
sizeof(PHP_CRC32_CTX),

ext/hash/hash_fnv.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,43 +21,59 @@
2121
#include "php_hash_fnv.h"
2222

2323
const php_hash_ops php_hash_fnv132_ops = {
24+
"fnv132",
2425
(php_hash_init_func_t) PHP_FNV132Init,
2526
(php_hash_update_func_t) PHP_FNV132Update,
2627
(php_hash_final_func_t) PHP_FNV132Final,
27-
(php_hash_copy_func_t) php_hash_copy,
28+
php_hash_copy,
29+
php_hash_serialize,
30+
php_hash_unserialize,
31+
PHP_FNV132_SPEC,
2832
4,
2933
4,
3034
sizeof(PHP_FNV132_CTX),
3135
0
3236
};
3337

3438
const php_hash_ops php_hash_fnv1a32_ops = {
39+
"fnv1a32",
3540
(php_hash_init_func_t) PHP_FNV132Init,
3641
(php_hash_update_func_t) PHP_FNV1a32Update,
37-
(php_hash_final_func_t) PHP_FNV132Final,
38-
(php_hash_copy_func_t) php_hash_copy,
42+
(php_hash_final_func_t) PHP_FNV132Final,
43+
php_hash_copy,
44+
php_hash_serialize,
45+
php_hash_unserialize,
46+
PHP_FNV132_SPEC,
3947
4,
4048
4,
4149
sizeof(PHP_FNV132_CTX),
4250
0
4351
};
4452

4553
const php_hash_ops php_hash_fnv164_ops = {
54+
"fnv164",
4655
(php_hash_init_func_t) PHP_FNV164Init,
4756
(php_hash_update_func_t) PHP_FNV164Update,
4857
(php_hash_final_func_t) PHP_FNV164Final,
49-
(php_hash_copy_func_t) php_hash_copy,
58+
php_hash_copy,
59+
php_hash_serialize,
60+
php_hash_unserialize,
61+
PHP_FNV164_SPEC,
5062
8,
5163
4,
5264
sizeof(PHP_FNV164_CTX),
5365
0
5466
};
5567

5668
const php_hash_ops php_hash_fnv1a64_ops = {
69+
"fnv1a64",
5770
(php_hash_init_func_t) PHP_FNV164Init,
5871
(php_hash_update_func_t) PHP_FNV1a64Update,
59-
(php_hash_final_func_t) PHP_FNV164Final,
60-
(php_hash_copy_func_t) php_hash_copy,
72+
(php_hash_final_func_t) PHP_FNV164Final,
73+
php_hash_copy,
74+
php_hash_serialize,
75+
php_hash_unserialize,
76+
PHP_FNV164_SPEC,
6177
8,
6278
4,
6379
sizeof(PHP_FNV164_CTX),

ext/hash/hash_gost.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,22 +304,43 @@ PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context)
304304
ZEND_SECURE_ZERO(context, sizeof(*context));
305305
}
306306

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+
307320
const php_hash_ops php_hash_gost_ops = {
321+
"gost",
308322
(php_hash_init_func_t) PHP_GOSTInit,
309323
(php_hash_update_func_t) PHP_GOSTUpdate,
310324
(php_hash_final_func_t) PHP_GOSTFinal,
311-
(php_hash_copy_func_t) php_hash_copy,
325+
php_hash_copy,
326+
php_hash_serialize,
327+
php_gost_unserialize,
328+
PHP_GOST_SPEC,
312329
32,
313330
32,
314331
sizeof(PHP_GOST_CTX),
315332
1
316333
};
317334

318335
const php_hash_ops php_hash_gost_crypto_ops = {
336+
"gost-crypto",
319337
(php_hash_init_func_t) PHP_GOSTInitCrypto,
320338
(php_hash_update_func_t) PHP_GOSTUpdate,
321339
(php_hash_final_func_t) PHP_GOSTFinal,
322-
(php_hash_copy_func_t) php_hash_copy,
340+
php_hash_copy,
341+
php_hash_serialize,
342+
php_gost_unserialize,
343+
PHP_GOST_SPEC,
323344
32,
324345
32,
325346
sizeof(PHP_GOST_CTX),

ext/hash/hash_haval.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,14 @@ static void PHP_5HAVALTransform(uint32_t state[8], const unsigned char block[128
247247

248248
#define PHP_HASH_HAVAL_INIT(p,b) \
249249
const php_hash_ops php_hash_##p##haval##b##_ops = { \
250+
"haval" #b "," #p, \
250251
(php_hash_init_func_t) PHP_##p##HAVAL##b##Init, \
251252
(php_hash_update_func_t) PHP_HAVALUpdate, \
252253
(php_hash_final_func_t) PHP_HAVAL##b##Final, \
253-
(php_hash_copy_func_t) php_hash_copy, \
254+
php_hash_copy, \
255+
php_hash_serialize, \
256+
php_hash_unserialize, \
257+
PHP_HAVAL_SPEC, \
254258
((b) / 8), 128, sizeof(PHP_HAVAL_CTX), 1 }; \
255259
PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *context) \
256260
{ int i; context->count[0] = context->count[1] = 0; \

ext/hash/hash_joaat.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@
2222
#include "php_hash_joaat.h"
2323

2424
const php_hash_ops php_hash_joaat_ops = {
25+
"joaat",
2526
(php_hash_init_func_t) PHP_JOAATInit,
2627
(php_hash_update_func_t) PHP_JOAATUpdate,
2728
(php_hash_final_func_t) PHP_JOAATFinal,
28-
(php_hash_copy_func_t) php_hash_copy,
29+
php_hash_copy,
30+
php_hash_serialize,
31+
php_hash_unserialize,
32+
PHP_JOAAT_SPEC,
2933
4,
3034
4,
3135
sizeof(PHP_JOAAT_CTX),

ext/hash/hash_md.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,46 @@
1818
#include "php_hash_md.h"
1919

2020
const php_hash_ops php_hash_md5_ops = {
21+
"md5",
2122
(php_hash_init_func_t) PHP_MD5Init,
2223
(php_hash_update_func_t) PHP_MD5Update,
2324
(php_hash_final_func_t) PHP_MD5Final,
24-
(php_hash_copy_func_t) php_hash_copy,
25+
php_hash_copy,
26+
php_hash_serialize,
27+
php_hash_unserialize,
28+
PHP_MD5_SPEC,
2529
16,
2630
64,
2731
sizeof(PHP_MD5_CTX),
2832
1
2933
};
3034

3135
const php_hash_ops php_hash_md4_ops = {
36+
"md4",
3237
(php_hash_init_func_t) PHP_MD4Init,
3338
(php_hash_update_func_t) PHP_MD4Update,
3439
(php_hash_final_func_t) PHP_MD4Final,
35-
(php_hash_copy_func_t) php_hash_copy,
40+
php_hash_copy,
41+
php_hash_serialize,
42+
php_hash_unserialize,
43+
PHP_MD4_SPEC,
3644
16,
3745
64,
3846
sizeof(PHP_MD4_CTX),
3947
1
4048
};
4149

50+
static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv);
51+
4252
const php_hash_ops php_hash_md2_ops = {
53+
"md2",
4354
(php_hash_init_func_t) PHP_MD2Init,
4455
(php_hash_update_func_t) PHP_MD2Update,
4556
(php_hash_final_func_t) PHP_MD2Final,
46-
(php_hash_copy_func_t) php_hash_copy,
57+
php_hash_copy,
58+
php_hash_serialize,
59+
php_md2_unserialize,
60+
PHP_MD2_SPEC,
4761
16,
4862
16,
4963
sizeof(PHP_MD2_CTX),
@@ -340,3 +354,16 @@ PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context)
340354

341355
memcpy(output, context->state, 16);
342356
}
357+
358+
static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv)
359+
{
360+
PHP_MD2_CTX *ctx = (PHP_MD2_CTX *) hash->context;
361+
int r = FAILURE;
362+
if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC
363+
&& (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == SUCCESS
364+
&& (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) {
365+
return SUCCESS;
366+
} else {
367+
return r != SUCCESS ? r : -2000;
368+
}
369+
}

ext/hash/hash_ripemd.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,43 +22,59 @@
2222
#include "php_hash_ripemd.h"
2323

2424
const php_hash_ops php_hash_ripemd128_ops = {
25+
"ripemd128",
2526
(php_hash_init_func_t) PHP_RIPEMD128Init,
2627
(php_hash_update_func_t) PHP_RIPEMD128Update,
2728
(php_hash_final_func_t) PHP_RIPEMD128Final,
28-
(php_hash_copy_func_t) php_hash_copy,
29+
php_hash_copy,
30+
php_hash_serialize,
31+
php_hash_unserialize,
32+
PHP_RIPEMD128_SPEC,
2933
16,
3034
64,
3135
sizeof(PHP_RIPEMD128_CTX),
3236
1
3337
};
3438

3539
const php_hash_ops php_hash_ripemd160_ops = {
40+
"ripemd160",
3641
(php_hash_init_func_t) PHP_RIPEMD160Init,
3742
(php_hash_update_func_t) PHP_RIPEMD160Update,
3843
(php_hash_final_func_t) PHP_RIPEMD160Final,
39-
(php_hash_copy_func_t) php_hash_copy,
44+
php_hash_copy,
45+
php_hash_serialize,
46+
php_hash_unserialize,
47+
PHP_RIPEMD160_SPEC,
4048
20,
4149
64,
4250
sizeof(PHP_RIPEMD160_CTX),
4351
1
4452
};
4553

4654
const php_hash_ops php_hash_ripemd256_ops = {
55+
"ripemd256",
4756
(php_hash_init_func_t) PHP_RIPEMD256Init,
4857
(php_hash_update_func_t) PHP_RIPEMD256Update,
4958
(php_hash_final_func_t) PHP_RIPEMD256Final,
50-
(php_hash_copy_func_t) php_hash_copy,
59+
php_hash_copy,
60+
php_hash_serialize,
61+
php_hash_unserialize,
62+
PHP_RIPEMD256_SPEC,
5163
32,
5264
64,
5365
sizeof(PHP_RIPEMD256_CTX),
5466
1
5567
};
5668

5769
const php_hash_ops php_hash_ripemd320_ops = {
70+
"ripemd320",
5871
(php_hash_init_func_t) PHP_RIPEMD320Init,
5972
(php_hash_update_func_t) PHP_RIPEMD320Update,
6073
(php_hash_final_func_t) PHP_RIPEMD320Final,
61-
(php_hash_copy_func_t) php_hash_copy,
74+
php_hash_copy,
75+
php_hash_serialize,
76+
php_hash_unserialize,
77+
PHP_RIPEMD320_SPEC,
6278
40,
6379
64,
6480
sizeof(PHP_RIPEMD320_CTX),

0 commit comments

Comments
 (0)