Skip to content

Commit 1ead882

Browse files
committed
Move HMAC hashing logic to hash.c
1 parent e1b7f13 commit 1ead882

4 files changed

Lines changed: 41 additions & 59 deletions

File tree

‎ext/hash/hash.c‎

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -487,13 +487,32 @@ static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops
487487
ops->hash_final(final, context);
488488
}
489489

490+
/* Computes HMAC(key, data) using the given hash algorithm and writes the
491+
* result into `digest`, which must be at least ops->digest_size bytes.
492+
* Intended for internal (C-level) use by other extensions; avoids the
493+
* zval/zend_string overhead of the userland hash_hmac() API. */
494+
PHPAPI void php_hash_hmac(const php_hash_ops *ops, const unsigned char *key, size_t key_len,
495+
const unsigned char *data, size_t data_len, unsigned char *digest)
496+
{
497+
void *context = php_hash_alloc_context(ops);
498+
unsigned char *K = emalloc(ops->block_size);
499+
500+
php_hash_hmac_prep_key(K, ops, context, key, key_len);
501+
php_hash_hmac_round(digest, ops, context, K, data, data_len);
502+
503+
php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
504+
php_hash_hmac_round(digest, ops, context, K, digest, ops->digest_size);
505+
506+
ZEND_SECURE_ZERO(K, ops->block_size);
507+
efree(K);
508+
php_hash_free_context(ops, context);
509+
}
510+
490511
static void php_hash_do_hash_hmac(
491512
zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, bool raw_output, bool isfilename
492513
) /* {{{ */ {
493514
zend_string *digest;
494-
unsigned char *K;
495515
const php_hash_ops *ops;
496-
void *context;
497516
php_stream *stream = NULL;
498517

499518
ops = php_hash_fetch_ops(algo);
@@ -514,16 +533,16 @@ static void php_hash_do_hash_hmac(
514533
}
515534
}
516535

517-
context = php_hash_alloc_context(ops);
518-
519-
K = emalloc(ops->block_size);
520536
digest = zend_string_alloc(ops->digest_size, 0);
521537

522-
php_hash_hmac_prep_key(K, ops, context, (unsigned char *) key, key_len);
523-
524538
if (isfilename) {
539+
void *context = php_hash_alloc_context(ops);
540+
unsigned char *K = emalloc(ops->block_size);
525541
char buf[1024];
526542
ssize_t n;
543+
544+
php_hash_hmac_prep_key(K, ops, context, (unsigned char *) key, key_len);
545+
527546
ops->hash_init(context, NULL);
528547
ops->hash_update(context, K, ops->block_size);
529548
while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
@@ -536,20 +555,17 @@ static void php_hash_do_hash_hmac(
536555
zend_string_efree(digest);
537556
RETURN_FALSE;
538557
}
539-
540558
ops->hash_final((unsigned char *) ZSTR_VAL(digest), context);
541-
} else {
542-
php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) data, data_len);
543-
}
544-
545-
php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
546559

547-
php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) ZSTR_VAL(digest), ops->digest_size);
560+
php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
561+
php_hash_hmac_round((unsigned char *) ZSTR_VAL(digest), ops, context, K, (unsigned char *) ZSTR_VAL(digest), ops->digest_size);
548562

549-
/* Zero the key */
550-
ZEND_SECURE_ZERO(K, ops->block_size);
551-
efree(K);
552-
php_hash_free_context(ops, context);
563+
ZEND_SECURE_ZERO(K, ops->block_size);
564+
efree(K);
565+
php_hash_free_context(ops, context);
566+
} else {
567+
php_hash_hmac(ops, (unsigned char *) key, key_len, (unsigned char *) data, data_len, (unsigned char *) ZSTR_VAL(digest));
568+
}
553569

