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
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;
#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); \
} \
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)) { \
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;
}
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;
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;
}
}
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;
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
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);
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);
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) {
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);
* 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) \
#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:
* 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>
#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); \
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;
}
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
+++ /dev/null
-/*
- * 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;
-}
#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
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:
*
* 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:
/**
* 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);
'helper.c',
'hex.c',
'histo.c',
- 'hmac.c',
'ht.c',
'httpd.c',
'interfaceiter.c',
#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>
#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;
if (*state == NULL) {
return -1;
}
- if (isc_hmac_reset(*state) != ISC_R_SUCCESS) {
- return -1;
- }
+
return 0;
}
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,
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);
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) {
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)
ISC_TEST_LIST_END
-ISC_TEST_MAIN_CUSTOM(_setup, _teardown)
+ISC_TEST_MAIN_CUSTOM(setup_test_hmac_state, _teardown)