summaryrefslogtreecommitdiff
path: root/ext/hash/hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/hash/hash.c')
-rw-r--r--ext/hash/hash.c148
1 files changed, 137 insertions, 11 deletions
diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index 0b05d28ad2..06c9f36705 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -31,12 +31,6 @@
static int php_hash_le_hash;
HashTable php_hash_hashtable;
-#if (PHP_MAJOR_VERSION >= 5)
-# define DEFAULT_CONTEXT FG(default_context)
-#else
-# define DEFAULT_CONTEXT NULL
-#endif
-
#ifdef PHP_MHASH_BC
struct mhash_bc_entry {
char *mhash_name;
@@ -140,7 +134,7 @@ static void php_hash_do_hash(INTERNAL_FUNCTION_PARAMETERS, int isfilename, zend_
php_error_docref(NULL, E_WARNING, "Invalid path");
RETURN_FALSE;
}
- stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
+ stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context));
if (!stream) {
/* Stream will report errors opening file */
RETURN_FALSE;
@@ -214,7 +208,7 @@ static inline void php_hash_string_xor(unsigned char *out, const unsigned char *
static inline void php_hash_hmac_prep_key(unsigned char *K, const php_hash_ops *ops, void *context, const unsigned char *key, const size_t key_len) {
memset(K, 0, ops->block_size);
- if (key_len > ops->block_size) {
+ if (key_len > (size_t)ops->block_size) {
/* Reduce the key first */
ops->hash_init(context);
ops->hash_update(context, key, key_len);
@@ -259,7 +253,7 @@ static void php_hash_do_hash_hmac(INTERNAL_FUNCTION_PARAMETERS, int isfilename,
php_error_docref(NULL, E_WARNING, "Invalid path");
RETURN_FALSE;
}
- stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, DEFAULT_CONTEXT);
+ stream = php_stream_open_wrapper_ex(data, "rb", REPORT_ERRORS, NULL, FG(default_context));
if (!stream) {
/* Stream will report errors opening file */
RETURN_FALSE;
@@ -373,7 +367,7 @@ PHP_FUNCTION(hash_init)
memset(K, 0, ops->block_size);
- if (key_len > ops->block_size) {
+ if (key_len > (size_t)ops->block_size) {
/* Reduce the key first */
ops->hash_update(context, (unsigned char *) key, key_len);
ops->hash_final((unsigned char *) K, context);
@@ -603,6 +597,122 @@ PHP_FUNCTION(hash_algos)
}
/* }}} */
+static inline zend_bool php_hash_is_crypto(const char *algo, size_t algo_len) {
+
+ char *blacklist[] = { "adler32", "crc32", "crc32b", "fnv132", "fnv1a32", "fnv164", "fnv1a64", "joaat", NULL };
+ char *lower = zend_str_tolower_dup(algo, algo_len);
+ int i = 0;
+
+ while (blacklist[i]) {
+ if (strcmp(lower, blacklist[i]) == 0) {
+ efree(lower);
+ return 0;
+ }
+
+ i++;
+ }
+
+ efree(lower);
+ return 1;
+}
+
+/* {{{ proto string hash_hkdf(string algo, string ikm [, int length = 0, string info = '', string salt = ''])
+RFC5869 HMAC-based key derivation function */
+PHP_FUNCTION(hash_hkdf)
+{
+ zend_string *returnval, *ikm, *algo, *info = NULL, *salt = NULL;
+ zend_long length = 0;
+ unsigned char *prk, *digest, *K;
+ int i, rounds;
+ const php_hash_ops *ops;
+ void *context;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS|lSS", &algo, &ikm, &length, &info, &salt) == FAILURE) {
+ return;
+ }
+
+ ops = php_hash_fetch_ops(ZSTR_VAL(algo), ZSTR_LEN(algo));
+ if (!ops) {
+ php_error_docref(NULL, E_WARNING, "Unknown hashing algorithm: %s", ZSTR_VAL(algo));
+ RETURN_FALSE;
+ }
+
+ if (!php_hash_is_crypto(ZSTR_VAL(algo), ZSTR_LEN(algo))) {
+ php_error_docref(NULL, E_WARNING, "Non-cryptographic hashing algorithm: %s", ZSTR_VAL(algo));
+ RETURN_FALSE;
+ }
+
+ if (ZSTR_LEN(ikm) == 0) {
+ php_error_docref(NULL, E_WARNING, "Input keying material cannot be empty");
+ RETURN_FALSE;
+ }
+
+ if (length < 0) {
+ php_error_docref(NULL, E_WARNING, "Length must be greater than or equal to 0: " ZEND_LONG_FMT, length);
+ RETURN_FALSE;
+ } else if (length == 0) {
+ length = ops->digest_size;
+ } else if (length > ops->digest_size * 255) {
+ php_error_docref(NULL, E_WARNING, "Length must be less than or equal to %d: " ZEND_LONG_FMT, ops->digest_size * 255, length);
+ RETURN_FALSE;
+ }
+
+ context = emalloc(ops->context_size);
+
+ // Extract
+ ops->hash_init(context);
+ K = emalloc(ops->block_size);
+ php_hash_hmac_prep_key(K, ops, context,
+ (unsigned char *) (salt ? ZSTR_VAL(salt) : ""), salt ? ZSTR_LEN(salt) : 0);
+
+ prk = emalloc(ops->digest_size);
+ php_hash_hmac_round(prk, ops, context, K, (unsigned char *) ZSTR_VAL(ikm), ZSTR_LEN(ikm));
+ php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
+ php_hash_hmac_round(prk, ops, context, K, prk, ops->digest_size);
+ ZEND_SECURE_ZERO(K, ops->block_size);
+
+ // Expand
+ returnval = zend_string_alloc(length, 0);
+ digest = emalloc(ops->digest_size);
+ for (i = 1, rounds = (length - 1) / ops->digest_size + 1; i <= rounds; i++) {
+ // chr(i)
+ unsigned char c[1];
+ c[0] = (i & 0xFF);
+
+ php_hash_hmac_prep_key(K, ops, context, prk, ops->digest_size);
+ ops->hash_init(context);
+ ops->hash_update(context, K, ops->block_size);
+
+ if (i > 1) {
+ ops->hash_update(context, digest, ops->digest_size);
+ }
+
+ if (info != NULL && ZSTR_LEN(info) > 0) {
+ ops->hash_update(context, (unsigned char *) ZSTR_VAL(info), ZSTR_LEN(info));
+ }
+
+ ops->hash_update(context, c, 1);
+ ops->hash_final(digest, context);
+ php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
+ php_hash_hmac_round(digest, ops, context, K, digest, ops->digest_size);
+ memcpy(
+ ZSTR_VAL(returnval) + ((i - 1) * ops->digest_size),
+ digest,
+ (i == rounds ? length - ((i - 1) * ops->digest_size) : ops->digest_size)
+ );
+ }
+
+ ZEND_SECURE_ZERO(K, ops->block_size);
+ ZEND_SECURE_ZERO(digest, ops->digest_size);
+ ZEND_SECURE_ZERO(prk, ops->digest_size);
+ efree(K);
+ efree(context);
+ efree(prk);
+ efree(digest);
+ ZSTR_VAL(returnval)[length] = 0;
+ RETURN_STR(returnval);
+}
+
/* {{{ proto string hash_pbkdf2(string algo, string password, string salt, int iterations [, int length = 0, bool raw_output = false])
Generate a PBKDF2 hash of the given password and salt
Returns lowercase hexits by default */
@@ -735,7 +845,8 @@ PHP_FUNCTION(hash_equals)
{
zval *known_zval, *user_zval;
char *known_str, *user_str;
- int result = 0, j;
+ int result = 0;
+ size_t j;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &known_zval, &user_zval) == FAILURE) {
return;
@@ -1013,7 +1124,13 @@ PHP_MINIT_FUNCTION(hash)
php_hash_register_algo("sha224", &php_hash_sha224_ops);
php_hash_register_algo("sha256", &php_hash_sha256_ops);
php_hash_register_algo("sha384", &php_hash_sha384_ops);
+ php_hash_register_algo("sha512/224", &php_hash_sha512_224_ops);
+ php_hash_register_algo("sha512/256", &php_hash_sha512_256_ops);
php_hash_register_algo("sha512", &php_hash_sha512_ops);
+ php_hash_register_algo("sha3-224", &php_hash_sha3_224_ops);
+ php_hash_register_algo("sha3-256", &php_hash_sha3_256_ops);
+ php_hash_register_algo("sha3-384", &php_hash_sha3_384_ops);
+ php_hash_register_algo("sha3-512", &php_hash_sha3_512_ops);
php_hash_register_algo("ripemd128", &php_hash_ripemd128_ops);
php_hash_register_algo("ripemd160", &php_hash_ripemd160_ops);
php_hash_register_algo("ripemd256", &php_hash_ripemd256_ops);
@@ -1204,6 +1321,14 @@ ZEND_BEGIN_ARG_INFO(arginfo_hash_equals, 0)
ZEND_ARG_INFO(0, user_string)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_hash_hkdf, 0, 0, 2)
+ ZEND_ARG_INFO(0, ikm)
+ ZEND_ARG_INFO(0, algo)
+ ZEND_ARG_INFO(0, length)
+ ZEND_ARG_INFO(0, string)
+ ZEND_ARG_INFO(0, salt)
+ZEND_END_ARG_INFO()
+
/* BC Land */
#ifdef PHP_MHASH_BC
ZEND_BEGIN_ARG_INFO(arginfo_mhash_get_block_size, 0)
@@ -1252,6 +1377,7 @@ const zend_function_entry hash_functions[] = {
PHP_FE(hash_algos, arginfo_hash_algos)
PHP_FE(hash_pbkdf2, arginfo_hash_pbkdf2)
PHP_FE(hash_equals, arginfo_hash_equals)
+ PHP_FE(hash_hkdf, arginfo_hash_hkdf)
/* BC Land */
#ifdef PHP_HASH_MD5_NOT_IN_CORE