Skip to content

Commit f2106d1

Browse files
ctzplstand
authored andcommitted
Double speed of PBKDF2
By hoisting the processing of inner/outer key blocks out of the inner loop, we calculate PBKDF2 with approx half the hash compression function applications. Speed comparison: test.php is: <?php hash_pbkdf2('sha1', 'password', 'saltsalt', 1 << 24, 0); ?> $ TIME='%U' time php test.php 24.56 $ TIME='%U' time ./sapi/cli/php test.php 12.82
1 parent e488f7b commit f2106d1

File tree

1 file changed

+35
-15
lines changed

1 file changed

+35
-15
lines changed

ext/hash/hash.c

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,12 @@ static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops
485485
ops->hash_final(final, context);
486486
}
487487

488+
static inline void php_hash_hmac_continue(unsigned char *final, const php_hash_ops *ops, void *start_context, void *context, const unsigned char *data, const zend_long data_size) {
489+
ops->hash_copy(ops, start_context, context);
490+
ops->hash_update(context, data, data_size);
491+
ops->hash_final(final, context);
492+
}
493+
488494
static void php_hash_do_hash_hmac(
489495
zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, zend_bool raw_output, bool isfilename
490496
) /* {{{ */ {
@@ -974,12 +980,12 @@ PHP_FUNCTION(hash_pbkdf2)
974980
{
975981
zend_string *returnval, *algo;
976982
char *salt, *pass = NULL;
977-
unsigned char *computed_salt, *digest, *temp, *result, *K1, *K2 = NULL;
983+
unsigned char *computed_salt, *digest, *temp, *result, *K = NULL;
978984
zend_long loops, i, j, iterations, digest_length = 0, length = 0;
979985
size_t pass_len, salt_len = 0;
980986
zend_bool raw_output = 0;
981987
const php_hash_ops *ops;
982-
void *context;
988+
void *context, *inner_context, *outer_context;
983989

984990
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sssl|lb", &algo, &pass, &pass_len, &salt, &salt_len, &iterations, &length, &raw_output) == FAILURE) {
985991
RETURN_THROWS();
@@ -1006,18 +1012,29 @@ PHP_FUNCTION(hash_pbkdf2)
10061012
RETURN_THROWS();
10071013
}
10081014

1015+
/* inner_context: hash of the key block ^ 0x36
1016+
* outer_context: hash of the key block ^ 0x5c
1017+
* context: misc scratch hashing context */
1018+
inner_context = php_hash_alloc_context(ops);
1019+
outer_context = php_hash_alloc_context(ops);
10091020
context = php_hash_alloc_context(ops);
10101021
ops->hash_init(context);
10111022

1012-
K1 = emalloc(ops->block_size);
1013-
K2 = emalloc(ops->block_size);
1023+
K = emalloc(ops->block_size);
10141024
digest = emalloc(ops->digest_size);
10151025
temp = emalloc(ops->digest_size);
10161026

10171027
/* Setup Keys that will be used for all hmac rounds */
1018-
php_hash_hmac_prep_key(K1, ops, context, (unsigned char *) pass, pass_len);
1019-
/* Convert K1 to opad -- 0x6A = 0x36 ^ 0x5C */
1020-
php_hash_string_xor_char(K2, K1, 0x6A, ops->block_size);
1028+
php_hash_hmac_prep_key(K, ops, context, (unsigned char *) pass, pass_len);
1029+
1030+
ops->hash_init(inner_context);
1031+
ops->hash_update(inner_context, K, ops->block_size);
1032+
1033+
/* Convert K from ipad to opad -- 0x6A = 0x36 ^ 0x5C */
1034+
php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
1035+
1036+
ops->hash_init(outer_context);
1037+
ops->hash_update(outer_context, K, ops->block_size);
10211038

10221039
/* Setup Main Loop to build a long enough result */
10231040
if (length == 0) {
@@ -1047,8 +1064,8 @@ PHP_FUNCTION(hash_pbkdf2)
10471064
computed_salt[salt_len + 2] = (unsigned char) ((i & 0xFF00) >> 8);
10481065
computed_salt[salt_len + 3] = (unsigned char) (i & 0xFF);
10491066

1050-
php_hash_hmac_round(digest, ops, context, K1, computed_salt, (zend_long) salt_len + 4);
1051-
php_hash_hmac_round(digest, ops, context, K2, digest, ops->digest_size);
1067+
php_hash_hmac_continue(digest, ops, inner_context, context ,computed_salt, (long) salt_len + 4);
1068+
php_hash_hmac_continue(digest, ops, outer_context, context, digest, ops->digest_size);
10521069
/* } */
10531070

10541071
/* temp = digest */
@@ -1060,8 +1077,8 @@ PHP_FUNCTION(hash_pbkdf2)
10601077
*/
10611078
for (j = 1; j < iterations; j++) {
10621079
/* digest = hash_hmac(digest, password) { */
1063-
php_hash_hmac_round(digest, ops, context, K1, digest, ops->digest_size);
1064-
php_hash_hmac_round(digest, ops, context, K2, digest, ops->digest_size);
1080+
php_hash_hmac_continue(digest, ops, inner_context, context, digest, ops->digest_size);
1081+
php_hash_hmac_continue(digest, ops, outer_context, context, digest, ops->digest_size);
10651082
/* } */
10661083
/* temp ^= digest */
10671084
php_hash_string_xor(temp, temp, digest, ops->digest_size);
@@ -1070,12 +1087,15 @@ PHP_FUNCTION(hash_pbkdf2)
10701087
memcpy(result + ((i - 1) * ops->digest_size), temp, ops->digest_size);
10711088
}
10721089
/* Zero potentially sensitive variables */
1073-
ZEND_SECURE_ZERO(K1, ops->block_size);
1074-
ZEND_SECURE_ZERO(K2, ops->block_size);
1090+
ZEND_SECURE_ZERO(K, ops->block_size);
10751091
ZEND_SECURE_ZERO(computed_salt, salt_len + 4);
1076-
efree(K1);
1077-
efree(K2);
1092+
ZEND_SECURE_ZERO(inner_context, ops->context_size);
1093+
ZEND_SECURE_ZERO(outer_context, ops->context_size);
1094+
ZEND_SECURE_ZERO(context, ops->context_size);
1095+
efree(K);
10781096
efree(computed_salt);
1097+
efree(inner_context);
1098+
efree(outer_context);
10791099
efree(context);
10801100
efree(digest);
10811101
efree(temp);

0 commit comments

Comments
 (0)