Skip to content

fuzz-tests: Add differential test for HMAC-SHA256 #8185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
1 change: 1 addition & 0 deletions tests/fuzz/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ LIBFUZZ_OBJS := $(LIBFUZZ_SRC:.c=.o)
tests/fuzz/fuzz-connectd-handshake-act*.o: tests/fuzz/connectd_handshake.h
tests/fuzz/fuzz-ripemd160: LDLIBS += -lcrypto
tests/fuzz/fuzz-sha256: LDLIBS += -lcrypto
tests/fuzz/fuzz-hmac-sha256: LDLIBS += -lcrypto
tests/fuzz/fuzz-wire-*.o: tests/fuzz/wire.h
tests/fuzz/fuzz-bolt12-*.o: tests/fuzz/bolt12.h

Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
~�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
���������������������������������������������������������������������������������ijjj�������������������""""""""""""""""""""""1""""���������������������������%���`����������������������ijjj�������������������""""""""""""""""""""""1""""���������������������������%���`������������
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
93 changes: 93 additions & 0 deletions tests/fuzz/fuzz-hmac-sha256.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/* This is a differential fuzz test comparing CCAN's HMAC‑SHA256 implementation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally, and it looks like there's execute permissions on this file. This isn't an executable file, so please remove that.

$ ls -l fuzz-hmac-sha256.c
-rwxr-xr-x. 1 matt matt 2600 May 19 09:46 fuzz-hmac-sha256.c

* against OpenSSL's HMAC.
*/
#include "config.h"
#include <assert.h>
#include <ccan/crypto/hmac_sha256/hmac_sha256.h>
#include <ccan/mem/mem.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <tests/fuzz/libfuzz.h>

static unsigned char *hmac_key;
static size_t hmac_key_len;

static EVP_MAC *hmac_sha256_algo;

void init(int *argc, char ***argv)
{
hmac_sha256_algo = EVP_MAC_fetch(NULL, "HMAC", NULL);
assert(hmac_sha256_algo);
}

/* Test that splitting the data and updating via multiple calls yields the same
* result as processing the data in a single pass.
*/
static void test_split_update(int num_splits, const struct hmac_sha256 *expected,
const u8 *data, size_t size)
{
const size_t split_size = size / (num_splits + 1);
struct hmac_sha256_ctx ctx;
struct hmac_sha256 actual;

hmac_sha256_init(&ctx, hmac_key, hmac_key_len);
for (int i = 0; i < num_splits; ++i) {
hmac_sha256_update(&ctx, data, split_size);
data += split_size;
size -= split_size;
}
hmac_sha256_update(&ctx, data, size); /* Process remaining data. */
hmac_sha256_done(&ctx, &actual);
assert(memeq(expected, sizeof(*expected), &actual, sizeof(actual)));
}

/* Test that the HMAC calculated by CCAN matches OpenSSL's HMAC. */
static void test_vs_openssl(const struct hmac_sha256 *expected, const u8 *data, size_t size)
{
u8 openssl_hash[SHA256_DIGEST_LENGTH];
size_t hash_size;
EVP_MAC_CTX *ctx;
OSSL_PARAM params[] = {
OSSL_PARAM_construct_utf8_string("digest", "SHA256", 0),
OSSL_PARAM_END
};

ctx = EVP_MAC_CTX_new(hmac_sha256_algo);
assert(ctx);

assert(EVP_MAC_init(ctx, hmac_key, hmac_key_len, params));
assert(EVP_MAC_update(ctx, data, size));
assert(EVP_MAC_final(ctx, openssl_hash, &hash_size, sizeof(openssl_hash)));
EVP_MAC_CTX_free(ctx);

assert(hash_size == SHA256_DIGEST_LENGTH);
assert(memeq(expected, sizeof(*expected), openssl_hash, sizeof(openssl_hash)));
}

void run(const u8 *data, size_t size)
{
struct hmac_sha256 expected;
u8 num_splits;

if (size < 1)
return;
hmac_key_len = (size_t) data[0];
++data; --size;

if (size < hmac_key_len)
return;
hmac_key = (unsigned char*) data;
data += hmac_key_len; size -= hmac_key_len;

if (size < 1)
return;
num_splits = *data;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a size check before reading this byte, so we don't cause a buffer overflow.

Copy link
Author

@Chand-ra Chand-ra Apr 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. If the condition on line 79 fails:

if (size <= hmac_key_len)
    return

it would imply that size > hmac_key_len which is the same thing as size >= hmac_key_len + 1. Therefore, even if we subsequently peel away hmac_key_len bytes, we're guaranteed to be left with at least one byte.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you're right. Still for readability I would prefer the condition to be written the same way as the other size check:

if (size < hmac_key_len + 1)
  return

++data; --size;

/* Compute expected HMAC using the one-shot function. */
hmac_sha256(&expected, hmac_key, hmac_key_len, data, size);
test_split_update(num_splits, &expected, data, size);
test_vs_openssl(&expected, data, size);
}