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.
This commit is contained in:
Aydın Mercan 2025-09-17 14:52:35 +02:00
parent f9ec4a1cdf
commit 8f106f2b66
No known key found for this signature in database
8 changed files with 618 additions and 395 deletions

View file

@ -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;

View file

@ -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;
isc_result_t r;
REQUIRE(ctx != NULL);
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen = sizeof(digest);
if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
r = isc_hmac_final(ctx, sig);
/* Turn CRYPTOFAILURE into OPENSSLFAILURE */
if (r == ISC_R_CRYPTOFAILURE) {
r = DST_R_OPENSSLFAILURE;
}
if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
if (isc_buffer_availablelength(sig) < digestlen) {
return ISC_R_NOSPACE;
}
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) {
isc_buffer_init(&hmac, digest, sizeof(digest));
if (isc_hmac_final(ctx, &hmac) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
if (isc_hmac_reset(ctx) != 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) {
REQUIRE(key != NULL && key->keydata.hmac_key != NULL);
dst_hmac_key_t *hkey = key->keydata.hmac_key;
unsigned int bytes;
isc_region_t raw_key;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes) {
REQUIRE(key != NULL && key->keydata.hmac_key != NULL);
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);

View file

@ -11,21 +11,37 @@
* information regarding copyright ownership.
*/
#include <stdint.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <isc/buffer.h>
#include <isc/crypto.h>
#include <isc/hmac.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/safe.h>
#include <isc/tls.h>
#include <isc/util.h>
#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:

View file

@ -11,6 +11,10 @@
* information regarding copyright ownership.
*/
#include <stdbool.h>
#include <stdint.h>
#include <openssl/core_names.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
@ -18,19 +22,64 @@
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <isc/buffer.h>
#include <isc/crypto.h>
#include <isc/hmac.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/region.h>
#include <isc/safe.h>
#include <isc/tls.h>
#include <isc/util.h>
#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

View file

@ -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 <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#include <isc/assertions.h>
#include <isc/hmac.h>
#include <isc/md.h>
#include <isc/safe.h>
#include <isc/string.h>
#include <isc/types.h>
#include <isc/util.h>
#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;
}

View file

@ -18,12 +18,16 @@
#pragma once
#include <stdbool.h>
#include <isc/md.h>
#include <isc/result.h>
#include <isc/types.h>
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.
*/
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.
* This function sets up HMAC context to use the secret specified in @key.
*/
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.
* it in @out, which must have space for the hash function output.
*
* After calling this function no additional calls to isc_hmac_update() can be
* made. Use isc_hmac_init() to reset/re-initialize the context.
*/
isc_result_t
isc_hmac_final(isc_hmac_t *hmac, unsigned char *digest,
unsigned int *digestlen);
/**
* isc_hmac_get_size:
*
* This function return the size of the message digest when passed an isc_hmac_t
* structure, i.e. the size of the hash.
*/
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_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out);

View file

@ -88,7 +88,6 @@ isc_srcset.add(
'helper.c',
'hex.c',
'histo.c',
'hmac.c',
'ht.c',
'httpd.c',
'interfaceiter.c',

View file

@ -21,13 +21,6 @@
#include <stdlib.h>
#include <string.h>
/*
* 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 <openssl/err.h>
#define UNIT_TESTING
#include <cmocka.h>
@ -39,14 +32,11 @@
#include <isc/region.h>
#include <isc/result.h>
#include "hmac.c"
#include <tests/isc.h>
#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_key_create(ISC_MD_MD5, NULL, 0,
isc_g_mctx, &key));
*/
expect_assert_failure(
isc_hmac_init(hmac_st, NULL, 0, ISC_MD_MD5));
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_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_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_key_t *key = NULL;
isc_hmac_t *hmac_st = *state;
assert_non_null(hmac_st);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA512, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
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)