Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ PHP NEWS
. Fixed GHSA-62xp-839h-2637 (IPv6 ACL bypass in FastCGI listen.allowed_clients
due to partial address comparison). (CVE-2026-91768) (Alexandre Daubois)

- Hash:
. Improved performance of hash_pbkdf2. (Sjoerd Langkemper)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to add a UPGRADING entry.


- MySQLnd:
. Fixed GHSA-r6x9-5r99-36j7 (Various packet overreads in mysqlnd wire
protocol). (CVE-2025-1218) (Jakub Zelenka, Nora Dossche)
Expand Down
29 changes: 23 additions & 6 deletions ext/hash/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ PHP_HASH_API void php_hash_register_algo(const char *algo, const php_hash_ops *o
}
/* }}} */

PHP_HASH_API int php_hash_copy(const void *ops, void *orig_context, void *dest_context) /* {{{ */
PHP_HASH_API int php_hash_copy(const void *ops, const void *orig_context, void *dest_context) /* {{{ */
{
php_hash_ops *hash_ops = (php_hash_ops *)ops;

Expand Down Expand Up @@ -497,6 +497,12 @@ static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops
ops->hash_final(final, context);
}

static inline void php_hash_hmac_round_with_copy(unsigned char *final, const php_hash_ops *ops, const void *base_context, void *context, const unsigned char *data, const zend_long data_size) {
ops->hash_copy(ops, base_context, context);
ops->hash_update(context, data, data_size);
ops->hash_final(final, context);
}

static void php_hash_do_hash_hmac(
zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, bool raw_output, bool isfilename
) /* {{{ */ {
Expand Down Expand Up @@ -996,7 +1002,7 @@ PHP_FUNCTION(hash_pbkdf2)
size_t pass_len, salt_len = 0;
bool raw_output = 0;
const php_hash_ops *ops;
void *context;
void *context, *inner_context, *outer_context;
HashTable *args = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sssl|lbh", &algo, &pass, &pass_len, &salt, &salt_len, &iterations, &length, &raw_output, &args) == FAILURE) {
Expand Down Expand Up @@ -1027,6 +1033,9 @@ PHP_FUNCTION(hash_pbkdf2)
context = php_hash_alloc_context(ops);
ops->hash_init(context, args);

inner_context = php_hash_alloc_context(ops);
outer_context = php_hash_alloc_context(ops);

K1 = emalloc(ops->block_size);
K2 = emalloc(ops->block_size);
digest = emalloc(ops->digest_size);
Expand All @@ -1037,6 +1046,12 @@ PHP_FUNCTION(hash_pbkdf2)
/* Convert K1 to opad -- 0x6A = 0x36 ^ 0x5C */
php_hash_string_xor_char(K2, K1, 0x6A, ops->block_size);

/* Precompute the hash states after absorbing the ipad and opad blocks */
ops->hash_init(inner_context, NULL);
ops->hash_update(inner_context, K1, ops->block_size);
ops->hash_init(outer_context, NULL);
ops->hash_update(outer_context, K2, ops->block_size);

/* Setup Main Loop to build a long enough result */
if (length == 0) {
length = ops->digest_size;
Expand Down Expand Up @@ -1065,8 +1080,8 @@ PHP_FUNCTION(hash_pbkdf2)
computed_salt[salt_len + 2] = (unsigned char) ((i & 0xFF00) >> 8);
computed_salt[salt_len + 3] = (unsigned char) (i & 0xFF);

php_hash_hmac_round(digest, ops, context, K1, computed_salt, (zend_long) salt_len + 4);
php_hash_hmac_round(digest, ops, context, K2, digest, ops->digest_size);
php_hash_hmac_round_with_copy(digest, ops, inner_context, context, computed_salt, (zend_long) salt_len + 4);
php_hash_hmac_round_with_copy(digest, ops, outer_context, context, digest, ops->digest_size);
/* } */

/* temp = digest */
Expand All @@ -1078,8 +1093,8 @@ PHP_FUNCTION(hash_pbkdf2)
*/
for (j = 1; j < iterations; j++) {
/* digest = hash_hmac(digest, password) { */
php_hash_hmac_round(digest, ops, context, K1, digest, ops->digest_size);
php_hash_hmac_round(digest, ops, context, K2, digest, ops->digest_size);
php_hash_hmac_round_with_copy(digest, ops, inner_context, context, digest, ops->digest_size);
php_hash_hmac_round_with_copy(digest, ops, outer_context, context, digest, ops->digest_size);
/* } */
/* temp ^= digest */
php_hash_string_xor(temp, temp, digest, ops->digest_size);
Expand All @@ -1095,6 +1110,8 @@ PHP_FUNCTION(hash_pbkdf2)
efree(K2);
efree(computed_salt);
efree(context);
efree(inner_context);
efree(outer_context);
efree(digest);
efree(temp);

Expand Down
4 changes: 2 additions & 2 deletions ext/hash/php_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ typedef struct _php_hashcontext_object php_hashcontext_object;
typedef void (*php_hash_init_func_t)(void *context, HashTable *args);
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_copy_func_t)(const void *ops, const 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);

Expand Down Expand Up @@ -147,7 +147,7 @@ extern zend_module_entry hash_module_entry;
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_copy(const void *ops, const 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);
Expand Down
Loading