From: Aydın Mercan Date: Wed, 17 Sep 2025 12:52:35 +0000 (+0200) Subject: Separate isc_hmac between pre and post OpenSSL 3.0 X-Git-Tag: v9.21.18~2^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8f106f2b66a8351e57cfd09be9bd41f1616332e9;p=thirdparty%2Fbind9.git Separate isc_hmac between pre and post OpenSSL 3.0 Instead of the `EVP_MD_CTX` based functions, use either the new `EVP_MAC` or the old `HMAC_CTX` based functions. `EVP_MAC` is the recommended way using using MAC functions in post-3.0 while `HMAC_CTX` is used internally by `EVP_MD_CTX`, making the latter redundant. --- diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h index 51f8cd5d26c..bc48c9fec41 100644 --- a/lib/dns/dst_internal.h +++ b/lib/dns/dst_internal.h @@ -62,8 +62,6 @@ typedef struct dst_func dst_func_t; -typedef struct dst_hmac_key dst_hmac_key_t; - /*% * Indicate whether a DST context will be used for signing * or for verification @@ -93,7 +91,7 @@ struct dst_key { union { void *generic; dns_gss_ctx_id_t gssctx; - dst_hmac_key_t *hmac_key; + isc_hmac_key_t *hmac_key; struct { EVP_PKEY *pub; EVP_PKEY *priv; diff --git a/lib/dns/hmac_link.c b/lib/dns/hmac_link.c index 40a87aac70c..aa3d5d7141d 100644 --- a/lib/dns/hmac_link.c +++ b/lib/dns/hmac_link.c @@ -55,7 +55,7 @@ #define hmac_register_algorithm(alg) \ static isc_result_t hmac##alg##_createctx(dst_key_t *key, \ dst_context_t *dctx) { \ - return (hmac_createctx(ISC_MD_##alg, key, dctx)); \ + return (hmac_createctx(key, dctx)); \ } \ static void hmac##alg##_destroyctx(dst_context_t *dctx) { \ hmac_destroyctx(dctx); \ @@ -74,7 +74,7 @@ } \ static bool hmac##alg##_compare(const dst_key_t *key1, \ const dst_key_t *key2) { \ - return (hmac_compare(ISC_MD_##alg, key1, key2)); \ + return (hmac_compare(key1, key2)); \ } \ static isc_result_t hmac##alg##_generate( \ dst_key_t *key, int pseudorandom_ok, void (*callback)(int)) { \ @@ -130,23 +130,20 @@ void dst__hmac##alg##_init(dst_func_t **funcp) { \ REQUIRE(funcp != NULL); \ if (*funcp == NULL) { \ - isc_hmac_t *ctx = isc_hmac_new(); \ - if (isc_hmac_init(ctx, "test", 4, ISC_MD_##alg) == \ - ISC_R_SUCCESS) \ + uint8_t data[] = "data"; \ + uint8_t mac_buffer[ISC_MAX_MD_SIZE]; \ + unsigned int mac_len = sizeof(mac_buffer); \ + if (isc_hmac(ISC_MD_##alg, "test", 4, data, 4, \ + mac_buffer, &mac_len) == ISC_R_SUCCESS) \ { \ *funcp = &hmac##alg##_functions; \ } \ - isc_hmac_free(ctx); \ } \ } static isc_result_t hmac_fromdns(isc_md_type_t type, dst_key_t *key, isc_buffer_t *data); -struct dst_hmac_key { - uint8_t key[ISC_MAX_BLOCK_SIZE]; -}; - static isc_result_t getkeybits(dst_key_t *key, struct dst_private_element *element) { uint16_t *bits = (uint16_t *)element->data; @@ -161,13 +158,11 @@ getkeybits(dst_key_t *key, struct dst_private_element *element) { } static isc_result_t -hmac_createctx(isc_md_type_t type, const dst_key_t *key, dst_context_t *dctx) { +hmac_createctx(const dst_key_t *key, dst_context_t *dctx) { isc_result_t result; - const dst_hmac_key_t *hkey = key->keydata.hmac_key; isc_hmac_t *ctx = isc_hmac_new(); /* Either returns or abort()s */ - result = isc_hmac_init(ctx, hkey->key, isc_md_type_get_block_size(type), - type); + result = isc_hmac_init(ctx, key->keydata.hmac_key); if (result != ISC_R_SUCCESS) { isc_hmac_free(ctx); return DST_R_UNSUPPORTEDALG; @@ -204,44 +199,35 @@ hmac_adddata(const dst_context_t *dctx, const isc_region_t *data) { static isc_result_t hmac_sign(const dst_context_t *dctx, isc_buffer_t *sig) { isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx; - REQUIRE(ctx != NULL); - unsigned char digest[ISC_MAX_MD_SIZE]; - unsigned int digestlen = sizeof(digest); + isc_result_t r; - if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) { - return DST_R_OPENSSLFAILURE; - } + REQUIRE(ctx != NULL); - if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) { - return DST_R_OPENSSLFAILURE; - } + r = isc_hmac_final(ctx, sig); - if (isc_buffer_availablelength(sig) < digestlen) { - return ISC_R_NOSPACE; + /* Turn CRYPTOFAILURE into OPENSSLFAILURE */ + if (r == ISC_R_CRYPTOFAILURE) { + r = DST_R_OPENSSLFAILURE; } - isc_buffer_putmem(sig, digest, digestlen); - - return ISC_R_SUCCESS; + return r; } static isc_result_t hmac_verify(const dst_context_t *dctx, const isc_region_t *sig) { isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx; unsigned char digest[ISC_MAX_MD_SIZE]; - unsigned int digestlen = sizeof(digest); + isc_buffer_t hmac; REQUIRE(ctx != NULL); - if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) { - return DST_R_OPENSSLFAILURE; - } + isc_buffer_init(&hmac, digest, sizeof(digest)); - if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) { + if (isc_hmac_final(ctx, &hmac) != ISC_R_SUCCESS) { return DST_R_OPENSSLFAILURE; } - if (sig->length > digestlen) { + if (sig->length > isc_buffer_usedlength(&hmac)) { return DST_R_VERIFYFAILURE; } @@ -251,8 +237,8 @@ hmac_verify(const dst_context_t *dctx, const isc_region_t *sig) { } static bool -hmac_compare(isc_md_type_t type, const dst_key_t *key1, const dst_key_t *key2) { - dst_hmac_key_t *hkey1, *hkey2; +hmac_compare(const dst_key_t *key1, const dst_key_t *key2) { + isc_hmac_key_t *hkey1, *hkey2; hkey1 = key1->keydata.hmac_key; hkey2 = key2->keydata.hmac_key; @@ -263,8 +249,7 @@ hmac_compare(isc_md_type_t type, const dst_key_t *key1, const dst_key_t *key2) { return false; } - return isc_safe_memequal(hkey1->key, hkey2->key, - isc_md_type_get_block_size(type)); + return isc_hmac_key_equal(hkey1, hkey2); } static isc_result_t @@ -303,31 +288,28 @@ hmac_isprivate(const dst_key_t *key) { static void hmac_destroy(dst_key_t *key) { - dst_hmac_key_t *hkey = key->keydata.hmac_key; - isc_safe_memwipe(hkey, sizeof(*hkey)); - isc_mem_put(key->mctx, hkey, sizeof(*hkey)); - key->keydata.hmac_key = NULL; + isc_hmac_key_destroy(&key->keydata.hmac_key); } static isc_result_t hmac_todns(const dst_key_t *key, isc_buffer_t *data) { + isc_region_t raw_key; + REQUIRE(key != NULL && key->keydata.hmac_key != NULL); - dst_hmac_key_t *hkey = key->keydata.hmac_key; - unsigned int bytes; - bytes = (key->key_size + 7) / 8; - if (isc_buffer_availablelength(data) < bytes) { + raw_key = isc_hmac_key_expose(key->keydata.hmac_key); + + if (isc_buffer_availablelength(data) < raw_key.length) { return ISC_R_NOSPACE; } - isc_buffer_putmem(data, hkey->key, bytes); - return ISC_R_SUCCESS; + return isc_buffer_copyregion(data, &raw_key); } static isc_result_t hmac_fromdns(isc_md_type_t type, dst_key_t *key, isc_buffer_t *data) { - dst_hmac_key_t *hkey; - unsigned int keylen; + isc_hmac_key_t *hkey = NULL; + isc_result_t result; isc_region_t r; isc_buffer_remainingregion(data, &r); @@ -335,24 +317,12 @@ hmac_fromdns(isc_md_type_t type, dst_key_t *key, isc_buffer_t *data) { return ISC_R_SUCCESS; } - hkey = isc_mem_get(key->mctx, sizeof(dst_hmac_key_t)); - - memset(hkey->key, 0, sizeof(hkey->key)); - - /* Hash the key if the key is longer then chosen MD block size */ - if (r.length > (unsigned int)isc_md_type_get_block_size(type)) { - if (isc_md(type, r.base, r.length, hkey->key, &keylen) != - ISC_R_SUCCESS) - { - isc_mem_put(key->mctx, hkey, sizeof(dst_hmac_key_t)); - return DST_R_OPENSSLFAILURE; - } - } else { - memmove(hkey->key, r.base, r.length); - keylen = r.length; + result = isc_hmac_key_create(type, r.base, r.length, key->mctx, &hkey); + if (result != ISC_R_SUCCESS) { + return DST_R_OPENSSLFAILURE; } - key->key_size = keylen * 8; + key->key_size = isc_hmac_key_expose(hkey).length * 8; key->keydata.hmac_key = hkey; isc_buffer_forward(data, r.length); @@ -362,47 +332,48 @@ hmac_fromdns(isc_md_type_t type, dst_key_t *key, isc_buffer_t *data) { static int hmac__get_tag_key(isc_md_type_t type) { - if (type == ISC_MD_MD5) { + switch (type) { + case ISC_MD_MD5: return TAG_HMACMD5_KEY; - } else if (type == ISC_MD_SHA1) { + case ISC_MD_SHA1: return TAG_HMACSHA1_KEY; - } else if (type == ISC_MD_SHA224) { + case ISC_MD_SHA224: return TAG_HMACSHA224_KEY; - } else if (type == ISC_MD_SHA256) { + case ISC_MD_SHA256: return TAG_HMACSHA256_KEY; - } else if (type == ISC_MD_SHA384) { + case ISC_MD_SHA384: return TAG_HMACSHA384_KEY; - } else if (type == ISC_MD_SHA512) { + case ISC_MD_SHA512: return TAG_HMACSHA512_KEY; - } else { + default: UNREACHABLE(); } } static int hmac__get_tag_bits(isc_md_type_t type) { - if (type == ISC_MD_MD5) { + switch (type) { + case ISC_MD_MD5: return TAG_HMACMD5_BITS; - } else if (type == ISC_MD_SHA1) { + case ISC_MD_SHA1: return TAG_HMACSHA1_BITS; - } else if (type == ISC_MD_SHA224) { + case ISC_MD_SHA224: return TAG_HMACSHA224_BITS; - } else if (type == ISC_MD_SHA256) { + case ISC_MD_SHA256: return TAG_HMACSHA256_BITS; - } else if (type == ISC_MD_SHA384) { + case ISC_MD_SHA384: return TAG_HMACSHA384_BITS; - } else if (type == ISC_MD_SHA512) { + case ISC_MD_SHA512: return TAG_HMACSHA512_BITS; - } else { + default: UNREACHABLE(); } } static isc_result_t hmac_tofile(isc_md_type_t type, const dst_key_t *key, const char *directory) { - dst_hmac_key_t *hkey; + isc_region_t raw_key; dst_private_t priv; - int bytes = (key->key_size + 7) / 8; uint16_t bits; if (key->keydata.hmac_key == NULL) { @@ -413,11 +384,11 @@ hmac_tofile(isc_md_type_t type, const dst_key_t *key, const char *directory) { return DST_R_EXTERNALKEY; } - hkey = key->keydata.hmac_key; + raw_key = isc_hmac_key_expose(key->keydata.hmac_key); priv.elements[0].tag = hmac__get_tag_key(type); - priv.elements[0].length = bytes; - priv.elements[0].data = hkey->key; + priv.elements[0].length = raw_key.length; + priv.elements[0].data = raw_key.base; bits = htons(key->key_bits); diff --git a/lib/isc/crypto/ossl1_1.c b/lib/isc/crypto/ossl1_1.c index 6920273b67c..e47e294ddda 100644 --- a/lib/isc/crypto/ossl1_1.c +++ b/lib/isc/crypto/ossl1_1.c @@ -11,21 +11,37 @@ * information regarding copyright ownership. */ +#include + #include #include #include #include #include +#include #include +#include #include +#include #include #include +#include #include #include #include "crypto_p.h" +#define HMAC_KEY_MAGIC ISC_MAGIC('H', 'M', 'A', 'C') + +struct isc_hmac_key { + uint32_t magic; + uint32_t len; + isc_mem_t *mctx; + EVP_MD *md; + uint8_t secret[]; +}; + static isc_mem_t *isc__crypto_mctx = NULL; #define md_register_algorithm(alg, upperalg) \ @@ -53,6 +69,175 @@ register_algorithms(void) { #undef md_unregister_algorithm +/* + * HMAC Notes + * + * For pre-3.0 libcrypto, we use HMAC_CTX instead of the EVP_PKEY API. + * + * EVP_PKEY will call HMAC_* functions internally so there is no need to add + * even more vtables. + */ + +isc_result_t +isc_hmac(isc_md_type_t type, const void *key, const size_t keylen, + const unsigned char *buf, const size_t len, unsigned char *digest, + unsigned int *digestlen) { + EVP_MD *md; + + REQUIRE(type < ISC_MD_MAX); + + md = isc__crypto_md[type]; + if (md == NULL) { + return ISC_R_NOTIMPLEMENTED; + } + + if (HMAC(md, key, keylen, buf, len, digest, digestlen) == NULL) { + ERR_clear_error(); + return ISC_R_CRYPTOFAILURE; + } + + return ISC_R_SUCCESS; +} + +isc_result_t +isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len, + isc_mem_t *mctx, isc_hmac_key_t **keyp) { + isc_hmac_key_t *key; + EVP_MD *md; + + REQUIRE(keyp != NULL && *keyp == NULL); + REQUIRE(type < ISC_MD_MAX); + + md = isc__crypto_md[type]; + if (md == NULL) { + return ISC_R_NOTIMPLEMENTED; + } + + key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, len)); + *key = (isc_hmac_key_t){ + .magic = HMAC_KEY_MAGIC, + .len = len, + .md = md, + }; + memmove(key->secret, secret, len); + isc_mem_attach(mctx, &key->mctx); + + *keyp = key; + + return ISC_R_SUCCESS; +} + +void +isc_hmac_key_destroy(isc_hmac_key_t **keyp) { + isc_hmac_key_t *key; + + REQUIRE(keyp != NULL && *keyp != NULL); + REQUIRE((*keyp)->magic == HMAC_KEY_MAGIC); + + key = *keyp; + *keyp = NULL; + + key->magic = 0x00; + + isc_safe_memwipe(key->secret, sizeof(key->len)); + + isc_mem_putanddetach(&key->mctx, key, + STRUCT_FLEX_SIZE(key, secret, key->len)); +} + +isc_region_t +isc_hmac_key_expose(isc_hmac_key_t *key) { + REQUIRE(key != NULL && key->magic == HMAC_KEY_MAGIC); + + return (isc_region_t){ .base = key->secret, .length = key->len }; +} + +bool +isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) { + REQUIRE(a != NULL && a->magic == HMAC_KEY_MAGIC); + REQUIRE(b != NULL && b->magic == HMAC_KEY_MAGIC); + + if (a->md != b->md) { + return false; + } + + if (a->len != b->len) { + return false; + } + + return isc_safe_memequal(a->secret, b->secret, a->len); +} + +isc_hmac_t * +isc_hmac_new(void) { + HMAC_CTX *ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx != NULL); + return ctx; +} + +void +isc_hmac_free(isc_hmac_t *hmac) { + if (hmac != NULL) { + HMAC_CTX_free(hmac); + } +} + +isc_result_t +isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) { + REQUIRE(hmac != NULL); + REQUIRE(key != NULL && key->magic == HMAC_KEY_MAGIC); + + if (HMAC_Init_ex(hmac, key->secret, key->len, key->md, NULL) != 1) { + ERR_clear_error(); + return ISC_R_CRYPTOFAILURE; + } + + return ISC_R_SUCCESS; +} + +isc_result_t +isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) { + REQUIRE(hmac != NULL); + + if (buf == NULL || len == 0) { + return ISC_R_SUCCESS; + } + + if (HMAC_Update(hmac, buf, len) != 1) { + ERR_clear_error(); + return ISC_R_CRYPTOFAILURE; + } + + return ISC_R_SUCCESS; +} + +isc_result_t +isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) { + unsigned int len; + + REQUIRE(hmac != NULL); + REQUIRE(out != NULL); + + /* + * LibreSSL changes HMAC_size's return from size_t to int but keeps the + * size_t signature in its manpage. + * + * Cast it instead of accepting LibreSSL's man(page)splaining. + */ + len = isc_buffer_availablelength(out); + if (len < (unsigned int)HMAC_size(hmac)) { + return ISC_R_NOSPACE; + } + + if (HMAC_Final(hmac, isc_buffer_used(out), &len) != 1) { + return ISC_R_CRYPTOFAILURE; + } + + isc_buffer_add(out, len); + + return ISC_R_SUCCESS; +} + #ifndef LIBRESSL_VERSION_NUMBER /* * This was crippled with LibreSSL, so just skip it: diff --git a/lib/isc/crypto/ossl3.c b/lib/isc/crypto/ossl3.c index 00ac7c82571..9b6e19bb27d 100644 --- a/lib/isc/crypto/ossl3.c +++ b/lib/isc/crypto/ossl3.c @@ -11,6 +11,10 @@ * information regarding copyright ownership. */ +#include +#include + +#include #include #include #include @@ -18,19 +22,64 @@ #include #include +#include #include +#include #include +#include #include #include +#include +#include #include #include #include "crypto_p.h" +struct isc_hmac_key { + uint32_t magic; + uint32_t len; + isc_mem_t *mctx; + const OSSL_PARAM *params; + uint8_t secret[]; +}; + +constexpr uint32_t hmac_key_magic = ISC_MAGIC('H', 'M', 'A', 'C'); + static isc_mem_t *isc__crypto_mctx = NULL; static OSSL_PROVIDER *base = NULL, *fips = NULL; +static EVP_MAC *evp_hmac = NULL; + +static OSSL_PARAM md_to_hmac_params[ISC_MD_MAX][2] = { + [ISC_MD_UNKNOWN] = { OSSL_PARAM_END }, + [ISC_MD_MD5] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("MD5"), sizeof("MD5") - 1), + OSSL_PARAM_END, + }, + [ISC_MD_SHA1] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA1"), sizeof("SHA1") - 1), + OSSL_PARAM_END, + }, + [ISC_MD_SHA224] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-224"), sizeof("SHA2-224") - 1), + OSSL_PARAM_END, + }, + [ISC_MD_SHA256] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-256"), sizeof("SHA2-256") - 1), + OSSL_PARAM_END, + }, + [ISC_MD_SHA384] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-384"), sizeof("SHA2-384") - 1), + OSSL_PARAM_END, + }, + [ISC_MD_SHA512] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-512"), sizeof("SHA2-512") - 1), + OSSL_PARAM_END, + }, +}; + #define md_register_algorithm(alg) \ { \ REQUIRE(isc__crypto_md[ISC_MD_##alg] == NULL); \ @@ -52,6 +101,15 @@ register_algorithms(void) { md_register_algorithm(SHA384); md_register_algorithm(SHA512); + /* We _must_ have HMAC */ + evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (evp_hmac == NULL) { + ERR_clear_error(); + FATAL_ERROR("OpenSSL failed to find an HMAC implementation. " + "Please make sure the default provider has an " + "EVP_MAC-HMAC implementation"); + } + return ISC_R_SUCCESS; } @@ -63,10 +121,207 @@ unregister_algorithms(void) { isc__crypto_md[i] = NULL; } } + + INSIST(evp_hmac != NULL); + EVP_MAC_free(evp_hmac); + evp_hmac = NULL; } #undef md_register_algorithm +/* + * HMAC + */ + +/* + * Do not call EVP_Q_mac or HMAC (since it calls EVP_Q_mac internally) + * + * Each invocation of the EVP_Q_mac function causes an explicit fetch. + */ +isc_result_t +isc_hmac(isc_md_type_t type, const void *key, const size_t keylen, + const unsigned char *buf, const size_t len, unsigned char *digest, + unsigned int *digestlen) { + EVP_MAC_CTX *ctx; + size_t maclen; + + REQUIRE(type < ISC_MD_MAX); + + if (isc__crypto_md[type] == NULL) { + return ISC_R_NOTIMPLEMENTED; + } + + ctx = EVP_MAC_CTX_new(evp_hmac); + RUNTIME_CHECK(ctx != NULL); + + if (EVP_MAC_init(ctx, key, keylen, md_to_hmac_params[type]) != 1) { + goto fail; + } + + if (EVP_MAC_update(ctx, buf, len) != 1) { + goto fail; + } + + maclen = *digestlen; + if (EVP_MAC_final(ctx, digest, &maclen, maclen) != 1) { + goto fail; + } + + *digestlen = maclen; + + EVP_MAC_CTX_free(ctx); + return ISC_R_SUCCESS; + +fail: + ERR_clear_error(); + EVP_MAC_CTX_free(ctx); + return ISC_R_CRYPTOFAILURE; +} + +/* + * You do not need to process the key to fit the block size. + * + * https://github.com/openssl/openssl/blob/925e4fba1098036e8f8d22652cff6f64c5c7d571/crypto/hmac/hmac.c#L61-L80 + */ +isc_result_t +isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len, + isc_mem_t *mctx, isc_hmac_key_t **keyp) { + isc_hmac_key_t *key; + uint8_t digest[ISC_MAX_MD_SIZE]; + unsigned int digest_len = sizeof(digest); + size_t key_len; + + REQUIRE(keyp != NULL && *keyp == NULL); + REQUIRE(type < ISC_MD_MAX); + + if (isc__crypto_md[type] == NULL) { + return ISC_R_NOTIMPLEMENTED; + } + + if (len > (size_t)EVP_MD_block_size(isc__crypto_md[type])) { + RETERR(isc_md(type, secret, len, digest, &digest_len)); + secret = digest; + key_len = digest_len; + } else { + key_len = len; + } + + key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, key_len)); + *key = (isc_hmac_key_t){ + .magic = hmac_key_magic, + .len = key_len, + .params = md_to_hmac_params[type], + }; + memmove(key->secret, secret, key_len); + isc_mem_attach(mctx, &key->mctx); + + *keyp = key; + + return ISC_R_SUCCESS; +} + +void +isc_hmac_key_destroy(isc_hmac_key_t **keyp) { + isc_hmac_key_t *key; + + REQUIRE(keyp != NULL && *keyp != NULL); + REQUIRE((*keyp)->magic == hmac_key_magic); + + key = *keyp; + *keyp = NULL; + + key->magic = 0x00; + + isc_safe_memwipe(key->secret, key->len); + isc_mem_putanddetach(&key->mctx, key, + STRUCT_FLEX_SIZE(key, secret, key->len)); +} + +isc_region_t +isc_hmac_key_expose(isc_hmac_key_t *key) { + REQUIRE(key != NULL && key->magic == hmac_key_magic); + + return (isc_region_t){ .base = key->secret, .length = key->len }; +} + +bool +isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) { + REQUIRE(a != NULL && a->magic == hmac_key_magic); + REQUIRE(b != NULL && b->magic == hmac_key_magic); + + if (a->params != b->params) { + return false; + } + + if (a->len != b->len) { + return false; + } + + return isc_safe_memequal(a->secret, b->secret, a->len); +} + +isc_hmac_t * +isc_hmac_new(void) { + EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(evp_hmac); + RUNTIME_CHECK(ctx != NULL); + return ctx; +} + +void +isc_hmac_free(isc_hmac_t *hmac) { + EVP_MAC_CTX_free(hmac); +} + +isc_result_t +isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) { + REQUIRE(key != NULL && key->magic == hmac_key_magic); + REQUIRE(hmac != NULL); + + if (EVP_MAC_init(hmac, key->secret, key->len, key->params) != 1) { + ERR_clear_error(); + return ISC_R_CRYPTOFAILURE; + } + + return ISC_R_SUCCESS; +} + +isc_result_t +isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) { + REQUIRE(hmac != NULL); + + if (buf == NULL || len == 0) { + return ISC_R_SUCCESS; + } + + if (EVP_MAC_update(hmac, buf, len) != 1) { + ERR_clear_error(); + return ISC_R_CRYPTOFAILURE; + } + + return ISC_R_SUCCESS; +} + +isc_result_t +isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) { + size_t len; + + REQUIRE(hmac != NULL); + + len = isc_buffer_availablelength(out); + if (len < EVP_MAC_CTX_get_mac_size(hmac)) { + return ISC_R_NOSPACE; + } + + if (EVP_MAC_final(hmac, isc_buffer_used(out), &len, len) != 1) { + ERR_clear_error(); + return ISC_R_CRYPTOFAILURE; + } + + isc_buffer_add(out, len); + + return ISC_R_SUCCESS; +} + #if ISC_MEM_TRACKLINES /* * We use the internal isc__mem API here, so we can pass the file and line diff --git a/lib/isc/hmac.c b/lib/isc/hmac.c deleted file mode 100644 index 05134cc1853..00000000000 --- a/lib/isc/hmac.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) Internet Systems Consortium, Inc. ("ISC") - * - * SPDX-License-Identifier: MPL-2.0 - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * See the COPYRIGHT file distributed with this work for additional - * information regarding copyright ownership. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "crypto/crypto_p.h" -#include "openssl_shim.h" - -isc_hmac_t * -isc_hmac_new(void) { - EVP_MD_CTX *hmac_st = EVP_MD_CTX_new(); - RUNTIME_CHECK(hmac_st != NULL); - return (isc_hmac_t *)hmac_st; -} - -void -isc_hmac_free(isc_hmac_t *hmac_st) { - if (hmac_st == NULL) { - return; - } - - EVP_MD_CTX_free((EVP_MD_CTX *)hmac_st); -} - -isc_result_t -isc_hmac_init(isc_hmac_t *hmac_st, const void *key, const size_t keylen, - isc_md_type_t type) { - EVP_PKEY *pkey; - EVP_MD *md; - - REQUIRE(hmac_st != NULL); - REQUIRE(key != NULL); - REQUIRE(keylen <= INT_MAX); - REQUIRE(type < ISC_MD_MAX); - - md = isc__crypto_md[type]; - if (md == NULL) { - return ISC_R_NOTIMPLEMENTED; - } - - pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, keylen); - if (pkey == NULL) { - ERR_clear_error(); - return ISC_R_CRYPTOFAILURE; - } - - if (EVP_DigestSignInit(hmac_st, NULL, md, NULL, pkey) != 1) { - EVP_PKEY_free(pkey); - ERR_clear_error(); - return ISC_R_CRYPTOFAILURE; - } - - EVP_PKEY_free(pkey); - - return ISC_R_SUCCESS; -} - -isc_result_t -isc_hmac_reset(isc_hmac_t *hmac_st) { - REQUIRE(hmac_st != NULL); - - if (EVP_MD_CTX_reset(hmac_st) != 1) { - ERR_clear_error(); - return ISC_R_CRYPTOFAILURE; - } - - return ISC_R_SUCCESS; -} - -isc_result_t -isc_hmac_update(isc_hmac_t *hmac_st, const unsigned char *buf, - const size_t len) { - REQUIRE(hmac_st != NULL); - - if (buf == NULL || len == 0) { - return ISC_R_SUCCESS; - } - - if (EVP_DigestSignUpdate(hmac_st, buf, len) != 1) { - ERR_clear_error(); - return ISC_R_CRYPTOFAILURE; - } - - return ISC_R_SUCCESS; -} - -isc_result_t -isc_hmac_final(isc_hmac_t *hmac_st, unsigned char *digest, - unsigned int *digestlen) { - REQUIRE(hmac_st != NULL); - REQUIRE(digest != NULL); - REQUIRE(digestlen != NULL); - - size_t len = *digestlen; - - if (EVP_DigestSignFinal(hmac_st, digest, &len) != 1) { - ERR_clear_error(); - return ISC_R_CRYPTOFAILURE; - } - - *digestlen = (unsigned int)len; - - return ISC_R_SUCCESS; -} - -size_t -isc_hmac_get_size(isc_hmac_t *hmac_st) { - REQUIRE(hmac_st != NULL); - - return (size_t)EVP_MD_CTX_size(hmac_st); -} - -int -isc_hmac_get_block_size(isc_hmac_t *hmac_st) { - REQUIRE(hmac_st != NULL); - - return EVP_MD_CTX_block_size(hmac_st); -} - -isc_result_t -isc_hmac(isc_md_type_t type, const void *key, const size_t keylen, - const unsigned char *buf, const size_t len, unsigned char *digest, - unsigned int *digestlen) { - isc_result_t res; - isc_hmac_t *hmac_st = isc_hmac_new(); - - res = isc_hmac_init(hmac_st, key, keylen, type); - if (res != ISC_R_SUCCESS) { - goto end; - } - - res = isc_hmac_update(hmac_st, buf, len); - if (res != ISC_R_SUCCESS) { - goto end; - } - - res = isc_hmac_final(hmac_st, digest, digestlen); - if (res != ISC_R_SUCCESS) { - goto end; - } -end: - isc_hmac_free(hmac_st); - - return res; -} diff --git a/lib/isc/include/isc/hmac.h b/lib/isc/include/isc/hmac.h index ecfc6e279c2..06f0893f985 100644 --- a/lib/isc/include/isc/hmac.h +++ b/lib/isc/include/isc/hmac.h @@ -18,12 +18,16 @@ #pragma once +#include + #include #include #include typedef void isc_hmac_t; +typedef struct isc_hmac_key isc_hmac_key_t; + /** * isc_hmac: * @type: the digest type @@ -47,6 +51,46 @@ isc_hmac(isc_md_type_t type, const void *key, const size_t keylen, const unsigned char *buf, const size_t len, unsigned char *digest, unsigned int *digestlen); +/* + * isc_hmac_key_create: + * @type: the digest type + * @secret: the secret key + * @len: length of the secret key + * @mctx: memory context + * @keyp: pointer to the key pointer + * + * This initializes the an HMAC key bound to a specific digest. + */ +isc_result_t +isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len, + isc_mem_t *mctx, isc_hmac_key_t **keyp); + +void +isc_hmac_key_destroy(isc_hmac_key_t **keyp); + +/** + * isc_hmac_key_expose: + * @key: The key to be exposed + * + * This function exposes the raw bytes of the HMAC key. + * + * The region is bound to the lifetime of the @key and MUST NOT be used after + * calling isc_hmac_key_destroy. + */ +isc_region_t +isc_hmac_key_expose(isc_hmac_key_t *key); + +/** + * isc_hmac_key_equal: + * @key1: The first key to be compared + * @key2: The second key to be compared + * + * Returns true is the two HMAC keys have the same contents and use the same + * digest function. + */ +bool +isc_hmac_key_equal(isc_hmac_key_t *key1, isc_hmac_key_t *key2); + /** * isc_hmac_new: * @@ -68,26 +112,11 @@ isc_hmac_free(isc_hmac_t *hmac); * isc_hmac_init: * @md: HMAC context * @key: HMAC key - * @keylen: HMAC key length - * @type: digest type * - * This function sets up HMAC context to use a hash function of @type and key - * @key which is @keylen bytes long. + * This function sets up HMAC context to use the secret specified in @key. */ - isc_result_t -isc_hmac_init(isc_hmac_t *hmac, const void *key, const size_t keylen, - isc_md_type_t type); - -/** - * isc_hmac_reset: - * @hmac: HMAC context - * - * This function resets the HMAC context. This can be used to reuse an already - * existing context. - */ -isc_result_t -isc_hmac_reset(isc_hmac_t *hmac); +isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key); /** * isc_hmac_update: @@ -104,34 +133,13 @@ isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len); /** * isc_hmac_final: * @hmac: HMAC context - * @digest: the output buffer - * @digestlen: in: the length of @digest - * out: the length of the data written to @digest + * @out: the output buffer * * This function retrieves the message authentication code from @hmac and places - * it in @digest, which must have space for the hash function output. @digestlen - * is used to pass in the length of the digest buffer and returns the length - * of digest written to @digest. After calling this function no additional - * calls to isc_hmac_update() can be made. - */ -isc_result_t -isc_hmac_final(isc_hmac_t *hmac, unsigned char *digest, - unsigned int *digestlen); - -/** - * isc_hmac_get_size: + * it in @out, which must have space for the hash function output. * - * This function return the size of the message digest when passed an isc_hmac_t - * structure, i.e. the size of the hash. + * After calling this function no additional calls to isc_hmac_update() can be + * made. Use isc_hmac_init() to reset/re-initialize the context. */ -size_t -isc_hmac_get_size(isc_hmac_t *hmac); - -/** - * isc_hmac_get_block_size: - * - * This function return the block size of the message digest when passed an - * isc_hmac_t structure. - */ -int -isc_hmac_get_block_size(isc_hmac_t *hmac); +isc_result_t +isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out); diff --git a/lib/isc/meson.build b/lib/isc/meson.build index 645ff469cf5..0836490f02f 100644 --- a/lib/isc/meson.build +++ b/lib/isc/meson.build @@ -88,7 +88,6 @@ isc_srcset.add( 'helper.c', 'hex.c', 'histo.c', - 'hmac.c', 'ht.c', 'httpd.c', 'interfaceiter.c', diff --git a/tests/isc/hmac_test.c b/tests/isc/hmac_test.c index 0245d6041b4..31797f7e448 100644 --- a/tests/isc/hmac_test.c +++ b/tests/isc/hmac_test.c @@ -21,13 +21,6 @@ #include #include -/* - * As a workaround, include an OpenSSL header file before including cmocka.h, - * because OpenSSL 3.1.0 uses __attribute__(malloc), conflicting with a - * redefined malloc in cmocka.h. - */ -#include - #define UNIT_TESTING #include @@ -39,14 +32,11 @@ #include #include -#include "hmac.c" - #include #define TEST_INPUT(x) (x), sizeof(x) - 1 -static int -_setup(void **state) { +ISC_SETUP_TEST_IMPL(hmac_state) { isc_hmac_t *hmac_st = isc_hmac_new(); if (hmac_st == NULL) { return -1; @@ -69,9 +59,7 @@ _reset(void **state) { if (*state == NULL) { return -1; } - if (isc_hmac_reset(*state) != ISC_R_SUCCESS) { - return -1; - } + return 0; } @@ -96,11 +84,16 @@ static void isc_hmac_test(isc_hmac_t *hmac_st, const void *key, size_t keylen, isc_md_type_t type, const char *buf, size_t buflen, const char *result, const size_t repeats) { + isc_hmac_key_t *hkey = NULL; isc_result_t res; + assert_int_equal( + isc_hmac_key_create(type, key, keylen, isc_g_mctx, &hkey), + ISC_R_SUCCESS); assert_non_null(hmac_st); - assert_int_equal(isc_hmac_init(hmac_st, key, keylen, type), - ISC_R_SUCCESS); + + res = isc_hmac_init(hmac_st, hkey); + assert_return_code(res, ISC_R_SUCCESS); for (size_t i = 0; i < repeats; i++) { assert_int_equal(isc_hmac_update(hmac_st, @@ -109,14 +102,16 @@ isc_hmac_test(isc_hmac_t *hmac_st, const void *key, size_t keylen, ISC_R_SUCCESS); } - unsigned char digest[ISC_MAX_MD_SIZE]; - unsigned int digestlen = sizeof(digest); - assert_int_equal(isc_hmac_final(hmac_st, digest, &digestlen), - ISC_R_SUCCESS); + unsigned char raw_digest[ISC_MAX_MD_SIZE]; + isc_buffer_t digest; + + isc_buffer_init(&digest, raw_digest, sizeof(raw_digest)); + assert_int_equal(isc_hmac_final(hmac_st, &digest), ISC_R_SUCCESS); char hexdigest[ISC_MAX_MD_SIZE * 2 + 3]; - isc_region_t r = { .base = digest, .length = digestlen }; + isc_region_t r; isc_buffer_t b; + isc_buffer_usedregion(&digest, &r); isc_buffer_init(&b, hexdigest, sizeof(hexdigest)); res = isc_hex_totext(&r, 0, "", &b); @@ -124,106 +119,86 @@ isc_hmac_test(isc_hmac_t *hmac_st, const void *key, size_t keylen, assert_return_code(res, ISC_R_SUCCESS); assert_memory_equal(hexdigest, result, result ? strlen(result) : 0); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + + isc_hmac_key_destroy(&hkey); } -ISC_RUN_TEST_IMPL(isc_hmac_init) { - isc_hmac_t *hmac_st = *state; - assert_non_null(hmac_st); +ISC_RUN_TEST_IMPL(isc_hmac_key_create) { + isc_hmac_key_t *key = NULL; - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_UNKNOWN), - ISC_R_NOTIMPLEMENTED); + assert_int_equal( + isc_hmac_key_create(ISC_MD_UNKNOWN, "", 0, isc_g_mctx, &key), + ISC_R_NOTIMPLEMENTED); if (!isc_crypto_fips_mode()) { - expect_assert_failure(isc_hmac_init(NULL, "", 0, ISC_MD_MD5)); - - expect_assert_failure( - isc_hmac_init(hmac_st, NULL, 0, ISC_MD_MD5)); + /* + expect_assert_failure(isc_hmac_key_create(ISC_MD_MD5, NULL, 0, + isc_g_mctx, &key)); + */ - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_MD5), + assert_int_equal(isc_hmac_key_create(ISC_MD_MD5, "", 0, + isc_g_mctx, &key), ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + isc_hmac_key_destroy(&key); } - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA1), - ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + assert_int_equal( + isc_hmac_key_create(ISC_MD_SHA1, "", 0, isc_g_mctx, &key), + ISC_R_SUCCESS); + isc_hmac_key_destroy(&key); - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA224), - ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + assert_int_equal( + isc_hmac_key_create(ISC_MD_SHA224, "", 0, isc_g_mctx, &key), + ISC_R_SUCCESS); + isc_hmac_key_destroy(&key); - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA256), - ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + assert_int_equal( + isc_hmac_key_create(ISC_MD_SHA256, "", 0, isc_g_mctx, &key), + ISC_R_SUCCESS); + isc_hmac_key_destroy(&key); - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA384), - ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + assert_int_equal( + isc_hmac_key_create(ISC_MD_SHA384, "", 0, isc_g_mctx, &key), + ISC_R_SUCCESS); + isc_hmac_key_destroy(&key); - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA512), - ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); + assert_int_equal( + isc_hmac_key_create(ISC_MD_SHA512, "", 0, isc_g_mctx, &key), + ISC_R_SUCCESS); + isc_hmac_key_destroy(&key); } ISC_RUN_TEST_IMPL(isc_hmac_update) { isc_hmac_t *hmac_st = *state; assert_non_null(hmac_st); - /* Uses message digest context initialized in isc_hmac_init_test() */ - expect_assert_failure(isc_hmac_update(NULL, NULL, 0)); - assert_int_equal(isc_hmac_update(hmac_st, NULL, 100), ISC_R_SUCCESS); assert_int_equal(isc_hmac_update(hmac_st, (const unsigned char *)"", 0), ISC_R_SUCCESS); } -ISC_RUN_TEST_IMPL(isc_hmac_reset) { +ISC_RUN_TEST_IMPL(isc_hmac_final) { + isc_hmac_key_t *key = NULL; isc_hmac_t *hmac_st = *state; -#if 0 - unsigned char digest[ISC_MAX_MD_SIZE] ISC_ATTR_UNUSED; - unsigned int digestlen ISC_ATTR_UNUSED; -#endif /* if 0 */ - assert_non_null(hmac_st); - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA512), - ISC_R_SUCCESS); - assert_int_equal( - isc_hmac_update(hmac_st, (const unsigned char *)"a", 1), - ISC_R_SUCCESS); assert_int_equal( - isc_hmac_update(hmac_st, (const unsigned char *)"b", 1), + isc_hmac_key_create(ISC_MD_SHA512, "", 0, isc_g_mctx, &key), ISC_R_SUCCESS); - assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS); - -#if 0 - /* - * This test would require OpenSSL compiled with mock_assert(), - * so this could be only manually checked that the test will - * segfault when called by hand - */ - expect_assert_failure(isc_hmac_final(hmac_st, digest, &digestlen)); -#endif /* if 0 */ -} - -ISC_RUN_TEST_IMPL(isc_hmac_final) { - isc_hmac_t *hmac_st = *state; - assert_non_null(hmac_st); - unsigned char digest[ISC_MAX_MD_SIZE]; - unsigned int digestlen = sizeof(digest); + isc_buffer_t digestbuf; - /* Fail when message digest context is empty */ - expect_assert_failure(isc_hmac_final(NULL, digest, &digestlen)); /* Fail when output buffer is empty */ - expect_assert_failure(isc_hmac_final(hmac_st, NULL, &digestlen)); + isc_buffer_init(&digestbuf, NULL, 0); + assert_int_equal(isc_hmac_final(hmac_st, &digestbuf), ISC_R_NOSPACE); - assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA512), - ISC_R_SUCCESS); - /* Fail when the digest length pointer is empty */ - expect_assert_failure(isc_hmac_final(hmac_st, digest, NULL)); + /* Fail when the digest length is empty */ + assert_int_equal(isc_hmac_init(hmac_st, key), ISC_R_SUCCESS); + isc_buffer_init(&digestbuf, digest, 0); + assert_int_equal(isc_hmac_final(hmac_st, &digestbuf), ISC_R_NOSPACE); + + isc_hmac_key_destroy(&key); } ISC_RUN_TEST_IMPL(isc_hmac_md5) { @@ -919,10 +894,7 @@ ISC_RUN_TEST_IMPL(isc_hmac_sha512) { ISC_TEST_LIST_START ISC_TEST_ENTRY(isc_hmac_new) -ISC_TEST_ENTRY_CUSTOM(isc_hmac_init, _reset, _reset) - -ISC_TEST_ENTRY_CUSTOM(isc_hmac_reset, _reset, _reset) - +ISC_TEST_ENTRY(isc_hmac_key_create) ISC_TEST_ENTRY(isc_hmac_md5) ISC_TEST_ENTRY(isc_hmac_sha1) ISC_TEST_ENTRY(isc_hmac_sha224) @@ -937,4 +909,4 @@ ISC_TEST_ENTRY(isc_hmac_free) ISC_TEST_LIST_END -ISC_TEST_MAIN_CUSTOM(_setup, _teardown) +ISC_TEST_MAIN_CUSTOM(setup_test_hmac_state, _teardown)