Skip to content

Commit

Permalink
hash: Support custom algo parameters
Browse files Browse the repository at this point in the history
The concrete need on this change is to support passing an initial seed
to the murmur hash. Passing a custom seed is important in terms of
randomizing the hash function.

The suggested implementation adds a HashTable parameter to all the
init callbacks. Further on, an array with custom arguments is accepted
from `hash` or `hash_init` from the user land. Currently several things
like `hash_hkdf` are not touched, as they don't need passing custom
args.

Some convenience macros have been added to the SHA/MD families of
functions, so the consuming code doesn't have to be changed widely.

Another way to implement this is to add another type of the init that
would accept a HT with arguments. However, that would still require
touching all the context structs in all the algos. That would also
increase the size of those structs. As an init function is called just
once, the way of modifying the existing init callback has been seen
as more preferrable.

Closes GH-6400.

Signed-off-by: Anatol Belski <ab@php.net>
Co-Developed-by: Nikita Popov <nikita.ppv@googlemail.com>
Signed-off-by: Nikita Popov <nikita.ppv@googlemail.com>
Acked-by: Michael Wallner <mike@php.net>
Reviewed-by: Máté Kocsis <kocsismate@woohoolabs.com>
Reviewed-by: Eddie Kohler <ekohler@gmail.com>
  • Loading branch information
weltling committed Dec 13, 2020
1 parent 70c22de commit 110b4e9
Show file tree
Hide file tree
Showing 37 changed files with 220 additions and 110 deletions.
48 changes: 27 additions & 21 deletions ext/hash/hash.c
Expand Up @@ -349,7 +349,7 @@ PHP_HASH_API int php_hash_unserialize(php_hashcontext_object *hash, zend_long ma
/* Userspace */

static void php_hash_do_hash(
zval *return_value, zend_string *algo, char *data, size_t data_len, zend_bool raw_output, bool isfilename
zval *return_value, zend_string *algo, char *data, size_t data_len, zend_bool raw_output, bool isfilename, HashTable *args
) /* {{{ */ {
zend_string *digest;
const php_hash_ops *ops;
Expand All @@ -374,7 +374,7 @@ static void php_hash_do_hash(
}

context = php_hash_alloc_context(ops);
ops->hash_init(context);
ops->hash_init(context, args);

if (isfilename) {
char buf[1024];
Expand Down Expand Up @@ -418,15 +418,17 @@ PHP_FUNCTION(hash)
char *data;
size_t data_len;
zend_bool raw_output = 0;
HashTable *args = NULL;

ZEND_PARSE_PARAMETERS_START(2, 3)
ZEND_PARSE_PARAMETERS_START(2, 4)
Z_PARAM_STR(algo)
Z_PARAM_STRING(data, data_len)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(raw_output)
Z_PARAM_ARRAY_HT(args)
ZEND_PARSE_PARAMETERS_END();

php_hash_do_hash(return_value, algo, data, data_len, raw_output, 0);
php_hash_do_hash(return_value, algo, data, data_len, raw_output, 0, args);
}
/* }}} */

Expand All @@ -438,15 +440,17 @@ PHP_FUNCTION(hash_file)
char *data;
size_t data_len;
zend_bool raw_output = 0;
HashTable *args = NULL;

ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(algo)
Z_PARAM_STRING(data, data_len)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(raw_output)
Z_PARAM_ARRAY_HT(args)
ZEND_PARSE_PARAMETERS_END();

php_hash_do_hash(return_value, algo, data, data_len, raw_output, 1);
php_hash_do_hash(return_value, algo, data, data_len, raw_output, 1, args);
}
/* }}} */

Expand All @@ -468,7 +472,7 @@ static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *
memset(K, 0, ops->block_size);
if (key_len > ops->block_size) {
/* Reduce the key first */
ops->hash_init(context);
ops->hash_init(context, NULL);
ops->hash_update(context, key, key_len);
ops->hash_final(K, context);
} else {
Expand All @@ -479,7 +483,7 @@ static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *
}

static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops *ops, void *context, const unsigned char *key, const unsigned char *data, const zend_long data_size) {
ops->hash_init(context);
ops->hash_init(context, NULL);
ops->hash_update(context, key, ops->block_size);
ops->hash_update(context, data, data_size);
ops->hash_final(final, context);
Expand Down Expand Up @@ -522,7 +526,7 @@ static void php_hash_do_hash_hmac(
if (isfilename) {
char buf[1024];
ssize_t n;
ops->hash_init(context);
ops->hash_init(context, NULL);
ops->hash_update(context, K, ops->block_size);
while ((n = php_stream_read(stream, buf, sizeof(buf))) > 0) {
ops->hash_update(context, (unsigned char *) buf, n);
Expand Down Expand Up @@ -605,8 +609,9 @@ PHP_FUNCTION(hash_init)
void *context;
const php_hash_ops *ops;
php_hashcontext_object *hash;
HashTable *args = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|lS", &algo, &options, &key) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|lSh", &algo, &options, &key, &args) == FAILURE) {
RETURN_THROWS();
}

Expand All @@ -632,7 +637,7 @@ PHP_FUNCTION(hash_init)
hash = php_hashcontext_from_object(Z_OBJ_P(return_value));

context = php_hash_alloc_context(ops);
ops->hash_init(context);
ops->hash_init(context, args);

hash->ops = ops;
hash->context = context;
Expand All @@ -650,7 +655,7 @@ PHP_FUNCTION(hash_init)
ops->hash_update(context, (unsigned char *) ZSTR_VAL(key), ZSTR_LEN(key));
ops->hash_final((unsigned char *) K, context);
/* Make the context ready to start over */
ops->hash_init(context);
ops->hash_init(context, args);
} else {
memcpy(K, ZSTR_VAL(key), ZSTR_LEN(key));
}
Expand Down Expand Up @@ -792,7 +797,7 @@ PHP_FUNCTION(hash_final)
}