554570
if (raw_output) {
555571
ZSTR_VAL(digest)[ops->digest_size] = 0;

‎ext/hash/php_hash.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ PHP_HASH_API hash_spec_result php_hash_serialize(const php_hashcontext_object *c
159159
PHP_HASH_API hash_spec_result php_hash_unserialize(php_hashcontext_object *context, zend_long magic, const zval *zv);
160160
PHP_HASH_API hash_spec_result php_hash_serialize_spec(const php_hashcontext_object *context, zval *zv, const char *spec);
161161
PHP_HASH_API hash_spec_result php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec);
162+
PHP_HASH_API void php_hash_hmac(const php_hash_ops *ops, const unsigned char *key, size_t key_len,
163+
const unsigned char *data, size_t data_len, unsigned char *digest);
162164

163165
static inline void *php_hash_alloc_context(const php_hash_ops *ops) {
164166
/* Zero out context memory so serialization doesn't expose internals */

‎ext/hash/php_hash_sha.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,6 @@ PHP_HASH_API void PHP_SHA512_224InitArgs(PHP_SHA512_CTX *, ZEND_ATTRIBUTE_UNUSED
103103
#define PHP_SHA512_224Update PHP_SHA512Update
104104
PHP_HASH_API void PHP_SHA512_224Final(unsigned char[28], PHP_SHA512_CTX *);
105105

106+
extern PHP_HASH_API const php_hash_ops php_hash_sha256_ops;
107+
106108
#endif /* PHP_HASH_SHA_H */

‎ext/standard/password.c‎

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
#include "zend_interfaces.h"
2525
#include "info.h"
2626
#include "ext/random/php_random_csprng.h"
27-
#include "ext/hash/php_hash.h" /* Needed for PHP_HASH_API in ext/hash/php_hash_sha.h */
27+
#include "ext/hash/php_hash.h"
2828
#include "ext/hash/php_hash_sha.h"
2929
#include "password_arginfo.h"
3030
#ifdef HAVE_ARGON2LIB
@@ -249,44 +249,6 @@ static bool php_password_b64char(unsigned char c)
249249
(c >= '0' && c <= '9');
250250
}
251251

252-
static void php_password_hmac_sha256(const unsigned char *key, size_t key_len,
253-
const unsigned char *msg, size_t msg_len, unsigned char digest[32])
254-
{
255-
PHP_SHA256_CTX ctx;
256-
unsigned char k_ipad[64], k_opad[64], key_hash[32];
257-
size_t i;
258-
259-
/* If the key is longer than the block size (64), hash it first. Our key is
260-
* always the 22-byte salt, so this branch is never taken in practice. */
261-
if (key_len > 64) {
262-
PHP_SHA256Init(&ctx);
263-
PHP_SHA256Update(&ctx, key, key_len);
264-
PHP_SHA256Final(key_hash, &ctx);
265-
key = key_hash;
266-
key_len = 32;
267-
}
268-
269-
for (i = 0; i < 64; i++) {
270-
unsigned char k = (i < key_len) ? key[i] : 0;
271-
k_ipad[i] = k ^ 0x36;
272-
k_opad[i] = k ^ 0x5c;
273-
}
274-
275-
PHP_SHA256Init(&ctx);
276-
PHP_SHA256Update(&ctx, k_ipad, 64);
277-
PHP_SHA256Update(&ctx, msg, msg_len);
278-
PHP_SHA256Final(key_hash, &ctx);
279-
280-
PHP_SHA256Init(&ctx);
281-
PHP_SHA256Update(&ctx, k_opad, 64);
282-
PHP_SHA256Update(&ctx, key_hash, 32);
283-
PHP_SHA256Final(digest, &ctx);
284-
285-
ZEND_SECURE_ZERO(k_ipad, sizeof(k_ipad));
286-
ZEND_SECURE_ZERO(k_opad, sizeof(k_opad));
287-
ZEND_SECURE_ZERO(key_hash, sizeof(key_hash));
288-
}
289-
290252
/* Validate a bcrypt-sha256 hash and, on success, extract its cost, salt and
291253
* digest. Layout:
292254
* $bcrypt-sha256$v=2,t=2b,r=<cost>$<salt:22>$<digest:31> (82 or 83 bytes)
@@ -414,7 +376,7 @@ static zend_string *php_password_bcrypt_sha256_hash(const zend_string *password,
414376
zend_string *key;
415377
{
416378
unsigned char mac[32];
417-
php_password_hmac_sha256((const unsigned char *) ZSTR_VAL(salt), ZSTR_LEN(salt),
379+
php_hash_hmac(&php_hash_sha256_ops, (const unsigned char *) ZSTR_VAL(salt), ZSTR_LEN(salt),
418380
(const unsigned char *) ZSTR_VAL(password), ZSTR_LEN(password), mac);
419381

420382
key = php_base64_encode(mac, sizeof(mac));
@@ -464,7 +426,7 @@ static bool php_password_bcrypt_sha256_verify(const zend_string *password, const
464426
zend_string *key;
465427
{
466428
unsigned char mac[32];
467-
php_password_hmac_sha256((const unsigned char *) salt, 22,
429+
php_hash_hmac(&php_hash_sha256_ops, (const unsigned char *) salt, 22,
468430
(const unsigned char *) ZSTR_VAL(password), ZSTR_LEN(password), mac);
469431

470432
key = php_base64_encode(mac, sizeof(mac));

0 commit comments

Comments
 (0)