diff --git a/configure.ac b/configure.ac index df64a61015fb..251667732865 100644 --- a/configure.ac +++ b/configure.ac @@ -491,6 +491,20 @@ PHP_CHECK_BUILTIN_CPU_INIT dnl Check __builtin_cpu_supports PHP_CHECK_BUILTIN_CPU_SUPPORTS +dnl Check for __alignof__ support in the compiler +AC_CACHE_CHECK(whether the compiler supports __alignof__, ac_cv_alignof_exists,[ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +]],[[ + int align = __alignof__(int); +]])],[ + ac_cv_alignof_exists=yes +],[ + ac_cv_alignof_exists=no +])]) +if test "$ac_cv_alignof_exists" = "yes"; then + AC_DEFINE([HAVE_ALIGNOF], 1, [whether the compiler supports __alignof__]) +fi + dnl Check for structure members. AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include ]) AC_CHECK_MEMBERS([struct stat.st_blksize, struct stat.st_rdev]) diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 31ead3716379..469d91e34bfc 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -23,12 +23,24 @@ #include "php_hash.h" #include "ext/standard/info.h" #include "ext/standard/file.h" +#include "ext/standard/php_var.h" +#include "ext/spl/spl_exceptions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" +#include "zend_smart_str.h" #include "hash_arginfo.h" +#ifdef PHP_WIN32 +# define __alignof__ __alignof +#else +# ifndef HAVE_ALIGNOF +# include +# define __alignof__(type) offsetof (struct { char c; type member;}, member) +# endif +#endif + HashTable php_hash_hashtable; zend_class_entry *php_hashcontext_ce; static zend_object_handlers php_hashcontext_handlers; @@ -111,6 +123,226 @@ PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_c } /* }}} */ + +static inline size_t align_to(size_t pos, size_t alignment) { + size_t offset = pos & (alignment - 1); + return pos + (offset ? alignment - offset : 0); +} + +static size_t parse_serialize_spec( + const char **specp, size_t *pos, size_t *sz, size_t *max_alignment) { + size_t count, alignment; + const char *spec = *specp; + /* parse size */ + if (*spec == 's' || *spec == 'S') { + *sz = 2; + alignment = __alignof__(uint16_t); /* usually 2 */ + } else if (*spec == 'l' || *spec == 'L') { + *sz = 4; + alignment = __alignof__(uint32_t); /* usually 4 */ + } else if (*spec == 'q' || *spec == 'Q') { + *sz = 8; + alignment = __alignof__(uint64_t); /* usually 8 */ + } else if (*spec == 'i' || *spec == 'I') { + *sz = sizeof(int); + alignment = __alignof__(int); /* usually 4 */ + } else { + ZEND_ASSERT(*spec == 'b' || *spec == 'B'); + *sz = 1; + alignment = 1; + } + /* process alignment */ + *pos = align_to(*pos, alignment); + *max_alignment = *max_alignment < alignment ? alignment : *max_alignment; + /* parse count */ + ++spec; + if (isdigit((unsigned char) *spec)) { + count = 0; + while (isdigit((unsigned char) *spec)) { + count = 10 * count + *spec - '0'; + ++spec; + } + } else { + count = 1; + } + *specp = spec; + return count; +} + +static uint64_t one_from_buffer(size_t sz, const unsigned char *buf) { + if (sz == 2) { + const uint16_t *x = (const uint16_t *) buf; + return *x; + } else if (sz == 4) { + const uint32_t *x = (const uint32_t *) buf; + return *x; + } else if (sz == 8) { + const uint64_t *x = (const uint64_t *) buf; + return *x; + } else { + ZEND_ASSERT(sz == 1); + return *buf; + } +} + +static void one_to_buffer(size_t sz, unsigned char *buf, uint64_t val) { + if (sz == 2) { + uint16_t *x = (uint16_t *) buf; + *x = val; + } else if (sz == 4) { + uint32_t *x = (uint32_t *) buf; + *x = val; + } else if (sz == 8) { + uint64_t *x = (uint64_t *) buf; + *x = val; + } else { + ZEND_ASSERT(sz == 1); + *buf = val; + } +} + +/* Serialize a hash context according to a `spec` string. + Spec contents: + b[COUNT] -- serialize COUNT bytes + s[COUNT] -- serialize COUNT 16-bit integers + l[COUNT] -- serialize COUNT 32-bit integers + q[COUNT] -- serialize COUNT 64-bit integers + i[COUNT] -- serialize COUNT `int`s + B[COUNT] -- skip COUNT bytes + S[COUNT], L[COUNT], etc. -- uppercase versions skip instead of read + . (must be last character) -- assert that the hash context has exactly + this size + Example: "llllllb64l16." is the spec for an MD5 context: 6 32-bit + integers, followed by 64 bytes, then 16 32-bit integers, and that's + exactly the size of the context. + + The serialization result is an array. Each integer is serialized as a + 32-bit integer, except that a run of 2 or more bytes is encoded as a + string, and each 64-bit integer is serialized as two 32-bit integers, least + significant bits first. This allows 32-bit and 64-bit architectures to + interchange serialized HashContexts. */ + +PHP_HASH_API int php_hash_serialize_spec(const php_hashcontext_object *hash, zval *zv, const char *spec) /* {{{ */ +{ + size_t pos = 0, max_alignment = 1; + unsigned char *buf = (unsigned char *) hash->context; + zval tmp; + array_init(zv); + while (*spec != '\0' && *spec != '.') { + char spec_ch = *spec; + size_t sz, count = parse_serialize_spec(&spec, &pos, &sz, &max_alignment); + if (pos + count * sz > hash->ops->context_size) { + return FAILURE; + } + if (isupper((unsigned char) spec_ch)) { + pos += count * sz; + } else if (sz == 1 && count > 1) { + ZVAL_STRINGL(&tmp, (char *) buf + pos, count); + zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp); + pos += count; + } else { + while (count > 0) { + uint64_t val = one_from_buffer(sz, buf + pos); + pos += sz; + ZVAL_LONG(&tmp, (int32_t) val); + zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp); + if (sz == 8) { + ZVAL_LONG(&tmp, (int32_t) (val >> 32)); + zend_hash_next_index_insert(Z_ARRVAL_P(zv), &tmp); + } + --count; + } + } + } + if (*spec == '.' && align_to(pos, max_alignment) != hash->ops->context_size) { + return FAILURE; + } + return SUCCESS; +} +/* }}} */ + +/* Unserialize a hash context serialized by `php_hash_serialize_spec` with `spec`. + Returns SUCCESS on success and a negative error code on failure. + Codes: FAILURE (-1) == generic failure + -999 == spec wrong size for context + -1000 - POS == problem at byte offset POS */ + +PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec) /* {{{ */ +{ + size_t pos = 0, max_alignment = 1, j = 0; + unsigned char *buf = (unsigned char *) hash->context; + zval *elt; + if (Z_TYPE_P(zv) != IS_ARRAY) { + return FAILURE; + } + while (*spec != '\0' && *spec != '.') { + char spec_ch = *spec; + size_t sz, count = parse_serialize_spec(&spec, &pos, &sz, &max_alignment); + if (pos + count * sz > hash->ops->context_size) { + return -999; + } + if (isupper((unsigned char) spec_ch)) { + pos += count * sz; + } else if (sz == 1 && count > 1) { + elt = zend_hash_index_find(Z_ARRVAL_P(zv), j); + if (!elt || Z_TYPE_P(elt) != IS_STRING || Z_STRLEN_P(elt) != count) { + return -1000 - pos; + } + ++j; + memcpy(buf + pos, Z_STRVAL_P(elt), count); + pos += count; + } else { + while (count > 0) { + uint64_t val; + elt = zend_hash_index_find(Z_ARRVAL_P(zv), j); + if (!elt || Z_TYPE_P(elt) != IS_LONG) { + return -1000 - pos; + } + ++j; + val = (uint32_t) Z_LVAL_P(elt); + if (sz == 8) { + elt = zend_hash_index_find(Z_ARRVAL_P(zv), j); + if (!elt || Z_TYPE_P(elt) != IS_LONG) { + return -1000 - pos; + } + ++j; + val += ((uint64_t) Z_LVAL_P(elt)) << 32; + } + one_to_buffer(sz, buf + pos, val); + pos += sz; + --count; + } + } + } + if (*spec == '.' && align_to(pos, max_alignment) != hash->ops->context_size) { + return -999; + } + return SUCCESS; +} +/* }}} */ + +PHP_HASH_API int php_hash_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv) /* {{{ */ +{ + if (hash->ops->serialize_spec) { + *magic = PHP_HASH_SERIALIZE_MAGIC_SPEC; + return php_hash_serialize_spec(hash, zv, hash->ops->serialize_spec); + } else { + return FAILURE; + } +} +/* }}} */ + +PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) /* {{{ */ +{ + if (hash->ops->serialize_spec + && magic == PHP_HASH_SERIALIZE_MAGIC_SPEC) { + return php_hash_unserialize_spec(hash, zv, hash->ops->serialize_spec); + } else { + return FAILURE; + } +} +/* }}} */ + /* Userspace */ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */ @@ -147,7 +379,7 @@ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_ } } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); if (isfilename) { @@ -271,7 +503,7 @@ static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename, } } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); K = emalloc(ops->block_size); digest = zend_string_alloc(ops->digest_size, 0); @@ -375,7 +607,7 @@ PHP_FUNCTION(hash_init) object_init_ex(return_value, php_hashcontext_ce); hash = php_hashcontext_from_object(Z_OBJ_P(return_value)); - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); hash->ops = ops; @@ -664,7 +896,7 @@ PHP_FUNCTION(hash_hkdf) RETURN_THROWS(); } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); // Extract ops->hash_init(context); @@ -759,7 +991,7 @@ PHP_FUNCTION(hash_pbkdf2) RETURN_THROWS(); } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); K1 = emalloc(ops->block_size); @@ -1075,7 +1307,7 @@ PHP_FUNCTION(mhash_keygen_s2k) times++; } - context = emalloc(ops->context_size); + context = php_hash_alloc_context(ops); ops->hash_init(context); key = ecalloc(1, times * block_size); @@ -1125,11 +1357,7 @@ static zend_object* php_hashcontext_create(zend_class_entry *ce) { static void php_hashcontext_dtor(zend_object *obj) { php_hashcontext_object *hash = php_hashcontext_from_object(obj); - /* Just in case the algo has internally allocated resources */ if (hash->context) { - unsigned char *dummy = emalloc(hash->ops->digest_size); - hash->ops->hash_final(dummy, hash->context); - efree(dummy); efree(hash->context); hash->context = NULL; } @@ -1152,7 +1380,7 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) { newobj->ops = oldobj->ops; newobj->options = oldobj->options; - newobj->context = emalloc(newobj->ops->context_size); + newobj->context = php_hash_alloc_context(newobj->ops); newobj->ops->hash_init(newobj->context); if (SUCCESS != newobj->ops->hash_copy(newobj->ops, oldobj->context, newobj->context)) { @@ -1170,6 +1398,144 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) { } /* }}} */ +/* Serialization format: 5-element array + Index 0: hash algorithm (string) + Index 1: options (long, 0) + Index 2: hash-determined serialization of context state (usually array) + Index 3: magic number defining layout of context state (long, usually 2) + Index 4: properties (array) + + HashContext serializations are not necessarily portable between architectures or + PHP versions. If the format of a serialized hash context changes, that should + be reflected in either a different value of `magic` or a different format of + the serialized context state. Most context states are unparsed and parsed using + a spec string, such as "llb128.", using the format defined by + `php_hash_serialize_spec`/`php_hash_unserialize_spec`. Some hash algorithms must + also check the unserialized state for validity, to ensure that using an + unserialized context is safe from memory errors. + + Currently HASH_HMAC contexts cannot be serialized, because serializing them + would require serializing the HMAC key in plaintext. */ + +/* {{{ proto array HashContext::__serialize() + Serialize the object */ +PHP_METHOD(HashContext, __serialize) +{ + zval *object = ZEND_THIS; + php_hashcontext_object *hash = php_hashcontext_from_object(Z_OBJ_P(object)); + zend_long magic = 0; + zval tmp; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + array_init(return_value); + + if (!hash->ops->hash_serialize) { + goto serialize_failure; + } else if (hash->options & PHP_HASH_HMAC) { + zend_throw_exception(NULL, "HashContext with HASH_HMAC option cannot be serialized", 0); + RETURN_THROWS(); + } + + ZVAL_STRING(&tmp, hash->ops->algo); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + ZVAL_LONG(&tmp, hash->options); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + if (hash->ops->hash_serialize(hash, &magic, &tmp) != SUCCESS) { + goto serialize_failure; + } + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + ZVAL_LONG(&tmp, magic); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + /* members */ + ZVAL_ARR(&tmp, zend_std_get_properties(&hash->std)); + Z_TRY_ADDREF(tmp); + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &tmp); + + return; + +serialize_failure: + zend_throw_exception_ex(NULL, 0, "HashContext for algorithm \"%s\" cannot be serialized", hash->ops->algo); + RETURN_THROWS(); +} +/* }}} */ + +/* {{{ proto void HashContext::__unserialize(array serialized) + * unserialize the object + */ +PHP_METHOD(HashContext, __unserialize) +{ + zval *object = ZEND_THIS; + php_hashcontext_object *hash = php_hashcontext_from_object(Z_OBJ_P(object)); + HashTable *data; + zval *algo_zv, *magic_zv, *options_zv, *hash_zv, *members_zv; + zend_long magic, options; + int unserialize_result; + const php_hash_ops *ops; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) { + RETURN_THROWS(); + } + + if (hash->context) { + zend_throw_exception(NULL, "HashContext::__unserialize called on initialized object", 0); + RETURN_THROWS(); + } + + algo_zv = zend_hash_index_find(data, 0); + options_zv = zend_hash_index_find(data, 1); + hash_zv = zend_hash_index_find(data, 2); + magic_zv = zend_hash_index_find(data, 3); + members_zv = zend_hash_index_find(data, 4); + + if (!algo_zv || Z_TYPE_P(algo_zv) != IS_STRING + || !magic_zv || Z_TYPE_P(magic_zv) != IS_LONG + || !options_zv || Z_TYPE_P(options_zv) != IS_LONG + || !hash_zv + || !members_zv || Z_TYPE_P(members_zv) != IS_ARRAY) { + zend_throw_exception(NULL, "Incomplete or ill-formed serialization data", 0); + RETURN_THROWS(); + } + + magic = Z_LVAL_P(magic_zv); + options = Z_LVAL_P(options_zv); + if (options & PHP_HASH_HMAC) { + zend_throw_exception(NULL, "HashContext with HASH_HMAC option cannot be serialized", 0); + RETURN_THROWS(); + } + + ops = php_hash_fetch_ops(Z_STR_P(algo_zv)); + if (!ops) { + zend_throw_exception(NULL, "Unknown hash algorithm", 0); + RETURN_THROWS(); + } else if (!ops->hash_unserialize) { + zend_throw_exception_ex(NULL, 0, "Hash algorithm \"%s\" cannot be unserialized", ops->algo); + RETURN_THROWS(); + } + + hash->ops = ops; + hash->context = php_hash_alloc_context(ops); + ops->hash_init(hash->context); + hash->options = options; + + unserialize_result = ops->hash_unserialize(hash, magic, hash_zv); + if (unserialize_result != SUCCESS) { + zend_throw_exception_ex(NULL, 0, "Incomplete or ill-formed serialization data (\"%s\" code %d)", ops->algo, unserialize_result); + /* free context */ + php_hashcontext_dtor(Z_OBJ_P(object)); + RETURN_THROWS(); + } + + object_properties_load(&hash->std, Z_ARRVAL_P(members_zv)); +} +/* }}} */ + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(hash) @@ -1241,8 +1607,6 @@ PHP_MINIT_FUNCTION(hash) php_hashcontext_ce = zend_register_internal_class(&ce); php_hashcontext_ce->ce_flags |= ZEND_ACC_FINAL; php_hashcontext_ce->create_object = php_hashcontext_create; - php_hashcontext_ce->serialize = zend_class_serialize_deny; - php_hashcontext_ce->unserialize = zend_class_unserialize_deny; memcpy(&php_hashcontext_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/hash/hash.stub.php b/ext/hash/hash.stub.php index 32465415fcbf..f8788ed61ed5 100644 --- a/ext/hash/hash.stub.php +++ b/ext/hash/hash.stub.php @@ -53,4 +53,8 @@ function mhash(int $hash, string $data, string $key = UNKNOWN): string|false {} final class HashContext { private function __construct() {} + + public function __serialize(): array {} + + public function __unserialize(array $serialized): void {} } diff --git a/ext/hash/hash_adler32.c b/ext/hash/hash_adler32.c index 8d2d2fcb9365..d45012f8e6d5 100644 --- a/ext/hash/hash_adler32.c +++ b/ext/hash/hash_adler32.c @@ -59,10 +59,14 @@ PHP_HASH_API int PHP_ADLER32Copy(const php_hash_ops *ops, PHP_ADLER32_CTX *orig_ } const php_hash_ops php_hash_adler32_ops = { + "adler32", (php_hash_init_func_t) PHP_ADLER32Init, (php_hash_update_func_t) PHP_ADLER32Update, (php_hash_final_func_t) PHP_ADLER32Final, (php_hash_copy_func_t) PHP_ADLER32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_ADLER32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_ADLER32_CTX), diff --git a/ext/hash/hash_arginfo.h b/ext/hash/hash_arginfo.h index c09fd1200162..7ba3fca9d722 100644 --- a/ext/hash/hash_arginfo.h +++ b/ext/hash/hash_arginfo.h @@ -118,6 +118,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_HashContext___construct, 0, 0, 0) ZEND_END_ARG_INFO() +#define arginfo_class_HashContext___serialize arginfo_hash_algos + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_HashContext___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, serialized, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_FUNCTION(hash); ZEND_FUNCTION(hash_file); @@ -150,6 +156,8 @@ ZEND_FUNCTION(mhash_count); ZEND_FUNCTION(mhash); #endif ZEND_METHOD(HashContext, __construct); +ZEND_METHOD(HashContext, __serialize); +ZEND_METHOD(HashContext, __unserialize); static const zend_function_entry ext_functions[] = { @@ -189,5 +197,7 @@ static const zend_function_entry ext_functions[] = { static const zend_function_entry class_HashContext_methods[] = { ZEND_ME(HashContext, __construct, arginfo_class_HashContext___construct, ZEND_ACC_PRIVATE) + ZEND_ME(HashContext, __serialize, arginfo_class_HashContext___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(HashContext, __unserialize, arginfo_class_HashContext___unserialize, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/hash/hash_crc32.c b/ext/hash/hash_crc32.c index 52c67717ffbd..de270522d7e2 100644 --- a/ext/hash/hash_crc32.c +++ b/ext/hash/hash_crc32.c @@ -78,10 +78,14 @@ PHP_HASH_API int PHP_CRC32Copy(const php_hash_ops *ops, PHP_CRC32_CTX *orig_cont } const php_hash_ops php_hash_crc32_ops = { + "crc32", (php_hash_init_func_t) PHP_CRC32Init, (php_hash_update_func_t) PHP_CRC32Update, (php_hash_final_func_t) PHP_CRC32LEFinal, (php_hash_copy_func_t) PHP_CRC32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_CRC32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_CRC32_CTX), @@ -89,10 +93,14 @@ const php_hash_ops php_hash_crc32_ops = { }; const php_hash_ops php_hash_crc32b_ops = { + "crc32b", (php_hash_init_func_t) PHP_CRC32Init, (php_hash_update_func_t) PHP_CRC32BUpdate, (php_hash_final_func_t) PHP_CRC32BEFinal, (php_hash_copy_func_t) PHP_CRC32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_CRC32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_CRC32_CTX), @@ -100,10 +108,14 @@ const php_hash_ops php_hash_crc32b_ops = { }; const php_hash_ops php_hash_crc32c_ops = { + "crc32c", (php_hash_init_func_t) PHP_CRC32Init, (php_hash_update_func_t) PHP_CRC32CUpdate, (php_hash_final_func_t) PHP_CRC32BEFinal, (php_hash_copy_func_t) PHP_CRC32Copy, + php_hash_serialize, + php_hash_unserialize, + PHP_CRC32_SPEC, 4, /* what to say here? */ 4, sizeof(PHP_CRC32_CTX), diff --git a/ext/hash/hash_fnv.c b/ext/hash/hash_fnv.c index 976c53b52728..2ee81b9c8601 100644 --- a/ext/hash/hash_fnv.c +++ b/ext/hash/hash_fnv.c @@ -21,10 +21,14 @@ #include "php_hash_fnv.h" const php_hash_ops php_hash_fnv132_ops = { + "fnv132", (php_hash_init_func_t) PHP_FNV132Init, (php_hash_update_func_t) PHP_FNV132Update, (php_hash_final_func_t) PHP_FNV132Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV132_SPEC, 4, 4, sizeof(PHP_FNV132_CTX), @@ -32,10 +36,14 @@ const php_hash_ops php_hash_fnv132_ops = { }; const php_hash_ops php_hash_fnv1a32_ops = { + "fnv1a32", (php_hash_init_func_t) PHP_FNV132Init, (php_hash_update_func_t) PHP_FNV1a32Update, - (php_hash_final_func_t) PHP_FNV132Final, - (php_hash_copy_func_t) php_hash_copy, + (php_hash_final_func_t) PHP_FNV132Final, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV132_SPEC, 4, 4, sizeof(PHP_FNV132_CTX), @@ -43,10 +51,14 @@ const php_hash_ops php_hash_fnv1a32_ops = { }; const php_hash_ops php_hash_fnv164_ops = { + "fnv164", (php_hash_init_func_t) PHP_FNV164Init, (php_hash_update_func_t) PHP_FNV164Update, (php_hash_final_func_t) PHP_FNV164Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV164_SPEC, 8, 4, sizeof(PHP_FNV164_CTX), @@ -54,10 +66,14 @@ const php_hash_ops php_hash_fnv164_ops = { }; const php_hash_ops php_hash_fnv1a64_ops = { + "fnv1a64", (php_hash_init_func_t) PHP_FNV164Init, (php_hash_update_func_t) PHP_FNV1a64Update, - (php_hash_final_func_t) PHP_FNV164Final, - (php_hash_copy_func_t) php_hash_copy, + (php_hash_final_func_t) PHP_FNV164Final, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_FNV164_SPEC, 8, 4, sizeof(PHP_FNV164_CTX), diff --git a/ext/hash/hash_gost.c b/ext/hash/hash_gost.c index a9e689de16ce..46ea032c32e3 100644 --- a/ext/hash/hash_gost.c +++ b/ext/hash/hash_gost.c @@ -304,11 +304,28 @@ PHP_HASH_API void PHP_GOSTFinal(unsigned char digest[32], PHP_GOST_CTX *context) ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_gost_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_GOST_CTX *ctx = (PHP_GOST_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_GOST_SPEC)) == SUCCESS + && ctx->length < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_gost_ops = { + "gost", (php_hash_init_func_t) PHP_GOSTInit, (php_hash_update_func_t) PHP_GOSTUpdate, (php_hash_final_func_t) PHP_GOSTFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_gost_unserialize, + PHP_GOST_SPEC, 32, 32, sizeof(PHP_GOST_CTX), @@ -316,10 +333,14 @@ const php_hash_ops php_hash_gost_ops = { }; const php_hash_ops php_hash_gost_crypto_ops = { + "gost-crypto", (php_hash_init_func_t) PHP_GOSTInitCrypto, (php_hash_update_func_t) PHP_GOSTUpdate, (php_hash_final_func_t) PHP_GOSTFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_gost_unserialize, + PHP_GOST_SPEC, 32, 32, sizeof(PHP_GOST_CTX), diff --git a/ext/hash/hash_haval.c b/ext/hash/hash_haval.c index 62f63cbee153..34210a8992aa 100644 --- a/ext/hash/hash_haval.c +++ b/ext/hash/hash_haval.c @@ -247,10 +247,14 @@ static void PHP_5HAVALTransform(uint32_t state[8], const unsigned char block[128 #define PHP_HASH_HAVAL_INIT(p,b) \ const php_hash_ops php_hash_##p##haval##b##_ops = { \ + "haval" #b "," #p, \ (php_hash_init_func_t) PHP_##p##HAVAL##b##Init, \ (php_hash_update_func_t) PHP_HAVALUpdate, \ (php_hash_final_func_t) PHP_HAVAL##b##Final, \ - (php_hash_copy_func_t) php_hash_copy, \ + php_hash_copy, \ + php_hash_serialize, \ + php_hash_unserialize, \ + PHP_HAVAL_SPEC, \ ((b) / 8), 128, sizeof(PHP_HAVAL_CTX), 1 }; \ PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *context) \ { int i; context->count[0] = context->count[1] = 0; \ diff --git a/ext/hash/hash_joaat.c b/ext/hash/hash_joaat.c index 10c3ca2748f5..5d5a2e53b9b3 100644 --- a/ext/hash/hash_joaat.c +++ b/ext/hash/hash_joaat.c @@ -22,10 +22,14 @@ #include "php_hash_joaat.h" const php_hash_ops php_hash_joaat_ops = { + "joaat", (php_hash_init_func_t) PHP_JOAATInit, (php_hash_update_func_t) PHP_JOAATUpdate, (php_hash_final_func_t) PHP_JOAATFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_JOAAT_SPEC, 4, 4, sizeof(PHP_JOAAT_CTX), diff --git a/ext/hash/hash_md.c b/ext/hash/hash_md.c index e5cbb49b0130..94fafbbf798a 100644 --- a/ext/hash/hash_md.c +++ b/ext/hash/hash_md.c @@ -18,10 +18,14 @@ #include "php_hash_md.h" const php_hash_ops php_hash_md5_ops = { + "md5", (php_hash_init_func_t) PHP_MD5Init, (php_hash_update_func_t) PHP_MD5Update, (php_hash_final_func_t) PHP_MD5Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_MD5_SPEC, 16, 64, sizeof(PHP_MD5_CTX), @@ -29,21 +33,31 @@ const php_hash_ops php_hash_md5_ops = { }; const php_hash_ops php_hash_md4_ops = { + "md4", (php_hash_init_func_t) PHP_MD4Init, (php_hash_update_func_t) PHP_MD4Update, (php_hash_final_func_t) PHP_MD4Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_MD4_SPEC, 16, 64, sizeof(PHP_MD4_CTX), 1 }; +static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv); + const php_hash_ops php_hash_md2_ops = { + "md2", (php_hash_init_func_t) PHP_MD2Init, (php_hash_update_func_t) PHP_MD2Update, (php_hash_final_func_t) PHP_MD2Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_md2_unserialize, + PHP_MD2_SPEC, 16, 16, sizeof(PHP_MD2_CTX), @@ -340,3 +354,16 @@ PHP_HASH_API void PHP_MD2Final(unsigned char output[16], PHP_MD2_CTX *context) memcpy(output, context->state, 16); } + +static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_MD2_CTX *ctx = (PHP_MD2_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_MD2_SPEC)) == SUCCESS + && (unsigned char) ctx->in_buffer < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} diff --git a/ext/hash/hash_ripemd.c b/ext/hash/hash_ripemd.c index 84a29f64868c..db1d1dc02b9e 100644 --- a/ext/hash/hash_ripemd.c +++ b/ext/hash/hash_ripemd.c @@ -22,10 +22,14 @@ #include "php_hash_ripemd.h" const php_hash_ops php_hash_ripemd128_ops = { + "ripemd128", (php_hash_init_func_t) PHP_RIPEMD128Init, (php_hash_update_func_t) PHP_RIPEMD128Update, (php_hash_final_func_t) PHP_RIPEMD128Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD128_SPEC, 16, 64, sizeof(PHP_RIPEMD128_CTX), @@ -33,10 +37,14 @@ const php_hash_ops php_hash_ripemd128_ops = { }; const php_hash_ops php_hash_ripemd160_ops = { + "ripemd160", (php_hash_init_func_t) PHP_RIPEMD160Init, (php_hash_update_func_t) PHP_RIPEMD160Update, (php_hash_final_func_t) PHP_RIPEMD160Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD160_SPEC, 20, 64, sizeof(PHP_RIPEMD160_CTX), @@ -44,10 +52,14 @@ const php_hash_ops php_hash_ripemd160_ops = { }; const php_hash_ops php_hash_ripemd256_ops = { + "ripemd256", (php_hash_init_func_t) PHP_RIPEMD256Init, (php_hash_update_func_t) PHP_RIPEMD256Update, (php_hash_final_func_t) PHP_RIPEMD256Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD256_SPEC, 32, 64, sizeof(PHP_RIPEMD256_CTX), @@ -55,10 +67,14 @@ const php_hash_ops php_hash_ripemd256_ops = { }; const php_hash_ops php_hash_ripemd320_ops = { + "ripemd320", (php_hash_init_func_t) PHP_RIPEMD320Init, (php_hash_update_func_t) PHP_RIPEMD320Update, (php_hash_final_func_t) PHP_RIPEMD320Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_RIPEMD320_SPEC, 40, 64, sizeof(PHP_RIPEMD320_CTX), diff --git a/ext/hash/hash_sha.c b/ext/hash/hash_sha.c index 30ab0d22adf7..4ea5b768d703 100644 --- a/ext/hash/hash_sha.c +++ b/ext/hash/hash_sha.c @@ -63,10 +63,14 @@ static void SHADecode32(uint32_t *output, const unsigned char *input, unsigned i /* }}} */ const php_hash_ops php_hash_sha1_ops = { + "sha1", (php_hash_init_func_t) PHP_SHA1Init, (php_hash_update_func_t) PHP_SHA1Update, (php_hash_final_func_t) PHP_SHA1Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA1_SPEC, 20, 64, sizeof(PHP_SHA1_CTX), @@ -76,10 +80,14 @@ const php_hash_ops php_hash_sha1_ops = { /* sha224/sha256 */ const php_hash_ops php_hash_sha256_ops = { + "sha256", (php_hash_init_func_t) PHP_SHA256Init, (php_hash_update_func_t) PHP_SHA256Update, (php_hash_final_func_t) PHP_SHA256Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA256_SPEC, 32, 64, sizeof(PHP_SHA256_CTX), @@ -87,10 +95,14 @@ const php_hash_ops php_hash_sha256_ops = { }; const php_hash_ops php_hash_sha224_ops = { + "sha224", (php_hash_init_func_t) PHP_SHA224Init, (php_hash_update_func_t) PHP_SHA224Update, (php_hash_final_func_t) PHP_SHA224Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA224_SPEC, 28, 64, sizeof(PHP_SHA224_CTX), @@ -578,10 +590,14 @@ PHP_HASH_API void PHP_SHA384Final(unsigned char digest[48], PHP_SHA384_CTX * con /* }}} */ const php_hash_ops php_hash_sha384_ops = { + "sha384", (php_hash_init_func_t) PHP_SHA384Init, (php_hash_update_func_t) PHP_SHA384Update, (php_hash_final_func_t) PHP_SHA384Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA384_SPEC, 48, 128, sizeof(PHP_SHA384_CTX), @@ -751,10 +767,14 @@ PHP_HASH_API void PHP_SHA512_224Final(unsigned char digest[28], PHP_SHA512_CTX * /* }}} */ const php_hash_ops php_hash_sha512_ops = { + "sha512", (php_hash_init_func_t) PHP_SHA512Init, (php_hash_update_func_t) PHP_SHA512Update, (php_hash_final_func_t) PHP_SHA512Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA512_SPEC, 64, 128, sizeof(PHP_SHA512_CTX), @@ -762,10 +782,14 @@ const php_hash_ops php_hash_sha512_ops = { }; const php_hash_ops php_hash_sha512_256_ops = { + "sha512/256", (php_hash_init_func_t) PHP_SHA512_256Init, (php_hash_update_func_t) PHP_SHA512_256Update, (php_hash_final_func_t) PHP_SHA512_256Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA512_SPEC, 32, 128, sizeof(PHP_SHA512_CTX), @@ -773,10 +797,14 @@ const php_hash_ops php_hash_sha512_256_ops = { }; const php_hash_ops php_hash_sha512_224_ops = { + "sha512/224", (php_hash_init_func_t) PHP_SHA512_224Init, (php_hash_update_func_t) PHP_SHA512_224Update, (php_hash_final_func_t) PHP_SHA512_224Final, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_hash_unserialize, + PHP_SHA512_SPEC, 28, 128, sizeof(PHP_SHA512_CTX), diff --git a/ext/hash/hash_sha3.c b/ext/hash/hash_sha3.c index e576adfcbfb9..52bd495f9df6 100644 --- a/ext/hash/hash_sha3.c +++ b/ext/hash/hash_sha3.c @@ -201,6 +201,22 @@ static void PHP_SHA3_Final(unsigned char* digest, ZEND_SECURE_ZERO(ctx, sizeof(PHP_SHA3_CTX)); } +static int php_sha3_unserialize(php_hashcontext_object *hash, + zend_long magic, + const zval *zv, + size_t block_size) +{ + PHP_SHA3_CTX *ctx = (PHP_SHA3_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_SHA3_SPEC)) == SUCCESS + && ctx->pos < block_size) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + // ========================================================================== #define DECLARE_SHA3_OPS(bits) \ @@ -219,11 +235,20 @@ void PHP_SHA3##bits##Final(unsigned char* digest, \ (1600 - (2 * bits)) >> 3, \ bits >> 3); \ } \ +static int php_sha3##bits##_unserialize(php_hashcontext_object *hash, \ + zend_long magic, \ + const zval *zv) { \ + return php_sha3_unserialize(hash, magic, zv, (1600 - (2 * bits)) >> 3); \ +} \ const php_hash_ops php_hash_sha3_##bits##_ops = { \ + "sha3-" #bits, \ (php_hash_init_func_t) PHP_SHA3##bits##Init, \ (php_hash_update_func_t) PHP_SHA3##bits##Update, \ (php_hash_final_func_t) PHP_SHA3##bits##Final, \ php_hash_copy, \ + php_hash_serialize, \ + php_sha3##bits##_unserialize, \ + PHP_SHA3_SPEC, \ bits >> 3, \ (1600 - (2 * bits)) >> 3, \ sizeof(PHP_SHA3_##bits##_CTX), \ @@ -237,41 +262,84 @@ const php_hash_ops php_hash_sha3_##bits##_ops = { \ #define SUCCESS SHA3_SUCCESS /* Avoid conflict between KeccacHash.h and zend_types.h */ #include "KeccakHash.h" +/* KECCAK SERIALIZATION + + Keccak_HashInstance consists of: + KeccakWidth1600_SpongeInstance { + unsigned char state[200]; + unsigned int rate; -- fixed for digest size + unsigned int byteIOIndex; -- in range [0, rate/8) + int squeezing; -- 0 normally, 1 only during finalize + } sponge; + unsigned int fixedOutputLength; -- fixed for digest size + unsigned char delimitedSuffix; -- fixed for digest size + + NB If the external sha3/ library is updated, the serialization code + may need to be updated. + + The simpler SHA3 code's serialization states are not interchangeable with + Keccak. Furthermore, the Keccak sponge state is sensitive to architecture + -- 32-bit and 64-bit implementations produce different states. It does not + appear that the state is sensitive to endianness. */ + +#if Keccak_HashInstance_ImplType == 64 +/* corresponds to sha3/generic64lc */ +# define PHP_HASH_SERIALIZE_MAGIC_KECCAK 100 +#elif Keccak_HashInstance_ImplType == 32 +/* corresponds to sha3/generic32lc */ +# define PHP_HASH_SERIALIZE_MAGIC_KECCAK 101 +#else +# error "Unknown Keccak_HashInstance_ImplType" +#endif +#define PHP_KECCAK_SPEC "b200IiIIB" -// ========================================================================== +static int php_keccak_serialize(const php_hashcontext_object *hash, zend_long *magic, zval *zv) +{ + *magic = PHP_HASH_SERIALIZE_MAGIC_KECCAK; + return php_hash_serialize_spec(hash, zv, PHP_KECCAK_SPEC); +} -static int hash_sha3_copy(const void *ops, void *orig_context, void *dest_context) +static int php_keccak_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) { - PHP_SHA3_CTX* orig = (PHP_SHA3_CTX*)orig_context; - PHP_SHA3_CTX* dest = (PHP_SHA3_CTX*)dest_context; - memcpy(dest->hashinstance, orig->hashinstance, sizeof(Keccak_HashInstance)); - return SUCCESS; + Keccak_HashInstance *ctx = (Keccak_HashInstance *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_KECCAK + && (r = php_hash_unserialize_spec(hash, zv, PHP_KECCAK_SPEC)) == SUCCESS + && ctx->sponge.byteIOIndex < ctx->sponge.rate / 8) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } } +// ========================================================================== + #define DECLARE_SHA3_OPS(bits) \ void PHP_SHA3##bits##Init(PHP_SHA3_##bits##_CTX* ctx) { \ - ctx->hashinstance = emalloc(sizeof(Keccak_HashInstance)); \ - Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx->hashinstance); \ + ZEND_ASSERT(sizeof(Keccak_HashInstance) <= sizeof(PHP_SHA3_##bits##_CTX)); \ + Keccak_HashInitialize_SHA3_##bits((Keccak_HashInstance *)ctx); \ } \ void PHP_SHA3##bits##Update(PHP_SHA3_##bits##_CTX* ctx, \ const unsigned char* input, \ size_t inputLen) { \ - Keccak_HashUpdate((Keccak_HashInstance *)ctx->hashinstance, input, inputLen * 8); \ + Keccak_HashUpdate((Keccak_HashInstance *)ctx, input, inputLen * 8); \ } \ void PHP_SHA3##bits##Final(unsigned char* digest, \ PHP_SHA3_##bits##_CTX* ctx) { \ - Keccak_HashFinal((Keccak_HashInstance *)ctx->hashinstance, digest); \ - efree(ctx->hashinstance); \ - ctx->hashinstance = NULL; \ + Keccak_HashFinal((Keccak_HashInstance *)ctx, digest); \ } \ const php_hash_ops php_hash_sha3_##bits##_ops = { \ + "sha3-" #bits, \ (php_hash_init_func_t) PHP_SHA3##bits##Init, \ (php_hash_update_func_t) PHP_SHA3##bits##Update, \ (php_hash_final_func_t) PHP_SHA3##bits##Final, \ - hash_sha3_copy, \ + php_hash_copy, \ + php_keccak_serialize, \ + php_keccak_unserialize, \ + PHP_KECCAK_SPEC, \ bits >> 3, \ (1600 - (2 * bits)) >> 3, \ - sizeof(PHP_SHA3_##bits##_CTX), \ + sizeof(PHP_SHA3_CTX), \ 1 \ } diff --git a/ext/hash/hash_snefru.c b/ext/hash/hash_snefru.c index fae767f2feec..292bfef2cb8d 100644 --- a/ext/hash/hash_snefru.c +++ b/ext/hash/hash_snefru.c @@ -189,11 +189,28 @@ PHP_HASH_API void PHP_SNEFRUFinal(unsigned char digest[32], PHP_SNEFRU_CTX *cont ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_snefru_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_SNEFRU_CTX *ctx = (PHP_SNEFRU_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_SNEFRU_SPEC)) == SUCCESS + && ctx->length < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_snefru_ops = { + "snefru", (php_hash_init_func_t) PHP_SNEFRUInit, (php_hash_update_func_t) PHP_SNEFRUUpdate, (php_hash_final_func_t) PHP_SNEFRUFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_snefru_unserialize, + PHP_SNEFRU_SPEC, 32, 32, sizeof(PHP_SNEFRU_CTX), diff --git a/ext/hash/hash_tiger.c b/ext/hash/hash_tiger.c index 399e95af0e3b..8e0f365dc49e 100644 --- a/ext/hash/hash_tiger.c +++ b/ext/hash/hash_tiger.c @@ -239,12 +239,29 @@ PHP_HASH_API void PHP_TIGER192Final(unsigned char digest[24], PHP_TIGER_CTX *con ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_tiger_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_TIGER_CTX *ctx = (PHP_TIGER_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_TIGER_SPEC)) == SUCCESS + && ctx->length < sizeof(ctx->buffer)) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + #define PHP_HASH_TIGER_OPS(p, b) \ const php_hash_ops php_hash_##p##tiger##b##_ops = { \ + "tiger" #b "," #p, \ (php_hash_init_func_t) PHP_##p##TIGERInit, \ (php_hash_update_func_t) PHP_TIGERUpdate, \ (php_hash_final_func_t) PHP_TIGER##b##Final, \ - (php_hash_copy_func_t) php_hash_copy, \ + php_hash_copy, \ + php_hash_serialize, \ + php_tiger_unserialize, \ + PHP_TIGER_SPEC, \ b/8, \ 64, \ sizeof(PHP_TIGER_CTX), \ diff --git a/ext/hash/hash_whirlpool.c b/ext/hash/hash_whirlpool.c index 5be7f80eea07..8d5cbb77370a 100644 --- a/ext/hash/hash_whirlpool.c +++ b/ext/hash/hash_whirlpool.c @@ -429,11 +429,31 @@ PHP_HASH_API void PHP_WHIRLPOOLFinal(unsigned char digest[64], PHP_WHIRLPOOL_CTX ZEND_SECURE_ZERO(context, sizeof(*context)); } +static int php_whirlpool_unserialize(php_hashcontext_object *hash, zend_long magic, const zval *zv) +{ + PHP_WHIRLPOOL_CTX *ctx = (PHP_WHIRLPOOL_CTX *) hash->context; + int r = FAILURE; + if (magic == PHP_HASH_SERIALIZE_MAGIC_SPEC + && (r = php_hash_unserialize_spec(hash, zv, PHP_WHIRLPOOL_SPEC)) == SUCCESS + && ctx->buffer.pos >= 0 + && ctx->buffer.pos < (int) sizeof(ctx->buffer.data) + && ctx->buffer.bits >= ctx->buffer.pos * 8 + && ctx->buffer.bits < ctx->buffer.pos * 8 + 8) { + return SUCCESS; + } else { + return r != SUCCESS ? r : -2000; + } +} + const php_hash_ops php_hash_whirlpool_ops = { + "whirlpool", (php_hash_init_func_t) PHP_WHIRLPOOLInit, (php_hash_update_func_t) PHP_WHIRLPOOLUpdate, (php_hash_final_func_t) PHP_WHIRLPOOLFinal, - (php_hash_copy_func_t) php_hash_copy, + php_hash_copy, + php_hash_serialize, + php_whirlpool_unserialize, + PHP_WHIRLPOOL_SPEC, 64, 64, sizeof(PHP_WHIRLPOOL_CTX), diff --git a/ext/hash/php_hash.h b/ext/hash/php_hash.h index 79bbc066d6c8..33cc6a473a7a 100644 --- a/ext/hash/php_hash.h +++ b/ext/hash/php_hash.h @@ -25,18 +25,28 @@ #define PHP_HASH_HMAC 0x0001 +#define PHP_HASH_SERIALIZE_MAGIC_SPEC 2 + #define L64 INT64_C +typedef struct _php_hashcontext_object php_hashcontext_object; + typedef void (*php_hash_init_func_t)(void *context); typedef void (*php_hash_update_func_t)(void *context, const unsigned char *buf, size_t count); typedef void (*php_hash_final_func_t)(unsigned char *digest, void *context); typedef int (*php_hash_copy_func_t)(const void *ops, void *orig_context, void *dest_context); +typedef int (*php_hash_serialize_func_t)(const php_hashcontext_object *hash, zend_long *magic, zval *zv); +typedef int (*php_hash_unserialize_func_t)(php_hashcontext_object *hash, zend_long magic, const zval *zv); typedef struct _php_hash_ops { + const char *algo; php_hash_init_func_t hash_init; php_hash_update_func_t hash_update; php_hash_final_func_t hash_final; php_hash_copy_func_t hash_copy; + php_hash_serialize_func_t hash_serialize; + php_hash_unserialize_func_t hash_unserialize; + const char *serialize_spec; size_t digest_size; size_t block_size; @@ -44,7 +54,7 @@ typedef struct _php_hash_ops { unsigned is_crypto: 1; } php_hash_ops; -typedef struct _php_hashcontext_object { +struct _php_hashcontext_object { const php_hash_ops *ops; void *context; @@ -52,7 +62,7 @@ typedef struct _php_hashcontext_object { unsigned char *key; zend_object std; -} php_hashcontext_object; +}; static inline php_hashcontext_object *php_hashcontext_from_object(zend_object *obj) { return ((php_hashcontext_object*)(obj + 1)) - 1; @@ -131,6 +141,15 @@ extern PHP_HASH_API zend_class_entry *php_hashcontext_ce; PHP_HASH_API const php_hash_ops *php_hash_fetch_ops(zend_string *algo); PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *ops); PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_context); +PHP_HASH_API int php_hash_serialize(const php_hashcontext_object *context, zend_long *magic, zval *zv); +PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *context, zend_long magic, const zval *zv); +PHP_HASH_API int php_hash_serialize_spec(const php_hashcontext_object *context, zval *zv, const char *spec); +PHP_HASH_API int php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec); + +static inline void *php_hash_alloc_context(const php_hash_ops *ops) { + /* Zero out context memory so serialization doesn't expose internals */ + return ecalloc(1, ops->context_size); +} static inline void php_hash_bin2hex(char *out, const unsigned char *in, size_t in_len) { diff --git a/ext/hash/php_hash_adler32.h b/ext/hash/php_hash_adler32.h index 184091c97122..049f16b28e95 100644 --- a/ext/hash/php_hash_adler32.h +++ b/ext/hash/php_hash_adler32.h @@ -22,6 +22,7 @@ typedef struct { uint32_t state; } PHP_ADLER32_CTX; +#define PHP_ADLER32_SPEC "l." PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context); PHP_HASH_API void PHP_ADLER32Update(PHP_ADLER32_CTX *context, const unsigned char *input, size_t len); diff --git a/ext/hash/php_hash_crc32.h b/ext/hash/php_hash_crc32.h index 346011e4176a..4c1b0fedc915 100644 --- a/ext/hash/php_hash_crc32.h +++ b/ext/hash/php_hash_crc32.h @@ -22,6 +22,7 @@ typedef struct { uint32_t state; } PHP_CRC32_CTX; +#define PHP_CRC32_SPEC "l." PHP_HASH_API void PHP_CRC32Init(PHP_CRC32_CTX *context); PHP_HASH_API void PHP_CRC32Update(PHP_CRC32_CTX *context, const unsigned char *input, size_t len); diff --git a/ext/hash/php_hash_fnv.h b/ext/hash/php_hash_fnv.h index 1cb8c78d295d..6728b2e90205 100644 --- a/ext/hash/php_hash_fnv.h +++ b/ext/hash/php_hash_fnv.h @@ -44,10 +44,12 @@ enum php_fnv_type { typedef struct { uint32_t state; } PHP_FNV132_CTX; +#define PHP_FNV132_SPEC "l." typedef struct { uint64_t state; } PHP_FNV164_CTX; +#define PHP_FNV164_SPEC "q." PHP_HASH_API void PHP_FNV132Init(PHP_FNV132_CTX *context); diff --git a/ext/hash/php_hash_gost.h b/ext/hash/php_hash_gost.h index 987eb655cb30..eb9441faa66a 100644 --- a/ext/hash/php_hash_gost.h +++ b/ext/hash/php_hash_gost.h @@ -27,6 +27,7 @@ typedef struct { unsigned char buffer[32]; const uint32_t (*tables)[4][256]; } PHP_GOST_CTX; +#define PHP_GOST_SPEC "l16l2bb32" PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *); PHP_HASH_API void PHP_GOSTUpdate(PHP_GOST_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_haval.h b/ext/hash/php_hash_haval.h index 8eaaf2d2b74f..43802b41834f 100644 --- a/ext/hash/php_hash_haval.h +++ b/ext/hash/php_hash_haval.h @@ -28,6 +28,7 @@ typedef struct { short output; void (*Transform)(uint32_t state[8], const unsigned char block[128]); } PHP_HAVAL_CTX; +#define PHP_HAVAL_SPEC "l8l2b128" #define PHP_HASH_HAVAL_INIT_DECL(p,b) PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *); \ PHP_HASH_API void PHP_HAVAL##b##Final(unsigned char*, PHP_HAVAL_CTX *); diff --git a/ext/hash/php_hash_joaat.h b/ext/hash/php_hash_joaat.h index ede108f1bb23..b0df8a67b38a 100644 --- a/ext/hash/php_hash_joaat.h +++ b/ext/hash/php_hash_joaat.h @@ -20,6 +20,7 @@ typedef struct { uint32_t state; } PHP_JOAAT_CTX; +#define PHP_JOAAT_SPEC "l." PHP_HASH_API void PHP_JOAATInit(PHP_JOAAT_CTX *context); PHP_HASH_API void PHP_JOAATUpdate(PHP_JOAAT_CTX *context, const unsigned char *input, size_t inputLen); diff --git a/ext/hash/php_hash_md.h b/ext/hash/php_hash_md.h index aed999261e93..2a677fe54d66 100644 --- a/ext/hash/php_hash_md.h +++ b/ext/hash/php_hash_md.h @@ -26,6 +26,7 @@ typedef struct { uint32_t count[2]; unsigned char buffer[64]; } PHP_MD4_CTX; +#define PHP_MD4_SPEC "l4l2b64." PHP_HASH_API void PHP_MD4Init(PHP_MD4_CTX *); PHP_HASH_API void PHP_MD4Update(PHP_MD4_CTX *context, const unsigned char *, size_t); @@ -36,8 +37,9 @@ typedef struct { unsigned char state[48]; unsigned char checksum[16]; unsigned char buffer[16]; - char in_buffer; + unsigned char in_buffer; } PHP_MD2_CTX; +#define PHP_MD2_SPEC "b48b16b16b." PHP_HASH_API void PHP_MD2Init(PHP_MD2_CTX *context); PHP_HASH_API void PHP_MD2Update(PHP_MD2_CTX *context, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_ripemd.h b/ext/hash/php_hash_ripemd.h index 131dd0ad8f0a..dd9913b85b72 100644 --- a/ext/hash/php_hash_ripemd.h +++ b/ext/hash/php_hash_ripemd.h @@ -24,24 +24,28 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD128_CTX; +#define PHP_RIPEMD128_SPEC "l4l2b64." typedef struct { uint32_t state[5]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD160_CTX; +#define PHP_RIPEMD160_SPEC "l5l2b64." typedef struct { uint32_t state[8]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD256_CTX; +#define PHP_RIPEMD256_SPEC "l8l2b64." typedef struct { uint32_t state[10]; /* state (ABCD) */ uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_RIPEMD320_CTX; +#define PHP_RIPEMD320_SPEC "l10l2b64." PHP_HASH_API void PHP_RIPEMD128Init(PHP_RIPEMD128_CTX *); PHP_HASH_API void PHP_RIPEMD128Update(PHP_RIPEMD128_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_sha.h b/ext/hash/php_hash_sha.h index 95471e7398b1..16da9273636a 100644 --- a/ext/hash/php_hash_sha.h +++ b/ext/hash/php_hash_sha.h @@ -27,6 +27,7 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 */ unsigned char buffer[64]; /* input buffer */ } PHP_SHA224_CTX; +#define PHP_SHA224_SPEC "l8l2b64." PHP_HASH_API void PHP_SHA224Init(PHP_SHA224_CTX *); PHP_HASH_API void PHP_SHA224Update(PHP_SHA224_CTX *, const unsigned char *, size_t); @@ -38,6 +39,7 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 */ unsigned char buffer[64]; /* input buffer */ } PHP_SHA256_CTX; +#define PHP_SHA256_SPEC "l8l2b64." PHP_HASH_API void PHP_SHA256Init(PHP_SHA256_CTX *); PHP_HASH_API void PHP_SHA256Update(PHP_SHA256_CTX *, const unsigned char *, size_t); @@ -49,6 +51,7 @@ typedef struct { uint64_t count[2]; /* number of bits, modulo 2^128 */ unsigned char buffer[128]; /* input buffer */ } PHP_SHA384_CTX; +#define PHP_SHA384_SPEC "q8q2b128." PHP_HASH_API void PHP_SHA384Init(PHP_SHA384_CTX *); PHP_HASH_API void PHP_SHA384Update(PHP_SHA384_CTX *, const unsigned char *, size_t); @@ -60,6 +63,7 @@ typedef struct { uint64_t count[2]; /* number of bits, modulo 2^128 */ unsigned char buffer[128]; /* input buffer */ } PHP_SHA512_CTX; +#define PHP_SHA512_SPEC "q8q2b128." PHP_HASH_API void PHP_SHA512Init(PHP_SHA512_CTX *); PHP_HASH_API void PHP_SHA512Update(PHP_SHA512_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_sha3.h b/ext/hash/php_hash_sha3.h index 256da7bda618..f75191f339fc 100644 --- a/ext/hash/php_hash_sha3.h +++ b/ext/hash/php_hash_sha3.h @@ -24,9 +24,12 @@ typedef struct { unsigned char state[200]; // 5 * 5 * sizeof(uint64) uint32_t pos; #else - void *hashinstance; + unsigned char state[224]; // this must fit a Keccak_HashInstance #endif } PHP_SHA3_CTX; +#ifdef HAVE_SLOW_HASH3 +#define PHP_SHA3_SPEC "b200l." +#endif typedef PHP_SHA3_CTX PHP_SHA3_224_CTX; typedef PHP_SHA3_CTX PHP_SHA3_256_CTX; diff --git a/ext/hash/php_hash_snefru.h b/ext/hash/php_hash_snefru.h index 25ba1183e3bc..0f339e93090a 100644 --- a/ext/hash/php_hash_snefru.h +++ b/ext/hash/php_hash_snefru.h @@ -30,6 +30,7 @@ typedef struct { unsigned char length; unsigned char buffer[32]; } PHP_SNEFRU_CTX; +#define PHP_SNEFRU_SPEC "l16l2bb32" PHP_HASH_API void PHP_SNEFRUInit(PHP_SNEFRU_CTX *); PHP_HASH_API void PHP_SNEFRUUpdate(PHP_SNEFRU_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/php_hash_tiger.h b/ext/hash/php_hash_tiger.h index 4476639040a6..d30276ddeaa2 100644 --- a/ext/hash/php_hash_tiger.h +++ b/ext/hash/php_hash_tiger.h @@ -22,9 +22,10 @@ typedef struct { uint64_t state[3]; uint64_t passed; unsigned char buffer[64]; + uint32_t length; unsigned int passes:1; - size_t length; } PHP_TIGER_CTX; +#define PHP_TIGER_SPEC "q3qb64l" PHP_HASH_API void PHP_3TIGERInit(PHP_TIGER_CTX *context); PHP_HASH_API void PHP_4TIGERInit(PHP_TIGER_CTX *context); diff --git a/ext/hash/php_hash_whirlpool.h b/ext/hash/php_hash_whirlpool.h index f96da414a531..fbd5948a4817 100644 --- a/ext/hash/php_hash_whirlpool.h +++ b/ext/hash/php_hash_whirlpool.h @@ -27,6 +27,7 @@ typedef struct { unsigned char data[64]; } buffer; } PHP_WHIRLPOOL_CTX; +#define PHP_WHIRLPOOL_SPEC "q8b32iib64." PHP_HASH_API void PHP_WHIRLPOOLInit(PHP_WHIRLPOOL_CTX *); PHP_HASH_API void PHP_WHIRLPOOLUpdate(PHP_WHIRLPOOL_CTX *, const unsigned char *, size_t); diff --git a/ext/hash/sha3/generic32lc/KeccakHash.h b/ext/hash/sha3/generic32lc/KeccakHash.h index ec35d3dab252..d53ac1c587ae 100644 --- a/ext/hash/sha3/generic32lc/KeccakHash.h +++ b/ext/hash/sha3/generic32lc/KeccakHash.h @@ -108,6 +108,8 @@ HashReturn Keccak_HashFinal(Keccak_HashInstance *hashInstance, BitSequence *hash */ HashReturn Keccak_HashSqueeze(Keccak_HashInstance *hashInstance, BitSequence *data, DataLength databitlen); +#define Keccak_HashInstance_ImplType 32 + #endif #endif diff --git a/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c b/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c index b56629122027..74ade78eb54d 100644 --- a/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c +++ b/ext/hash/sha3/generic32lc/KeccakP-1600-inplace32BI.c @@ -17,6 +17,14 @@ and related or neighboring rights to the source code in this file. #include "brg_endian.h" #include "KeccakP-1600-SnP.h" #include "SnP-Relaned.h" +#ifdef __has_feature +# if __has_feature(undefined_behavior_sanitizer) +# define ALLOW_MISALIGNED_ACCESS __attribute__((no_sanitize("alignment"))) +# endif +#endif +#ifndef ALLOW_MISALIGNED_ACCESS +# define ALLOW_MISALIGNED_ACCESS +#endif typedef unsigned char UINT8; typedef unsigned int UINT32; @@ -162,6 +170,7 @@ void KeccakP1600_AddBytesInLane(void *state, unsigned int lanePosition, const un /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_AddLanes(void *state, const unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -217,6 +226,7 @@ void KeccakP1600_OverwriteBytesInLane(void *state, unsigned int lanePosition, co /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_OverwriteLanes(void *state, const unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -304,6 +314,7 @@ void KeccakP1600_ExtractBytesInLane(const void *state, unsigned int lanePosition /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_ExtractLanes(const void *state, unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -378,6 +389,7 @@ void KeccakP1600_ExtractAndAddBytesInLane(const void *state, unsigned int lanePo /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_ExtractAndAddLanes(const void *state, const unsigned char *input, unsigned char *output, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) diff --git a/ext/hash/sha3/generic64lc/KeccakHash.h b/ext/hash/sha3/generic64lc/KeccakHash.h index ec35d3dab252..6afe6c8c9a85 100644 --- a/ext/hash/sha3/generic64lc/KeccakHash.h +++ b/ext/hash/sha3/generic64lc/KeccakHash.h @@ -108,6 +108,8 @@ HashReturn Keccak_HashFinal(Keccak_HashInstance *hashInstance, BitSequence *hash */ HashReturn Keccak_HashSqueeze(Keccak_HashInstance *hashInstance, BitSequence *data, DataLength databitlen); +#define Keccak_HashInstance_ImplType 64 + #endif #endif diff --git a/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c b/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c index 40853ff99793..fa708ad833fb 100644 --- a/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c +++ b/ext/hash/sha3/generic64lc/KeccakP-1600-opt64.c @@ -17,6 +17,14 @@ and related or neighboring rights to the source code in this file. #include #include "brg_endian.h" #include "KeccakP-1600-opt64-config.h" +#ifdef __has_feature +# if __has_feature(undefined_behavior_sanitizer) +# define ALLOW_MISALIGNED_ACCESS __attribute__((no_sanitize("alignment"))) +# endif +#endif +#ifndef ALLOW_MISALIGNED_ACCESS +# define ALLOW_MISALIGNED_ACCESS +#endif typedef unsigned char UINT8; typedef unsigned long long int UINT64; @@ -114,6 +122,7 @@ void KeccakP1600_AddBytesInLane(void *state, unsigned int lanePosition, const un /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS void KeccakP1600_AddLanes(void *state, const unsigned char *data, unsigned int laneCount) { #if (PLATFORM_BYTE_ORDER == IS_LITTLE_ENDIAN) @@ -462,6 +471,7 @@ void KeccakP1600_ExtractAndAddBytes(const void *state, const unsigned char *inpu /* ---------------------------------------------------------------- */ +ALLOW_MISALIGNED_ACCESS size_t KeccakF1600_FastLoop_Absorb(void *state, unsigned int laneCount, const unsigned char *data, size_t dataByteLen) { size_t originalDataByteLen = dataByteLen; diff --git a/ext/hash/tests/hash_serialize_001.phpt b/ext/hash/tests/hash_serialize_001.phpt new file mode 100644 index 000000000000..d515d2c5c55c --- /dev/null +++ b/ext/hash/tests/hash_serialize_001.phpt @@ -0,0 +1,361 @@ +--TEST-- +Hash: serialize()/unserialize() +--FILE-- + +--EXPECT-- +string(3) "md2" +string(32) "d5ac4ffd08f6a57b9bd402b8068392ff" +string(32) "d5ac4ffd08f6a57b9bd402b8068392ff" +string(3) "md4" +string(32) "302c45586b53a984bd3a1237cb81c15f" +string(32) "302c45586b53a984bd3a1237cb81c15f" +string(3) "md5" +string(32) "e35759f6ea35db254e415b5332269435" +string(32) "e35759f6ea35db254e415b5332269435" +string(4) "sha1" +string(40) "29f62a228f726cd728efa7a0ac6a2aba318baf15" +string(40) "29f62a228f726cd728efa7a0ac6a2aba318baf15" +string(6) "sha224" +string(56) "51fd0aa76a00b4a86103895cad5c7c2651ec7da9f4fc1e50c43ede29" +string(56) "51fd0aa76a00b4a86103895cad5c7c2651ec7da9f4fc1e50c43ede29" +string(6) "sha256" +string(64) "d3a13cf52af8e9390caed78b77b6b1e06e102204e3555d111dfd149bc5d54dba" +string(64) "d3a13cf52af8e9390caed78b77b6b1e06e102204e3555d111dfd149bc5d54dba" +string(6) "sha384" +string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3" +string(96) "6950d861ace4102b803ab8b3779d2f471968233010d2608974ab89804cef6f76162b4433d6e554e11e40a7cdcf510ea3" +string(10) "sha512/224" +string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568" +string(56) "a2573d0e3f6c3e2d174c935a35a8ea31032f04e9e83499ac3ceda568" +string(10) "sha512/256" +string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a" +string(64) "fddacab80b3a610ba024c9d75a5fe0cafe5ae7c789f829b3c5fbea8ef11ccc1a" +string(6) "sha512" +string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13" +string(128) "caced3db8e9e3a5543d5b933bcbe9e7834e6667545c3f5d4087b58ec8d78b4c8a4a5500c9b88f65f7368810ba9905e51f1cff3b25a5dccf76634108fb4e7ce13" +string(8) "sha3-224" +string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4" +string(56) "7e1126cffee98e5c4b0e9dd5c6efabd5c9356d668e9a2d3cfab724d4" +string(8) "sha3-256" +string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a" +string(64) "834abfed9197af09cbe66b7748c65a050a3755ef7a556d6764eb6eabc93b4c7a" +string(8) "sha3-384" +string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b" +string(96) "c9016992586f7a8663c5379ed892349c1140ad258f7c44ee82f61f0b8cb75c675012ea94dc1314e06699be2d1465f67b" +string(8) "sha3-512" +string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4" +string(128) "5f85341bc9c6621406bf1841c4ce01727ea8759fdf2927106c3e70a75ad9fffd095b87f995aeee844e1a2c287e1195ce809b9bdb1c31258f7fc098175b6de0b4" +string(9) "ripemd128" +string(32) "5f1bc5f5aeaf747574dd34a6535cd94a" +string(32) "5f1bc5f5aeaf747574dd34a6535cd94a" +string(9) "ripemd160" +string(40) "02a2a535ee10404c6b5cf9acb178a04fbed67269" +string(40) "02a2a535ee10404c6b5cf9acb178a04fbed67269" +string(9) "ripemd256" +string(64) "547d2ed85ca0a0e3208b5ecf4fc6a7fc1e64db8ff13493e4beaf11e4d71648e2" +string(64) "547d2ed85ca0a0e3208b5ecf4fc6a7fc1e64db8ff13493e4beaf11e4d71648e2" +string(9) "ripemd320" +string(80) "785a7df56858f550966cddfd59ce14b13bf4b18e7892c4c1ad91bf23bf67639bd2c96749ba29cfa6" +string(80) "785a7df56858f550966cddfd59ce14b13bf4b18e7892c4c1ad91bf23bf67639bd2c96749ba29cfa6" +string(9) "whirlpool" +string(128) "6e60597340640e621e25f975cef2b000b0c4c09a7af7d240a52d193002b0a8426fa7da7acc5b37ed9608016d4f396db834a0ea2f2c35f900461c9ac7e5604082" +string(128) "6e60597340640e621e25f975cef2b000b0c4c09a7af7d240a52d193002b0a8426fa7da7acc5b37ed9608016d4f396db834a0ea2f2c35f900461c9ac7e5604082" +string(10) "tiger128,3" +string(32) "8d68e78bc5e62ba925a67aa48595cfc6" +string(32) "8d68e78bc5e62ba925a67aa48595cfc6" +string(10) "tiger160,3" +string(40) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e0" +string(40) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e0" +string(10) "tiger192,3" +string(48) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e08224fc35" +string(48) "8d68e78bc5e62ba925a67aa48595cfc62cd1e5e08224fc35" +string(10) "tiger128,4" +string(32) "a26ca3f58e74fb32ee44b099cb1b5122" +string(32) "a26ca3f58e74fb32ee44b099cb1b5122" +string(10) "tiger160,4" +string(40) "a26ca3f58e74fb32ee44b099cb1b512203375900" +string(40) "a26ca3f58e74fb32ee44b099cb1b512203375900" +string(10) "tiger192,4" +string(48) "a26ca3f58e74fb32ee44b099cb1b512203375900f30b741d" +string(48) "a26ca3f58e74fb32ee44b099cb1b512203375900f30b741d" +string(6) "snefru" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(9) "snefru256" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(64) "fbe88daa74c89b9e29468fa3cd3a657d31845e21bb58dd3f8d806f5179a85c26" +string(4) "gost" +string(64) "5820c7c4a0650587538b30ef4099f2b5993069758d5c847a552e6ef7360766a5" +string(64) "5820c7c4a0650587538b30ef4099f2b5993069758d5c847a552e6ef7360766a5" +string(11) "gost-crypto" +string(64) "f7c4e35548d66aabe2b106f20515d289fde90969225d3d7b83f6dd12d694f043" +string(64) "f7c4e35548d66aabe2b106f20515d289fde90969225d3d7b83f6dd12d694f043" +string(7) "adler32" +string(8) "6f7c0928" +string(8) "6f7c0928" +string(5) "crc32" +string(8) "e5cfc160" +string(8) "e5cfc160" +string(6) "crc32b" +string(8) "69147a4e" +string(8) "69147a4e" +string(6) "crc32c" +string(8) "5e405e93" +string(8) "5e405e93" +string(6) "fnv132" +string(8) "98139504" +string(8) "98139504" +string(7) "fnv1a32" +string(8) "aae4e042" +string(8) "aae4e042" +string(6) "fnv164" +string(16) "14522659f8138684" +string(16) "14522659f8138684" +string(7) "fnv1a64" +string(16) "bebc746a33b6ab62" +string(16) "bebc746a33b6ab62" +string(5) "joaat" +string(8) "aaebf370" +string(8) "aaebf370" +string(10) "haval128,3" +string(32) "86362472c8895e68e223ef8b3711d8d9" +string(32) "86362472c8895e68e223ef8b3711d8d9" +string(10) "haval160,3" +string(40) "fabdf6905f3ba18a3c93d6a16b91e31f7222a7a4" +string(40) "fabdf6905f3ba18a3c93d6a16b91e31f7222a7a4" +string(10) "haval192,3" +string(48) "e05d0ff5723028bd5494f32c0c2494cd0b9ccf7540af7b47" +string(48) "e05d0ff5723028bd5494f32c0c2494cd0b9ccf7540af7b47" +string(10) "haval224,3" +string(56) "56b196289d8de8a22296588cf90e5b09cb6fa1b01ce8e92bca40cae2" +string(56) "56b196289d8de8a22296588cf90e5b09cb6fa1b01ce8e92bca40cae2" +string(10) "haval256,3" +string(64) "ff4d7ab0fac2ca437b945461f9b62fd16e71e9103524d5d140445a00e3d49239" +string(64) "ff4d7ab0fac2ca437b945461f9b62fd16e71e9103524d5d140445a00e3d49239" +string(10) "haval128,4" +string(32) "ee44418e0195a0c4a35d112722919a9c" +string(32) "ee44418e0195a0c4a35d112722919a9c" +string(10) "haval160,4" +string(40) "f320cce982d5201a1ccacc1c5ff835a258a97eb1" +string(40) "f320cce982d5201a1ccacc1c5ff835a258a97eb1" +string(10) "haval192,4" +string(48) "a96600107463e8e97a7fe6f260d9bf4f4587a281caafa6db" +string(48) "a96600107463e8e97a7fe6f260d9bf4f4587a281caafa6db" +string(10) "haval224,4" +string(56) "7147c9e1c1e67b942da3229f59a1ab18f121f5d7f5765ca88bc9f200" +string(56) "7147c9e1c1e67b942da3229f59a1ab18f121f5d7f5765ca88bc9f200" +string(10) "haval256,4" +string(64) "82fec42679ed5a77a841962827b88a9cddf7d677736e50bc81f1a14b99f06061" +string(64) "82fec42679ed5a77a841962827b88a9cddf7d677736e50bc81f1a14b99f06061" +string(10) "haval128,5" +string(32) "8d0b157828328ae7d34d60b4b60c1dab" +string(32) "8d0b157828328ae7d34d60b4b60c1dab" +string(10) "haval160,5" +string(40) "54dab5e10dc41503f9b8aa32ffe3bab7cf1da8a3" +string(40) "54dab5e10dc41503f9b8aa32ffe3bab7cf1da8a3" +string(10) "haval192,5" +string(48) "7d91265a1b27698279d8d95a5ee0a20014528070bf6415e7" +string(48) "7d91265a1b27698279d8d95a5ee0a20014528070bf6415e7" +string(10) "haval224,5" +string(56) "7772b2e22f2a3bce917e08cf57ebece46bb33168619a776c6f2f7234" +string(56) "7772b2e22f2a3bce917e08cf57ebece46bb33168619a776c6f2f7234" +string(10) "haval256,5" +string(64) "438a602cb1a761f7bd0a633b7bd8b3ccd0577b524d05174ca1ae1f559b9a2c2a" +string(64) "438a602cb1a761f7bd0a633b7bd8b3ccd0577b524d05174ca1ae1f559b9a2c2a" +string(3) "md2" +string(32) "5c36f61062d091a8324991132c5e8dbd" +string(32) "5c36f61062d091a8324991132c5e8dbd" +string(3) "md4" +string(32) "1d4196526aada3506efb4c7425651584" +string(32) "1d4196526aada3506efb4c7425651584" +string(3) "md5" +string(32) "f255c114bd6ce94aad092b5141c00d46" +string(32) "f255c114bd6ce94aad092b5141c00d46" +string(4) "sha1" +string(40) "a273396f056554dcd491b5dea1e7baa3b89b802b" +string(40) "a273396f056554dcd491b5dea1e7baa3b89b802b" +string(6) "sha224" +string(56) "1aee028400c56ceb5539625dc2f395abf491409336ca0f3e177a50e2" +string(56) "1aee028400c56ceb5539625dc2f395abf491409336ca0f3e177a50e2" +string(6) "sha256" +string(64) "268e7f4cf88504a53fd77136c4c4748169f46ff7150b376569ada9c374836944" +string(64) "268e7f4cf88504a53fd77136c4c4748169f46ff7150b376569ada9c374836944" +string(6) "sha384" +string(96) "0d44981d04bb11b1ef75d5c2932bd0aa2785e7bc454daac954d77e2ca10047879b58997533fc99650b20049c6cb9a6cc" +string(96) "0d44981d04bb11b1ef75d5c2932bd0aa2785e7bc454daac954d77e2ca10047879b58997533fc99650b20049c6cb9a6cc" +string(10) "sha512/224" +string(56) "cbc2bbf0028ed803af785b0f264962c84ec48d8ee0908322ef995ddb" +string(56) "cbc2bbf0028ed803af785b0f264962c84ec48d8ee0908322ef995ddb" +string(10) "sha512/256" +string(64) "2cec704878ffa7128e0c4a61eef87d1f3c823184d364dfa3fed73beb00499b00" +string(64) "2cec704878ffa7128e0c4a61eef87d1f3c823184d364dfa3fed73beb00499b00" +string(6) "sha512" +string(128) "28d7c721433782a880f840af0c3f3ea2cad4ef55de2114dda9d504cedeb110e1cf2519c49e4b5da3da4484bb6ba4fd1621ceadc6408f4410b2ebe9d83a4202c2" +string(128) "28d7c721433782a880f840af0c3f3ea2cad4ef55de2114dda9d504cedeb110e1cf2519c49e4b5da3da4484bb6ba4fd1621ceadc6408f4410b2ebe9d83a4202c2" +string(8) "sha3-224" +string(56) "9a21a5464794c2c9784df50cf89cf72234e11941bddaee93f912753e" +string(56) "9a21a5464794c2c9784df50cf89cf72234e11941bddaee93f912753e" +string(8) "sha3-256" +string(64) "57aa7a90f29b5ab66592760592780da247fd39b4c911773687450f9df8cc8ed0" +string(64) "57aa7a90f29b5ab66592760592780da247fd39b4c911773687450f9df8cc8ed0" +string(8) "sha3-384" +string(96) "5d6d7e42b241288bc707b74c50f90a37d69a4afa854ca72021a22cb379356e53b6233aea1be2f33d393d6effa9b5e36c" +string(96) "5d6d7e42b241288bc707b74c50f90a37d69a4afa854ca72021a22cb379356e53b6233aea1be2f33d393d6effa9b5e36c" +string(8) "sha3-512" +string(128) "9b88c689bc13a36e6983b32e8ee9464d63b619f246ca451d1fe2a6c9670f01e71d0c8eb245f3204d27d27c056f2a0fef76a1e3bc30fb74cccbc984dbd4883ae6" +string(128) "9b88c689bc13a36e6983b32e8ee9464d63b619f246ca451d1fe2a6c9670f01e71d0c8eb245f3204d27d27c056f2a0fef76a1e3bc30fb74cccbc984dbd4883ae6" +string(9) "ripemd128" +string(32) "f95f5e22b8875ee0c48219ae97f0674b" +string(32) "f95f5e22b8875ee0c48219ae97f0674b" +string(9) "ripemd160" +string(40) "900d615c1abe714e340f4ecd6a3d65599fd30ff4" +string(40) "900d615c1abe714e340f4ecd6a3d65599fd30ff4" +string(9) "ripemd256" +string(64) "b9799db40d1af5614118c329169cdcd2c718db6af03bf945ea7f7ba72b8e14f4" +string(64) "b9799db40d1af5614118c329169cdcd2c718db6af03bf945ea7f7ba72b8e14f4" +string(9) "ripemd320" +string(80) "d6d12c1fca7a9c4a59c1be4f40188e92a746a035219e0a6ca1ee53b36a8282527187f7dffaa57ecc" +string(80) "d6d12c1fca7a9c4a59c1be4f40188e92a746a035219e0a6ca1ee53b36a8282527187f7dffaa57ecc" +string(9) "whirlpool" +string(128) "e8c6a921e7d8eac2fd21d4df6054bb27a02321b2beb5b01b6f88c40706164e64d67ec97519bf76c8af8df896745478b78d42a0159f1a0db16777771fd9d420dc" +string(128) "e8c6a921e7d8eac2fd21d4df6054bb27a02321b2beb5b01b6f88c40706164e64d67ec97519bf76c8af8df896745478b78d42a0159f1a0db16777771fd9d420dc" +string(10) "tiger128,3" +string(32) "a99d2c0348d480dc0f3c35852926e0f1" +string(32) "a99d2c0348d480dc0f3c35852926e0f1" +string(10) "tiger160,3" +string(40) "a99d2c0348d480dc0f3c35852926e0f1e1825c16" +string(40) "a99d2c0348d480dc0f3c35852926e0f1e1825c16" +string(10) "tiger192,3" +string(48) "a99d2c0348d480dc0f3c35852926e0f1e1825c1651957ee3" +string(48) "a99d2c0348d480dc0f3c35852926e0f1e1825c1651957ee3" +string(10) "tiger128,4" +string(32) "66e2c0322421c4e5a9208e6aeed481e5" +string(32) "66e2c0322421c4e5a9208e6aeed481e5" +string(10) "tiger160,4" +string(40) "66e2c0322421c4e5a9208e6aeed481e5c4b00448" +string(40) "66e2c0322421c4e5a9208e6aeed481e5c4b00448" +string(10) "tiger192,4" +string(48) "66e2c0322421c4e5a9208e6aeed481e5c4b00448e344d9d0" +string(48) "66e2c0322421c4e5a9208e6aeed481e5c4b00448e344d9d0" +string(6) "snefru" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(9) "snefru256" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(64) "614ca924864fa0e8fa309aa0944e047d5edbfd4964a35858f4d8ec66a0fb88b0" +string(4) "gost" +string(64) "a00961e371287c71c527a41c14564f13b6ed12ac7cd9d5f5dfb3542a25e28d3b" +string(64) "a00961e371287c71c527a41c14564f13b6ed12ac7cd9d5f5dfb3542a25e28d3b" +string(11) "gost-crypto" +string(64) "68ca9aea6729dc07d995fbe071a4b5c6490bb27fc4dc65ec0e96200d5e082996" +string(64) "68ca9aea6729dc07d995fbe071a4b5c6490bb27fc4dc65ec0e96200d5e082996" +string(7) "adler32" +string(8) "d9141747" +string(8) "d9141747" +string(5) "crc32" +string(8) "59f8d3d2" +string(8) "59f8d3d2" +string(6) "crc32b" +string(8) "3ee63999" +string(8) "3ee63999" +string(6) "crc32c" +string(8) "516ad412" +string(8) "516ad412" +string(6) "fnv132" +string(8) "59ad036f" +string(8) "59ad036f" +string(7) "fnv1a32" +string(8) "fadc2cef" +string(8) "fadc2cef" +string(6) "fnv164" +string(16) "5e8c64fba6a5ffcf" +string(16) "5e8c64fba6a5ffcf" +string(7) "fnv1a64" +string(16) "893899e4415a920f" +string(16) "893899e4415a920f" +string(5) "joaat" +string(8) "836fb0e5" +string(8) "836fb0e5" +string(10) "haval128,3" +string(32) "ebeeeb05c18af1e53d2d127b561d5e0d" +string(32) "ebeeeb05c18af1e53d2d127b561d5e0d" +string(10) "haval160,3" +string(40) "f1a2c9604fb40899ad502abe0dfcec65115c8a9a" +string(40) "f1a2c9604fb40899ad502abe0dfcec65115c8a9a" +string(10) "haval192,3" +string(48) "d3a7315773a326678208650ed02510ed96cd488d74cd5231" +string(48) "d3a7315773a326678208650ed02510ed96cd488d74cd5231" +string(10) "haval224,3" +string(56) "6d7132fabc83c9ab7913748b79ecf10e25409569d3ed144177f46731" +string(56) "6d7132fabc83c9ab7913748b79ecf10e25409569d3ed144177f46731" +string(10) "haval256,3" +string(64) "7a469868ad4b92891a3a44524c58a2b8d0f3bebb92b4cf47d19bc6aba973eb95" +string(64) "7a469868ad4b92891a3a44524c58a2b8d0f3bebb92b4cf47d19bc6aba973eb95" +string(10) "haval128,4" +string(32) "6ecddb39615f43fd211839287ff38461" +string(32) "6ecddb39615f43fd211839287ff38461" +string(10) "haval160,4" +string(40) "bcd2e7821723ac22e122b8b7cbbd2daaa9a862df" +string(40) "bcd2e7821723ac22e122b8b7cbbd2daaa9a862df" +string(10) "haval192,4" +string(48) "ae74619a88dcec1fbecde28e27f009a65ecc12170824d2cd" +string(48) "ae74619a88dcec1fbecde28e27f009a65ecc12170824d2cd" +string(10) "haval224,4" +string(56) "fdaba6563f1334d40de24e311f14b324577f97c3b78b9439c408cdca" +string(56) "fdaba6563f1334d40de24e311f14b324577f97c3b78b9439c408cdca" +string(10) "haval256,4" +string(64) "289a2ba4820218bdb25a6534fbdf693f9de101362584fdd41e32244c719caa37" +string(64) "289a2ba4820218bdb25a6534fbdf693f9de101362584fdd41e32244c719caa37" +string(10) "haval128,5" +string(32) "ffa7993a4e183b245263fb1f63e27343" +string(32) "ffa7993a4e183b245263fb1f63e27343" +string(10) "haval160,5" +string(40) "375ee5ab3a9bd07a1dbe5d071e07b2afb3165e3b" +string(40) "375ee5ab3a9bd07a1dbe5d071e07b2afb3165e3b" +string(10) "haval192,5" +string(48) "c650585f93c6e041e835caedc621f8c42d8bc6829fb76789" +string(48) "c650585f93c6e041e835caedc621f8c42d8bc6829fb76789" +string(10) "haval224,5" +string(56) "bc674d465a822817d939f19b38edde083fe5668759836c203c56e3e4" +string(56) "bc674d465a822817d939f19b38edde083fe5668759836c203c56e3e4" +string(10) "haval256,5" +string(64) "da70ad9bd09ed7c9675329ea2b5279d57761807c7aeac6340d94b5d494809457" +string(64) "da70ad9bd09ed7c9675329ea2b5279d57761807c7aeac6340d94b5d494809457" +Done diff --git a/ext/hash/tests/hash_serialize_002.phpt b/ext/hash/tests/hash_serialize_002.phpt new file mode 100644 index 000000000000..b6b4ccfb5eab --- /dev/null +++ b/ext/hash/tests/hash_serialize_002.phpt @@ -0,0 +1,116 @@ +--TEST-- +Hash: serialize()/unserialize() with HASH_HMAC +--FILE-- +getMessage(), "\n"; + } +} + +echo "Done\n"; +?> +--EXPECT-- +string(3) "md2" +HashContext with HASH_HMAC option cannot be serialized +string(3) "md4" +HashContext with HASH_HMAC option cannot be serialized +string(3) "md5" +HashContext with HASH_HMAC option cannot be serialized +string(4) "sha1" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha224" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha256" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha384" +HashContext with HASH_HMAC option cannot be serialized +string(10) "sha512/224" +HashContext with HASH_HMAC option cannot be serialized +string(10) "sha512/256" +HashContext with HASH_HMAC option cannot be serialized +string(6) "sha512" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-224" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-256" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-384" +HashContext with HASH_HMAC option cannot be serialized +string(8) "sha3-512" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd128" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd160" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd256" +HashContext with HASH_HMAC option cannot be serialized +string(9) "ripemd320" +HashContext with HASH_HMAC option cannot be serialized +string(9) "whirlpool" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger128,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger160,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger192,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger128,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger160,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "tiger192,4" +HashContext with HASH_HMAC option cannot be serialized +string(6) "snefru" +HashContext with HASH_HMAC option cannot be serialized +string(9) "snefru256" +HashContext with HASH_HMAC option cannot be serialized +string(4) "gost" +HashContext with HASH_HMAC option cannot be serialized +string(11) "gost-crypto" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval128,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval160,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval192,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval224,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval256,3" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval128,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval160,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval192,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval224,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval256,4" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval128,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval160,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval192,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval224,5" +HashContext with HASH_HMAC option cannot be serialized +string(10) "haval256,5" +HashContext with HASH_HMAC option cannot be serialized +Done diff --git a/ext/hash/tests/hash_serialize_003.phpt b/ext/hash/tests/hash_serialize_003.phpt new file mode 100644 index 000000000000..a687c2aeffbd --- /dev/null +++ b/ext/hash/tests/hash_serialize_003.phpt @@ -0,0 +1,261 @@ +--TEST-- +Hash: serialization formats +--FILE-- +getMessage(), "\n", $e->getTraceAsString(); + } +} + +foreach ($serializations as $slist) { + $algo = $slist[0]; + $hash = hash($algo, "I can't remember anythingCan’t tell if this is true or dream"); + + $ctx = hash_init($algo); + hash_update($ctx, "I can't remember anything"); + $serial = base64_encode(serialize($ctx)); + if (!in_array($serial, $slist)) { + echo "$algo: unexpected serialization $serial\n"; + } + + test_serialization($serial, $hash, $algo); +} + +echo "Done\n"; +?> +--EXPECT-- +Done diff --git a/ext/hash/tests/hash_serialize_004.phpt b/ext/hash/tests/hash_serialize_004.phpt new file mode 100644 index 000000000000..ee5c08f973e2 --- /dev/null +++ b/ext/hash/tests/hash_serialize_004.phpt @@ -0,0 +1,45 @@ +--TEST-- +Hash: serialization errors +--FILE-- +__unserialize($ctx->__serialize()); +} catch (Exception $e) { + echo $e->getMessage(), "\n"; +} + +// bad formats +foreach ([ + "TzoxMToiSGFzaENvbnRleHQiOjA6e30=", // no contents + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtpOjE7aToxO2k6MDtpOjI7YTo4OntpOjA7aToxNzMyNTg0MTkzO2k6MTtpOi0yNzE3MzM4Nzk7aToyO2k6LTE3MzI1ODQxOTQ7aTozO2k6MjcxNzMzODc4O2k6NDtpOi0xMDA5NTg5Nzc2O2k6NTtpOjIwMDtpOjY7aTowO2k6NztzOjY0OiJJIGNhbid0IHJlbWVtYmVyIGFueXRoaW5nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIjt9aTozO2k6MjtpOjQ7YTowOnt9fQ==", // algorithm is int + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtzOjA6IiI7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=", // flags are string + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjE7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=", // flags indicate HASH_HMAC + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjI7aToyO3M6MTA6ImFiY2RlZmdoaWoiO2k6MztpOjI7aTo0O2E6MDp7fX0=", // serialization format wrong + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjA7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O3M6MDoiIjtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=", // serialization internals wrong + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6InNoYTEiO2k6MTtpOjA7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjA7aTo0O2E6MDp7fX0=", // bad magic number + "TzoxMToiSGFzaENvbnRleHQiOjU6e2k6MDtzOjQ6Inh4eDEiO2k6MTtpOjA7aToyO2E6ODp7aTowO2k6MTczMjU4NDE5MztpOjE7aTotMjcxNzMzODc5O2k6MjtpOi0xNzMyNTg0MTk0O2k6MztpOjI3MTczMzg3ODtpOjQ7aTotMTAwOTU4OTc3NjtpOjU7aToyMDA7aTo2O2k6MDtpOjc7czo2NDoiSSBjYW4ndCByZW1lbWJlciBhbnl0aGluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACI7fWk6MztpOjI7aTo0O2E6MDp7fX0=" // bad algorithm +] as $serial) { + try { + $ctx = unserialize(base64_decode($serial)); + echo "Unexpected success\n"; + } catch (Exception $e) { + echo $e->getMessage(), "\n"; + } +} + +echo "Done\n"; +?> +--EXPECT-- +HashContext::__unserialize called on initialized object +Incomplete or ill-formed serialization data +Incomplete or ill-formed serialization data +Incomplete or ill-formed serialization data +HashContext with HASH_HMAC option cannot be serialized +Incomplete or ill-formed serialization data ("sha1" code -1) +Incomplete or ill-formed serialization data ("sha1" code -1024) +Incomplete or ill-formed serialization data ("sha1" code -1) +Unknown hash algorithm +Done diff --git a/ext/hash/tests/serialize-context.phpt b/ext/hash/tests/serialize-context.phpt deleted file mode 100644 index 32aa0ab80607..000000000000 --- a/ext/hash/tests/serialize-context.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -Hash: Context serialization ---FILE-- -getMessage()}\n"; -} ---EXPECT-- -Exception: Serialization of 'HashContext' is not allowed diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 index 5081a6b677f2..04b9d0aea1d6 100644 --- a/ext/standard/config.m4 +++ b/ext/standard/config.m4 @@ -260,23 +260,6 @@ dnl If one of them is missing, use our own implementation, portable code is then dnl dnl TODO This is currently always enabled if test "$ac_cv_crypt_blowfish" = "no" || test "$ac_cv_crypt_des" = "no" || test "$ac_cv_crypt_ext_des" = "no" || test "$ac_cv_crypt_md5" = "no" || test "$ac_cv_crypt_sha512" = "no" || test "$ac_cv_crypt_sha256" = "no" || test "$ac_cv_func_crypt_r" != "yes" || true; then - - dnl - dnl Check for __alignof__ support in the compiler - dnl - AC_CACHE_CHECK(whether the compiler supports __alignof__, ac_cv_alignof_exists,[ - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ - ]],[[ - int align = __alignof__(int); - ]])],[ - ac_cv_alignof_exists=yes - ],[ - ac_cv_alignof_exists=no - ])]) - if test "$ac_cv_alignof_exists" = "yes"; then - AC_DEFINE([HAVE_ALIGNOF], 1, [whether the compiler supports __alignof__]) - fi - AC_DEFINE_UNQUOTED(PHP_USE_PHP_CRYPT_R, 1, [Whether PHP has to use its own crypt_r for blowfish, des, ext des and md5]) PHP_ADD_SOURCES(PHP_EXT_DIR(standard), crypt_freesec.c crypt_blowfish.c crypt_sha512.c crypt_sha256.c php_crypt_r.c) diff --git a/ext/standard/md5.h b/ext/standard/md5.h index 15417990d7bf..ac60d7fca4cd 100644 --- a/ext/standard/md5.h +++ b/ext/standard/md5.h @@ -40,6 +40,7 @@ typedef struct { unsigned char buffer[64]; uint32_t block[16]; } PHP_MD5_CTX; +#define PHP_MD5_SPEC "llllllb64l16." PHPAPI void PHP_MD5Init(PHP_MD5_CTX *ctx); PHPAPI void PHP_MD5Update(PHP_MD5_CTX *ctx, const void *data, size_t size); diff --git a/ext/standard/sha1.h b/ext/standard/sha1.h index c904969dd36f..ef98ecc29cc8 100644 --- a/ext/standard/sha1.h +++ b/ext/standard/sha1.h @@ -25,6 +25,7 @@ typedef struct { uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } PHP_SHA1_CTX; +#define PHP_SHA1_SPEC "l5l2b64." PHPAPI void PHP_SHA1Init(PHP_SHA1_CTX *); PHPAPI void PHP_SHA1Update(PHP_SHA1_CTX *, const unsigned char *, size_t); diff --git a/sapi/fuzzer/Makefile.frag b/sapi/fuzzer/Makefile.frag index 57952a8ff475..75a387d3d4ee 100644 --- a/sapi/fuzzer/Makefile.frag +++ b/sapi/fuzzer/Makefile.frag @@ -8,6 +8,9 @@ $(SAPI_FUZZER_PATH)/php-fuzz-parser: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_F $(SAPI_FUZZER_PATH)/php-fuzz-unserialize: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZE_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZE_OBJS) -o $@ +$(SAPI_FUZZER_PATH)/php-fuzz-unserializehash: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_UNSERIALIZEHASH_OBJS) + $(FUZZER_BUILD) $(PHP_FUZZER_UNSERIALIZEHASH_OBJS) -o $@ + $(SAPI_FUZZER_PATH)/php-fuzz-json: $(PHP_GLOBAL_OBJS) $(PHP_SAPI_OBJS) $(PHP_FUZZER_JSON_OBJS) $(FUZZER_BUILD) $(PHP_FUZZER_JSON_OBJS) -o $@ diff --git a/sapi/fuzzer/README.md b/sapi/fuzzer/README.md index 0645a04bc715..710a10f053a6 100644 --- a/sapi/fuzzer/README.md +++ b/sapi/fuzzer/README.md @@ -22,6 +22,7 @@ When running `make` it creates these binaries in `sapi/fuzzer/`: * `php-fuzz-parser`: Fuzzing language parser and compiler * `php-fuzz-unserialize`: Fuzzing unserialize() function +* `php-fuzz-unserializehash`: Fuzzing unserialize() for HashContext objects * `php-fuzz-json`: Fuzzing JSON parser (requires --enable-json) * `php-fuzz-exif`: Fuzzing `exif_read_data()` function (requires --enable-exif) * `php-fuzz-mbstring`: fuzzing `mb_ereg[i]()` (requires --enable-mbstring) @@ -41,6 +42,14 @@ cp -r sapi/fuzzer/corpus/unserialize ./my-unserialize-corpus sapi/fuzzer/php-fuzz-unserialize -dict=$PWD/sapi/fuzzer/dict/unserialize ./my-unserialize-corpus ``` +For the unserializehash fuzzer, generate a corpus of initial hash serializations: + +```sh +sapi/cli/php sapi/fuzzer/generate_unserializehash_corpus.php +cp -r sapi/fuzzer/corpus/unserializehash ./my-unserialize-corpus +sapi/fuzzer/php-fuzz-unserializehash ./my-unserialize-corpus +``` + For the parser fuzzer, a corpus may be generated from Zend test files: ```sh diff --git a/sapi/fuzzer/config.m4 b/sapi/fuzzer/config.m4 index 8e15a274ec1c..a148985df936 100644 --- a/sapi/fuzzer/config.m4 +++ b/sapi/fuzzer/config.m4 @@ -76,6 +76,7 @@ if test "$PHP_FUZZER" != "no"; then PHP_FUZZER_TARGET([parser], PHP_FUZZER_PARSER_OBJS) PHP_FUZZER_TARGET([unserialize], PHP_FUZZER_UNSERIALIZE_OBJS) + PHP_FUZZER_TARGET([unserializehash], PHP_FUZZER_UNSERIALIZEHASH_OBJS) PHP_FUZZER_TARGET([json], PHP_FUZZER_JSON_OBJS) if test -n "$enable_exif" && test "$enable_exif" != "no"; then diff --git a/sapi/fuzzer/fuzzer-unserializehash.c b/sapi/fuzzer/fuzzer-unserializehash.c new file mode 100644 index 000000000000..9fbc10e6a89a --- /dev/null +++ b/sapi/fuzzer/fuzzer-unserializehash.c @@ -0,0 +1,104 @@ +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + */ + + +#include "fuzzer.h" + +#include "Zend/zend.h" +#include "main/php_config.h" +#include "main/php_main.h" + +#include +#include +#include + +#include "fuzzer-sapi.h" + +#include "ext/standard/php_var.h" + +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t FullSize) { + zend_execute_data execute_data; + zend_function func; + const uint8_t *Start = memchr(Data, '|', FullSize); + if (!Start) { + return 0; + } + ++Start; + + size_t Size = (Data + FullSize) - Start; + unsigned char *orig_data = malloc(Size+1); + memcpy(orig_data, Start, Size); + orig_data[Size] = '\0'; + + if (fuzzer_request_startup()==FAILURE) { + return 0; + } + + /* Set up a dummy stack frame so that exceptions may be thrown. */ + { + memset(&execute_data, 0, sizeof(zend_execute_data)); + memset(&func, 0, sizeof(zend_function)); + + func.type = ZEND_INTERNAL_FUNCTION; + func.common.function_name = ZSTR_EMPTY_ALLOC(); + execute_data.func = &func; + EG(current_execute_data) = &execute_data; + } + + { + const unsigned char *data = orig_data; + zval result; + ZVAL_UNDEF(&result); + + php_unserialize_data_t var_hash; + PHP_VAR_UNSERIALIZE_INIT(var_hash); + php_var_unserialize(&result, (const unsigned char **) &data, data + Size, &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + + if (Z_TYPE(result) == IS_OBJECT + && zend_string_equals_literal(Z_OBJCE(result)->name, "HashContext")) { + zval args[2]; + ZVAL_COPY_VALUE(&args[0], &result); + ZVAL_STRINGL(&args[1], (char *) Data, (Start - Data) - 1); + fuzzer_call_php_func_zval("hash_update", 2, args); + zval_ptr_dtor(&args[1]); + fuzzer_call_php_func_zval("hash_final", 1, args); + } + + zval_ptr_dtor(&result); + + /* Destroy any thrown exception. */ + if (EG(exception)) { + zend_object_release(EG(exception)); + EG(exception) = NULL; + } + } + + /* Unserialize may create circular structure. Make sure we free them. + * Two calls are performed to handle objects with destructors. */ + zend_gc_collect_cycles(); + zend_gc_collect_cycles(); + php_request_shutdown(NULL); + + free(orig_data); + + return 0; +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + fuzzer_init_php(); + + /* fuzzer_shutdown_php(); */ + return 0; +} diff --git a/sapi/fuzzer/generate_unserializehash_corpus.php b/sapi/fuzzer/generate_unserializehash_corpus.php new file mode 100644 index 000000000000..04c6ea142875 --- /dev/null +++ b/sapi/fuzzer/generate_unserializehash_corpus.php @@ -0,0 +1,10 @@ +