diff --git a/ext/hash/hash.c b/ext/hash/hash.c index a950d824700f..c5a4e24a18f5 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -487,6 +487,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) { + ZEND_ASSERT(SUCCESS == 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 ) /* {{{ */ { @@ -982,7 +988,7 @@ PHP_FUNCTION(hash_pbkdf2) size_t pass_len, salt_len = 0; bool raw_output = false; 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) { @@ -1013,6 +1019,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); @@ -1023,6 +1032,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; @@ -1051,8 +1066,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 */ @@ -1064,8 +1079,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); @@ -1081,6 +1096,8 @@ PHP_FUNCTION(hash_pbkdf2) efree(K2); efree(computed_salt); php_hash_free_context(ops, context); + php_hash_free_context(ops, inner_context); + php_hash_free_context(ops, outer_context); efree(digest); efree(temp);