/* Feed this result into the outer hash */
hash->ops->hash_init(hash->context);
hash->ops->hash_init(hash->context, NULL);
hash->ops->hash_update(hash->context, hash->key, hash->ops->block_size);
hash->ops->hash_update(hash->context, (unsigned char *) ZSTR_VAL(digest), hash->ops->digest_size);
hash->ops->hash_final((unsigned char *) ZSTR_VAL(digest), hash->context);
Expand Down Expand Up @@ -915,7 +920,7 @@ PHP_FUNCTION(hash_hkdf)
context = php_hash_alloc_context(ops);

// Extract
ops->hash_init(context);
ops->hash_init(context, NULL);
K = emalloc(ops->block_size);
php_hash_hmac_prep_key(K, ops, context,
(unsigned char *) (salt ? ZSTR_VAL(salt) : ""), salt ? ZSTR_LEN(salt) : 0);
Expand All @@ -935,7 +940,7 @@ PHP_FUNCTION(hash_hkdf)
c[0] = (i & 0xFF);

php_hash_hmac_prep_key(K, ops, context, prk, ops->digest_size);
ops->hash_init(context);
ops->hash_init(context, NULL);
ops->hash_update(context, K, ops->block_size);

if (i > 1) {
Expand Down Expand Up @@ -980,8 +985,9 @@ PHP_FUNCTION(hash_pbkdf2)
zend_bool raw_output = 0;
const php_hash_ops *ops;
void *context;
HashTable *args;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sssl|lb", &algo, &pass, &pass_len, &salt, &salt_len, &iterations, &length, &raw_output) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Sssl|lbh", &algo, &pass, &pass_len, &salt, &salt_len, &iterations, &length, &raw_output, &args) == FAILURE) {
RETURN_THROWS();
}

Expand All @@ -1007,7 +1013,7 @@ PHP_FUNCTION(hash_pbkdf2)
}

context = php_hash_alloc_context(ops);
ops->hash_init(context);
ops->hash_init(context, args);

K1 = emalloc(ops->block_size);
K2 = emalloc(ops->block_size);
Expand Down Expand Up @@ -1212,7 +1218,7 @@ PHP_FUNCTION(mhash)
if (key) {
php_hash_do_hash_hmac(return_value, algo, data, data_len, key, key_len, 1, 0);
} else {
php_hash_do_hash(return_value, algo, data, data_len, 1, 0);
php_hash_do_hash(return_value, algo, data, data_len, 1, 0, NULL);
}

if (algo) {
Expand Down Expand Up @@ -1319,13 +1325,13 @@ PHP_FUNCTION(mhash_keygen_s2k)
}

context = php_hash_alloc_context(ops);
ops->hash_init(context);
ops->hash_init(context, NULL);

key = ecalloc(1, times * block_size);
digest = emalloc(ops->digest_size + 1);

