From f6847728f90d45d3a92b683c9a70430f5e064952 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Sat, 4 Jul 2015 18:37:12 +0100 Subject: [PATCH] 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: $ TIME='%U' time php test.php 24.56 $ TIME='%U' time ./sapi/cli/php test.php 12.82 --- ext/hash/hash.c | 51 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 780b2e012238f..ca654b1375514 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -239,6 +239,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_continue(unsigned char *final, const php_hash_ops *ops, void *start_context, void *context, const unsigned char *data, const long data_size) { + ops->hash_copy(ops, start_context, context); + ops->hash_update(context, data, data_size); + ops->hash_final(final, context); +} + static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_bool raw_output_default) /* {{{ */ { char *algo, *data, *digest, *key, *K; @@ -615,12 +621,12 @@ Returns lowercase hexits by default */ PHP_FUNCTION(hash_pbkdf2) { char *returnval, *algo, *salt, *pass; - unsigned char *computed_salt, *digest, *temp, *result, *K1, *K2; + unsigned char *computed_salt, *digest, *temp, *result, *K; long loops, i, j, iterations, length = 0, digest_length; int algo_len, pass_len, salt_len; zend_bool raw_output = 0; const php_hash_ops *ops; - void *context; + void *context, *inner_context, *outer_context; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sssl|lb", &algo, &algo_len, &pass, &pass_len, &salt, &salt_len, &iterations, &length, &raw_output) == FAILURE) { return; @@ -647,18 +653,28 @@ PHP_FUNCTION(hash_pbkdf2) RETURN_FALSE; } + /* inner_context: hash of the key block ^ 0x36 + * outer_context: hash of the key block ^ 0x5c + * context: misc scratch hashing context */ + inner_context = emalloc(ops->context_size); + outer_context = emalloc(ops->context_size); context = emalloc(ops->context_size); - ops->hash_init(context); - K1 = emalloc(ops->block_size); - K2 = emalloc(ops->block_size); + K = emalloc(ops->block_size); digest = emalloc(ops->digest_size); temp = emalloc(ops->digest_size); /* Setup Keys that will be used for all hmac rounds */ - php_hash_hmac_prep_key(K1, ops, context, (unsigned char *) pass, pass_len); - /* Convert K1 to opad -- 0x6A = 0x36 ^ 0x5C */ - php_hash_string_xor_char(K2, K1, 0x6A, ops->block_size); + php_hash_hmac_prep_key(K, ops, context, (unsigned char *) pass, pass_len); + + ops->hash_init(inner_context); + ops->hash_update(inner_context, K, ops->block_size); + + /* Convert K from ipad to opad -- 0x6A = 0x36 ^ 0x5C */ + php_hash_string_xor_char(K, K, 0x6A, ops->block_size); + + ops->hash_init(outer_context); + ops->hash_update(outer_context, K, ops->block_size); /* Setup Main Loop to build a long enough result */ if (length == 0) { @@ -688,8 +704,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, (long) salt_len + 4); - php_hash_hmac_round(digest, ops, context, K2, digest, ops->digest_size); + php_hash_hmac_continue(digest, ops, inner_context, context ,computed_salt, (long) salt_len + 4); + php_hash_hmac_continue(digest, ops, outer_context, context, digest, ops->digest_size); /* } */ /* temp = digest */ @@ -701,8 +717,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_continue(digest, ops, inner_context, context, digest, ops->digest_size); + php_hash_hmac_continue(digest, ops, outer_context, context, digest, ops->digest_size); /* } */ /* temp ^= digest */ php_hash_string_xor(temp, temp, digest, ops->digest_size); @@ -711,12 +727,15 @@ PHP_FUNCTION(hash_pbkdf2) memcpy(result + ((i - 1) * ops->digest_size), temp, ops->digest_size); } /* Zero potentially sensitive variables */ - memset(K1, 0, ops->block_size); - memset(K2, 0, ops->block_size); + memset(K, 0, ops->block_size); memset(computed_salt, 0, salt_len + 4); - efree(K1); - efree(K2); + memset(inner_context, 0, ops->context_size); + memset(outer_context, 0, ops->context_size); + memset(context, 0, ops->context_size); + efree(K); efree(computed_salt); + efree(inner_context); + efree(outer_context); efree(context); efree(digest); efree(temp);