|
| 1 | +/* This is a differential fuzz test comparing CCAN's HMAC‑SHA256 implementation |
| 2 | + * against OpenSSL's HMAC. |
| 3 | + */ |
| 4 | +#include "config.h" |
| 5 | +#include <assert.h> |
| 6 | +#include <ccan/crypto/hmac_sha256/hmac_sha256.h> |
| 7 | +#include <ccan/mem/mem.h> |
| 8 | +#include <openssl/hmac.h> |
| 9 | +#include <openssl/evp.h> |
| 10 | +#include <openssl/sha.h> |
| 11 | +#include <tests/fuzz/libfuzz.h> |
| 12 | + |
| 13 | +/* Fixed key for testing purposes */ |
| 14 | +static const unsigned char hmac_key[] = "fuzz"; |
| 15 | +static const size_t hmac_key_len = sizeof(hmac_key) - 1; |
| 16 | + |
| 17 | +static EVP_MAC *hmac_sha256_algo; |
| 18 | + |
| 19 | +void init(int *argc, char ***argv) |
| 20 | +{ |
| 21 | + hmac_sha256_algo = EVP_MAC_fetch(NULL, "HMAC", NULL); |
| 22 | + assert(hmac_sha256_algo); |
| 23 | +} |
| 24 | + |
| 25 | +/* Test that splitting the data and updating via multiple calls yields the same |
| 26 | + * result as processing the data in a single pass. |
| 27 | + */ |
| 28 | +static void test_split_update(int num_splits, const struct hmac_sha256 *expected, |
| 29 | + const u8 *data, size_t size) |
| 30 | +{ |
| 31 | + const size_t split_size = size / (num_splits + 1); |
| 32 | + struct hmac_sha256_ctx ctx; |
| 33 | + struct hmac_sha256 actual; |
| 34 | + |
| 35 | + hmac_sha256_init(&ctx, hmac_key, hmac_key_len); |
| 36 | + for (int i = 0; i < num_splits; ++i) { |
| 37 | + hmac_sha256_update(&ctx, data, split_size); |
| 38 | + data += split_size; |
| 39 | + size -= split_size; |
| 40 | + } |
| 41 | + hmac_sha256_update(&ctx, data, size); /* Process remaining data. */ |
| 42 | + hmac_sha256_done(&ctx, &actual); |
| 43 | + assert(memeq(expected, sizeof(*expected), &actual, sizeof(actual))); |
| 44 | +} |
| 45 | + |
| 46 | +/* Test that the HMAC calculated by CCAN matches OpenSSL's HMAC. */ |
| 47 | +static void test_vs_openssl(const struct hmac_sha256 *expected, const u8 *data, size_t size) |
| 48 | +{ |
| 49 | + u8 openssl_hash[SHA256_DIGEST_LENGTH]; |
| 50 | + size_t hash_size; |
| 51 | + EVP_MAC_CTX *ctx; |
| 52 | + OSSL_PARAM params[] = { |
| 53 | + OSSL_PARAM_construct_utf8_string("digest", "SHA256", 0), |
| 54 | + OSSL_PARAM_END |
| 55 | + }; |
| 56 | + |
| 57 | + ctx = EVP_MAC_CTX_new(hmac_sha256_algo); |
| 58 | + assert(ctx); |
| 59 | + |
| 60 | + assert(EVP_MAC_init(ctx, hmac_key, hmac_key_len, params)); |
| 61 | + assert(EVP_MAC_update(ctx, data, size)); |
| 62 | + assert(EVP_MAC_final(ctx, openssl_hash, &hash_size, sizeof(openssl_hash))); |
| 63 | + EVP_MAC_CTX_free(ctx); |
| 64 | + |
| 65 | + assert(hash_size == SHA256_DIGEST_LENGTH); |
| 66 | + assert(memeq(expected, sizeof(*expected), openssl_hash, sizeof(openssl_hash))); |
| 67 | +} |
| 68 | + |
| 69 | +void run(const u8 *data, size_t size) |
| 70 | +{ |
| 71 | + struct hmac_sha256 expected; |
| 72 | + u8 num_splits; |
| 73 | + |
| 74 | + if (size < 1) |
| 75 | + return; |
| 76 | + |
| 77 | + num_splits = *data; |
| 78 | + ++data; |
| 79 | + --size; |
| 80 | + |
| 81 | + /* Compute expected HMAC using the one-shot function. */ |
| 82 | + hmac_sha256(&expected, hmac_key, hmac_key_len, data, size); |
| 83 | + |
| 84 | + test_split_update(num_splits, &expected, data, size); |
| 85 | + test_vs_openssl(&expected, data, size); |
| 86 | +} |
| 87 | + |
0 commit comments