for (i = 0; i < times; i++) {
ops->hash_init(context);
ops->hash_init(context, NULL);

for (j=0;j<i;j++) {
ops->hash_update(context, &null, 1);
Expand Down Expand Up @@ -1392,7 +1398,7 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) {
newobj->ops = oldobj->ops;
newobj->options = oldobj->options;
newobj->context = php_hash_alloc_context(newobj->ops);
newobj->ops->hash_init(newobj->context);
newobj->ops->hash_init(newobj->context, NULL);

if (SUCCESS != newobj->ops->hash_copy(newobj->ops, oldobj->context, newobj->context)) {
efree(newobj->context);
Expand Down Expand Up @@ -1529,8 +1535,8 @@ PHP_METHOD(HashContext, __unserialize)

hash->ops = ops;
hash->context = php_hash_alloc_context(ops);
ops->hash_init(hash->context);
hash->options = options;
ops->hash_init(hash->context, NULL);

unserialize_result = ops->hash_unserialize(hash, magic, hash_zv);
if (unserialize_result != SUCCESS) {
Expand Down
6 changes: 3 additions & 3 deletions ext/hash/hash.stub.php
Expand Up @@ -2,15 +2,15 @@

/** @generate-function-entries */

function hash(string $algo, string $data, bool $binary = false): string|false {}
function hash(string $algo, string $data, bool $binary = false, array $options = []): string|false {}

function hash_file(string $algo, string $filename, bool $binary = false): string|false {}
function hash_file(string $algo, string $filename, bool $binary = false, array $options = []): string|false {}

function hash_hmac(string $algo, string $data, string $key, bool $binary = false): string|false {}

function hash_hmac_file(string $algo, string $data, string $key, bool $binary = false): string|false {}

function hash_init(string $algo, int $flags = 0, string $key = ""): HashContext {}
function hash_init(string $algo, int $flags = 0, string $key = "", array $options = []): HashContext {}

function hash_update(HashContext $context, string $data): bool {}

Expand Down
2 changes: 1 addition & 1 deletion ext/hash/hash_adler32.c
Expand Up @@ -18,7 +18,7 @@
#include "php_hash.h"
#include "php_hash_adler32.h"

PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context)
PHP_HASH_API void PHP_ADLER32Init(PHP_ADLER32_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
context->state = 1;
}
Expand Down
5 changes: 4 additions & 1 deletion ext/hash/hash_arginfo.h
@@ -1,16 +1,18 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 9352e0ac98e2ac53dc15d5024f9ef0c8092c4e9c */
* Stub hash: e8466049fca2eae179adbc19bb67e71f6486ec4e */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, "false")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash_file, 0, 2, MAY_BE_STRING|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, filename, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, binary, _IS_BOOL, 0, "false")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_hash_hmac, 0, 3, MAY_BE_STRING|MAY_BE_FALSE)
Expand All @@ -26,6 +28,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_hash_init, 0, 1, HashContext, 0)
ZEND_ARG_TYPE_INFO(0, algo, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "\"\"")
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]")
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_hash_update, 0, 2, _IS_BOOL, 0)
Expand Down
2 changes: 1 addition & 1 deletion ext/hash/hash_crc32.c
Expand Up @@ -20,7 +20,7 @@
#include "php_hash_crc32_tables.h"
#include "ext/standard/crc32_x86.h"

