Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions ext/hash/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,35 @@ static inline void php_hash_hmac_round(unsigned char *final, const php_hash_ops
ops->hash_final(final, context);
}

PHP_HASH_API void php_hash_hmac(
const char *algo, const size_t algo_len,
const unsigned char *data, const size_t data_len,
const unsigned char *key, const size_t key_len,
unsigned char *output, const size_t output_len
) {
const php_hash_ops *ops = zend_hash_str_find_ptr(&php_hash_hashtable, algo, algo_len);
ZEND_ASSERT(ops && ops->is_crypto);
void *ctx = php_hash_alloc_context(ops);

ALLOCA_FLAG(use_heap_key);
unsigned char *K = do_alloca(ops->block_size, use_heap_key);
ALLOCA_FLAG(use_heap_digest);
unsigned char *digest = do_alloca(ops->digest_size, use_heap_digest);

php_hash_hmac_prep_key(K, ops, ctx, key, key_len);
php_hash_hmac_round(digest, ops, ctx, K, data, data_len);
php_hash_string_xor_char(K, K, 0x6A, ops->block_size);
php_hash_hmac_round(digest, ops, ctx, K, digest, ops->digest_size);
php_hash_free_context(ops, ctx);

memcpy(output, digest, output_len < ops->digest_size ? output_len : ops->digest_size);

ZEND_SECURE_ZERO(K, ops->block_size);
free_alloca(K, use_heap_key);
ZEND_SECURE_ZERO(digest, ops->digest_size);
free_alloca(digest, use_heap_digest);
}

static void php_hash_do_hash_hmac(
zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, bool raw_output, bool isfilename
) /* {{{ */ {
Expand Down
77 changes: 77 additions & 0 deletions ext/hash/hash_sha3.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,3 +351,80 @@ DECLARE_SHA3_OPS(384);
DECLARE_SHA3_OPS(512);

#undef DECLARE_SHA3_OPS

static size_t php_hash_sha3_left_encode(uint8_t *out, uint64_t val)
{
uint8_t bytes[8];
size_t num_bytes = 0;

if (val == 0) {
bytes[0] = 0;
num_bytes = 1;
} else {
while (val > 0) {
bytes[num_bytes++] = (uint8_t)(val & 0xFF);
val >>= 8;
}
}

out[0] = (uint8_t)num_bytes;
for (size_t i = 0; i < num_bytes; i++) {
out[1 + i] = bytes[num_bytes - 1 - i];
}
return 1 + num_bytes;
}

PHP_HASH_API void php_hash_cshake256(
const unsigned char *data, size_t data_len,
const unsigned char *custom, size_t custom_len,
unsigned char *output, size_t out_len
) {
PHP_SHA3_512_CTX ctx;
uint8_t header_buf[32];
size_t pos = 0;
const size_t rate = 136;

/* cSHAKE256 is basically SHA3-512 with a few adjusted params:
* rate = 136 bytes, capacity = 512 bits, 0 output size, 0x04 suffix
* The slow path has to account for that differently, but it works. */
#ifdef HAVE_SLOW_HASH3
PHP_SHA3_Init(&ctx, 512);
# define CSHAKE_UPDATE(ctx, buf, len) PHP_SHA3_Update((ctx), (buf), (len), rate)
#else
Keccak_HashInstance *hash = (Keccak_HashInstance *)&ctx;
Keccak_HashInitialize(hash, rate * 8, 512, 0, 0x04);
# define CSHAKE_UPDATE(ctx, buf, len) PHP_SHA3512Update((ctx), (buf), (len))
#endif

/* NIST SP800-185 header metadata */
pos += php_hash_sha3_left_encode(&header_buf[pos], rate);
pos += php_hash_sha3_left_encode(&header_buf[pos], 0); // reserved
pos += php_hash_sha3_left_encode(&header_buf[pos], (uint64_t)custom_len * 8);

CSHAKE_UPDATE(&ctx, header_buf, pos);
if (custom_len > 0) {
CSHAKE_UPDATE(&ctx, custom, custom_len);
}

size_t remainder = (pos + custom_len) % rate;
if (remainder > 0) {
size_t pad_len = rate - remainder;
uint8_t zero_pad[rate];
memset(zero_pad, 0, sizeof(zero_pad));
CSHAKE_UPDATE(&ctx, zero_pad, pad_len);
}

if (data_len > 0) {
CSHAKE_UPDATE(&ctx, data, data_len);
}

#ifdef HAVE_SLOW_HASH3
/* Hack for 0x04 when PHP_SHA3_Final does ctx->state[ctx=>post++] ^= 0x06 */
ctx.state[ctx.pos] ^= 0x02;
PHP_SHA3_Final(output, &ctx, rate, out_len);
#else
Keccak_HashFinal(hash, NULL);
Keccak_HashSqueeze(hash, output, out_len * 8);
#endif
#undef CSHAKE_UPDATE
}
6 changes: 6 additions & 0 deletions ext/hash/php_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ PHP_HASH_API hash_spec_result php_hash_serialize(const php_hashcontext_object *c
PHP_HASH_API hash_spec_result php_hash_unserialize(php_hashcontext_object *context, zend_long magic, const zval *zv);
PHP_HASH_API hash_spec_result php_hash_serialize_spec(const php_hashcontext_object *context, zval *zv, const char *spec);
PHP_HASH_API hash_spec_result php_hash_unserialize_spec(php_hashcontext_object *hash, const zval *zv, const char *spec);
PHP_HASH_API void php_hash_hmac(
const char *algo, const size_t algo_len,
const unsigned char *data, const size_t data_len,
const unsigned char *key, const size_t key_len,
unsigned char *output, const size_t output_len
);

static inline void *php_hash_alloc_context(const php_hash_ops *ops) {
/* Zero out context memory so serialization doesn't expose internals */
Expand Down
8 changes: 8 additions & 0 deletions ext/hash/php_hash_sha3.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,12 @@ PHP_HASH_API void PHP_SHA3512Init(PHP_SHA3_512_CTX*, ZEND_ATTRIBUTE_UNUSED HashT
PHP_HASH_API void PHP_SHA3512Update(PHP_SHA3_512_CTX*, const unsigned char*, size_t);
PHP_HASH_API void PHP_SAH3512Final(unsigned char[32], PHP_SHA3_512_CTX*);

/* TODO: Decide whether this is appropriate; kept minimal here to avoid scope creep,
* but userspace-exposed cSHAKE makes sense and likely looks different. */
PHP_HASH_API void php_hash_cshake256(
const unsigned char *data, size_t data_len,
const unsigned char *custom, size_t custom_len,
unsigned char *output, size_t out_len
);

#endif
2 changes: 1 addition & 1 deletion ext/standard/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,6 @@ PHP_NEW_EXTENSION([standard], m4_normalize([
[no])

PHP_ADD_BUILD_DIR([$ext_builddir/libavifinfo])

PHP_ADD_EXTENSION_DEP([standard], [hash])
PHP_ADD_MAKEFILE_FRAGMENT
PHP_INSTALL_HEADERS([ext/standard/])
Loading
Loading