Skip to content

Commit 0e39d92

Browse files
committed
Improve PBKDF2 performance
This is a security bug, since it makes PBKDF2 faster for attackers than defenders. So it should be fixed from PHP 8.2 on.
1 parent 1b47a92 commit 0e39d92

1 file changed

Lines changed: 22 additions & 5 deletions

File tree

‎ext/hash/hash.c‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,12 @@ static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops
497497
ops->hash_final(final, context);
498498
}
499499

500+
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) {
501+
ZEND_ASSERT(SUCCESS == ops->hash_copy(ops, base_context, context));
502+
ops->hash_update(context, data, data_size);
503+
ops->hash_final(final, context);
504+
}
505+
500506
static void php_hash_do_hash_hmac(
501507
zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, bool raw_output, bool isfilename
502508
) /* {{{ */ {
@@ -996,7 +1002,7 @@ PHP_FUNCTION(hash_pbkdf2)
9961002
size_t pass_len, salt_len = 0;
9971003
bool raw_output = 0;
9981004
const php_hash_ops *ops;
999-
void *context;
1005+
void *context, *inner_context, *outer_context;
10001006
HashTable *args = NULL;
10011007

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

1036+
inner_context = php_hash_alloc_context(ops);
1037+
outer_context = php_hash_alloc_context(ops);
1038+
10301039
K1 = emalloc(ops->block_size);
10311040
K2 = emalloc(ops->block_size);
10321041
digest = emalloc(ops->digest_size);
@@ -1037,6 +1046,12 @@ PHP_FUNCTION(hash_pbkdf2)
10371046
/* Convert K1 to opad -- 0x6A = 0x36 ^ 0x5C */
10381047
php_hash_string_xor_char(K2, K1, 0x6A, ops->block_size);
10391048

1049+
/* Precompute the hash states after absorbing the ipad and opad blocks */
1050+
ops->hash_init(inner_context, NULL);
1051+
ops->hash_update(inner_context, K1, ops->block_size);
1052+
ops->hash_init(outer_context, NULL);
1053+
ops->hash_update(outer_context, K2, ops->block_size);
1054+
10401055
/* Setup Main Loop to build a long enough result */
10411056
if (length == 0) {
10421057
length = ops->digest_size;
@@ -1065,8 +1080,8 @@ PHP_FUNCTION(hash_pbkdf2)
10651080
computed_salt[salt_len + 2] = (unsigned char) ((i & 0xFF00) >> 8);
10661081
computed_salt[salt_len + 3] = (unsigned char) (i & 0xFF);
10671082

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

10721087
/* temp = digest */
@@ -1078,8 +1093,8 @@ PHP_FUNCTION(hash_pbkdf2)
10781093
*/
10791094
for (j = 1; j < iterations; j++) {
10801095
/* digest = hash_hmac(digest, password) { */
1081-
php_hash_hmac_round(digest, ops, context, K1, digest, ops->digest_size);
1082-
php_hash_hmac_round(digest, ops, context, K2, digest, ops->digest_size);
1096+
php_hash_hmac_round_with_copy(digest, ops, inner_context, context, digest, ops->digest_size);
1097+
php_hash_hmac_round_with_copy(digest, ops, outer_context, context, digest, ops->digest_size);
10831098
/* } */
10841099
/* temp ^= digest */
10851100
php_hash_string_xor(temp, temp, digest, ops->digest_size);
@@ -1095,6 +1110,8 @@ PHP_FUNCTION(hash_pbkdf2)
10951110
efree(K2);
10961111
efree(computed_salt);
10971112
efree(context);
1113+
efree(inner_context);
1114+
efree(outer_context);
10981115
efree(digest);
10991116
efree(temp);
11001117

0 commit comments

Comments
 (0)