PHP_HASH_API void PHP_CRC32Init(PHP_CRC32_CTX *context)
PHP_HASH_API void PHP_CRC32Init(PHP_CRC32_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
context->state = ~0;
}
Expand Down
4 changes: 2 additions & 2 deletions ext/hash/hash_fnv.c
Expand Up @@ -83,7 +83,7 @@ const php_hash_ops php_hash_fnv1a64_ops = {
/* {{{ PHP_FNV132Init
* 32-bit FNV-1 hash initialisation
*/
PHP_HASH_API void PHP_FNV132Init(PHP_FNV132_CTX *context)
PHP_HASH_API void PHP_FNV132Init(PHP_FNV132_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
context->state = PHP_FNV1_32_INIT;
}
Expand Down Expand Up @@ -118,7 +118,7 @@ PHP_HASH_API void PHP_FNV132Final(unsigned char digest[4], PHP_FNV132_CTX * cont
/* {{{ PHP_FNV164Init
* 64-bit FNV-1 hash initialisation
*/
PHP_HASH_API void PHP_FNV164Init(PHP_FNV164_CTX *context)
PHP_HASH_API void PHP_FNV164Init(PHP_FNV164_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
context->state = PHP_FNV1_64_INIT;
}
Expand Down
6 changes: 3 additions & 3 deletions ext/hash/hash_gost.c
Expand Up @@ -235,15 +235,15 @@ static inline void GostTransform(PHP_GOST_CTX *context, const unsigned char inpu
Gost(context, data);
}

PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *context)
PHP_HASH_API void PHP_GOSTInit(PHP_GOST_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
memset(context, 0, sizeof(*context));
context->tables = &tables_test;
}

PHP_HASH_API void PHP_GOSTInitCrypto(PHP_GOST_CTX *context)
PHP_HASH_API void PHP_GOSTInitCrypto(PHP_GOST_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
PHP_GOSTInit(context);
PHP_GOSTInit(context, NULL);
context->tables = &tables_crypto;
}

Expand Down
2 changes: 1 addition & 1 deletion ext/hash/hash_haval.c
Expand Up @@ -253,7 +253,7 @@ const php_hash_ops php_hash_##p##haval##b##_ops = { \
php_hash_unserialize, \
PHP_HAVAL_SPEC, \
((b) / 8), 128, sizeof(PHP_HAVAL_CTX), 1 }; \
PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *context) \
PHP_HASH_API void PHP_##p##HAVAL##b##Init(PHP_HAVAL_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args) \
{ int i; context->count[0] = context->count[1] = 0; \
for(i = 0; i < 8; i++) context->state[i] = D0[i]; \
context->passes = p; context->output = b; \
Expand Down
2 changes: 1 addition & 1 deletion ext/hash/hash_joaat.c
Expand Up @@ -36,7 +36,7 @@ const php_hash_ops php_hash_joaat_ops = {
0
};

PHP_HASH_API void PHP_JOAATInit(PHP_JOAAT_CTX *context)
PHP_HASH_API void PHP_JOAATInit(PHP_JOAAT_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
context->state = 0;
}
Expand Down
12 changes: 6 additions & 6 deletions ext/hash/hash_md.c
Expand Up @@ -19,7 +19,7 @@

const php_hash_ops php_hash_md5_ops = {
"md5",
(php_hash_init_func_t) PHP_MD5Init,
(php_hash_init_func_t) PHP_MD5InitArgs,
(php_hash_update_func_t) PHP_MD5Update,
(php_hash_final_func_t) PHP_MD5Final,
php_hash_copy,
Expand All @@ -34,7 +34,7 @@ const php_hash_ops php_hash_md5_ops = {

const php_hash_ops php_hash_md4_ops = {
"md4",
(php_hash_init_func_t) PHP_MD4Init,
(php_hash_init_func_t) PHP_MD4InitArgs,
(php_hash_update_func_t) PHP_MD4Update,
(php_hash_final_func_t) PHP_MD4Final,
php_hash_copy,
Expand All @@ -51,7 +51,7 @@ static int php_md2_unserialize(php_hashcontext_object *hash, zend_long magic, co

const php_hash_ops php_hash_md2_ops = {
"md2",
(php_hash_init_func_t) PHP_MD2Init,
(php_hash_init_func_t) PHP_MD2InitArgs,
(php_hash_update_func_t) PHP_MD2Update,
(php_hash_final_func_t) PHP_MD2Final,
php_hash_copy,
Expand Down Expand Up @@ -182,10 +182,10 @@ static void MD4Transform(uint32_t state[4], const unsigned char block[64])
state[3] += d;
}

/* {{{ PHP_MD4Init
/* {{{ PHP_MD4InitArgs
* MD4 initialization. Begins an MD4 operation, writing a new context.
*/
PHP_HASH_API void PHP_MD4Init(PHP_MD4_CTX * context)
PHP_HASH_API void PHP_MD4InitArgs(PHP_MD4_CTX * context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
context->count[0] = context->count[1] = 0;
/* Load magic initialization constants.
Expand Down Expand Up @@ -287,7 +287,7 @@ static const unsigned char MD2_S[256] = {
242, 239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10,
49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131, 20 };

PHP_HASH_API void PHP_MD2Init(PHP_MD2_CTX *context)
PHP_HASH_API void PHP_MD2InitArgs(PHP_MD2_CTX *context, ZEND_ATTRIBUTE_UNUSED HashTable *args)
{
memset(context, 0, sizeof(PHP_MD2_CTX));
}
Expand Down

0 comments on commit 110b4e9

Please sign in to comment.