#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#include <openssl/kdf.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <isc/safe.h>
#include <isc/util.h>
+#ifdef HAVE_OPENSSL_HKDF_H
+#include <openssl/hkdf.h>
+#endif /* HAVE_OPENSSL_HKDF_H */
+
+#define CRYPTO_ERROR(fn) \
+ isc__ossl_wrap_logged_toresult( \
+ ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, fn, \
+ ISC_R_CRYPTOFAILURE, __FILE__, __LINE__)
+
#define HMAC_KEY_MAGIC ISC_MAGIC('H', 'M', 'A', 'C')
struct isc_hmac_key {
#endif /* !LIBRESSL_VERSION_NUMBER */
+#ifdef HAVE_OPENSSL_HKDF_H
+
+isc_result_t
+isc_crypto_hkdf_extract(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret, isc_constregion_t salt) {
+ EVP_MD *evp;
+ size_t len;
+
+ REQUIRE(out.base != NULL && out.length != 0 &&
+ out.length <= EVP_MAX_MD_SIZE);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(salt.base != NULL && salt.length != 0);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ len = out.length;
+ if (HKDF_extract(out.base, &len, evp, secret.base, secret.length,
+ salt.base, salt.length) != 1)
+ {
+ return CRYPTO_ERROR("HKDF_extract");
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+isc_crypto_hkdf_expand(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t prk, isc_constregion_t info) {
+ EVP_MD *evp;
+
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(prk.base != NULL && prk.length != 0);
+ REQUIRE(info.base != NULL && info.length != 0);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ if (HKDF_expand(out.base, out.length, evp, prk.base, prk.length,
+ info.base, info.length) != 1)
+ {
+ return CRYPTO_ERROR("HKDF_expand");
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+isc_crypto_hkdf_expand_label(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret,
+ isc_constregion_t label) {
+ const uint8_t label_prefix[] = { 't', 'l', 's', '1', '3', ' ' };
+ uint8_t hkdf_label[256];
+ isc_buffer_t buffer;
+ EVP_MD *evp;
+
+ REQUIRE(out.base != NULL && out.length != 0 &&
+ out.length <= UINT16_MAX);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(label.base != NULL && label.length != 0 && label.length <= 12);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ /*
+ * struct {
+ * uint16 length = Length;
+ * opaque label<7..255> = "tls13 " + Label;
+ * opaque context<0..255> = Context;
+ * } HkdfLabel;
+ */
+ isc_buffer_init(&buffer, hkdf_label, sizeof(hkdf_label));
+ isc_buffer_putuint16(&buffer, out.length);
+ isc_buffer_putuint8(&buffer, sizeof(label_prefix) + label.length);
+ isc_buffer_putmem(&buffer, label_prefix, sizeof(label_prefix));
+ isc_buffer_putmem(&buffer, label.base, label.length);
+ isc_buffer_putuint8(&buffer, 0);
+
+ if (HKDF_expand(out.base, out.length, evp, secret.base, secret.length,
+ isc_buffer_base(&buffer),
+ isc_buffer_usedlength(&buffer)) != 1)
+ {
+ return CRYPTO_ERROR("HKDF_expand");
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t
+isc_crypto_hkdf(isc_region_t out, isc_md_type_t md, isc_constregion_t secret,
+ isc_constregion_t salt, isc_constregion_t info) {
+ EVP_MD *evp;
+
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(salt.base != NULL && salt.length != 0);
+ REQUIRE(info.base != NULL && info.length != 0);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ if (HKDF(out.base, out.length, evp, secret.base, secret.length,
+ salt.base, salt.length, info.base, info.length) != 1)
+ {
+ return CRYPTO_ERROR("HKDF");
+ }
+
+ return ISC_R_SUCCESS;
+}
+
+#else /* HAVE_OPENSSL_HKDF_H */
+
+isc_result_t
+isc_crypto_hkdf_extract(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret, isc_constregion_t salt) {
+ isc_result_t result;
+ EVP_PKEY_CTX *pctx;
+ EVP_MD *evp;
+ size_t len;
+
+ REQUIRE(out.base != NULL && out.length != 0 &&
+ out.length <= EVP_MAX_MD_SIZE);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(salt.base != NULL && salt.length != 0);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_new_id"));
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive_init"));
+ }
+
+ if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1)
+ {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_hkdf_mode"));
+ }
+
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, evp) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set_hkdf_md"));
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.base, salt.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set1_hkdf_salt"));
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret.base, secret.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set1_hkdf_key"));
+ }
+
+ len = out.length;
+ if (EVP_PKEY_derive(pctx, out.base, &len) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive"));
+ }
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_PKEY_CTX_free(pctx);
+ return result;
+}
+
+isc_result_t
+isc_crypto_hkdf_expand(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t prk, isc_constregion_t info) {
+ isc_result_t result;
+ EVP_PKEY_CTX *pctx;
+ EVP_MD *evp;
+ size_t len;
+
+ REQUIRE(out.base != NULL && out.length != 0 &&
+ out.length <= UINT16_MAX);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(prk.base != NULL && prk.length != 0);
+ REQUIRE(info.base != NULL && info.length != 0);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_new_id"));
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive_init"));
+ }
+
+ if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1)
+ {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_hkdf_mode"));
+ }
+
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, evp) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set_hkdf_md"));
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, prk.base, prk.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set1_hkdf_key"));
+ }
+
+ if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info.base, info.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_add1_hkdf_info"));
+ }
+
+ len = out.length;
+ if (EVP_PKEY_derive(pctx, out.base, &len) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive"));
+ }
+
+ INSIST(len == out.length);
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_PKEY_CTX_free(pctx);
+ return result;
+}
+
+isc_result_t
+isc_crypto_hkdf_expand_label(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret,
+ isc_constregion_t label) {
+ const uint8_t label_prefix[] = { 't', 'l', 's', '1', '3', ' ' };
+ uint8_t hkdf_label[256];
+ isc_buffer_t buffer;
+ isc_result_t result;
+ EVP_PKEY_CTX *pctx;
+ EVP_MD *evp;
+ size_t len;
+
+ REQUIRE(out.base != NULL && out.length != 0 &&
+ out.length <= UINT16_MAX);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(label.base != NULL && label.length != 0 && label.length <= 12);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ /*
+ * struct {
+ * uint16 length = Length;
+ * opaque label<7..255> = "tls13 " + Label;
+ * opaque context<0..255> = Context;
+ * } HkdfLabel;
+ */
+ isc_buffer_init(&buffer, hkdf_label, sizeof(hkdf_label));
+ isc_buffer_putuint16(&buffer, out.length);
+ isc_buffer_putuint8(&buffer, sizeof(label_prefix) + label.length);
+ isc_buffer_putmem(&buffer, label_prefix, sizeof(label_prefix));
+ isc_buffer_putmem(&buffer, label.base, label.length);
+ isc_buffer_putuint8(&buffer, 0);
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_new_id"));
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive_init"));
+ }
+
+ if (EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1)
+ {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_hkdf_mode"));
+ }
+
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, evp) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set_hkdf_md"));
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret.base, secret.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set1_hkdf_key"));
+ }
+
+ if (EVP_PKEY_CTX_add1_hkdf_info(pctx, isc_buffer_base(&buffer),
+ isc_buffer_usedlength(&buffer)) != 1)
+ {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_add1_hkdf_info"));
+ }
+
+ len = out.length;
+ if (EVP_PKEY_derive(pctx, out.base, &len) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive"));
+ }
+
+ INSIST(len == out.length);
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_PKEY_CTX_free(pctx);
+ return result;
+}
+
+isc_result_t
+isc_crypto_hkdf(isc_region_t out, isc_md_type_t md, isc_constregion_t secret,
+ isc_constregion_t salt, isc_constregion_t info) {
+ isc_result_t result;
+ EVP_PKEY_CTX *pctx;
+ EVP_MD *evp;
+ size_t len;
+
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(salt.base != NULL && salt.length != 0);
+ REQUIRE(info.base != NULL && info.length != 0);
+
+ evp = isc__crypto_md[md];
+ if (evp == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_new_id"));
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive_init"));
+ }
+
+ if (EVP_PKEY_CTX_set_hkdf_md(pctx, evp) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set_hkdf_md"));
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.base, salt.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set1_hkdf_salt"));
+ }
+
+ if (EVP_PKEY_CTX_set1_hkdf_key(pctx, secret.base, secret.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_set1_hkdf_key"));
+ }
+
+ if (EVP_PKEY_CTX_add1_hkdf_info(pctx, info.base, info.length) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_CTX_add1_hkdf_info"));
+ }
+
+ len = out.length;
+ if (EVP_PKEY_derive(pctx, out.base, &len) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_PKEY_derive"));
+ }
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_PKEY_CTX_free(pctx);
+ return result;
+}
+
+#endif /* HAVE_OPENSSL_HKDF_H */
+
#ifdef HAVE_FIPS_MODE
bool
isc_crypto_fips_mode(void) {
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
+#include <openssl/kdf.h>
#include <openssl/provider.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <isc/safe.h>
#include <isc/util.h>
+#define CRYPTO_ERROR(fn) \
+ isc__ossl_wrap_logged_toresult( \
+ ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, fn, \
+ ISC_R_CRYPTOFAILURE, __FILE__, __LINE__)
+
struct isc_hmac_key {
uint32_t magic;
uint32_t len;
static OSSL_PROVIDER *base = NULL, *fips = NULL;
+/*
+ * Because HKDF-Expand-Label is defined in the RFC of TLS 1.3, OpenSSL
+ * has named the algorithm as TLS1.3 KDF internally.
+ */
+static EVP_KDF *evp_tls_1_3_kdf = NULL;
+static EVP_KDF *evp_hkdf = NULL;
+
static EVP_MAC *evp_hmac = NULL;
+static isc_constregion_t md_to_name[ISC_MD_MAX] = {
+ [ISC_MD_UNKNOWN] = { NULL, 0 },
+ [ISC_MD_MD5] = { "MD5", sizeof("MD5") - 1 },
+ [ISC_MD_SHA1] = { "SHA1", sizeof("SHA1") - 1 },
+ [ISC_MD_SHA224] = { "SHA2-224", sizeof("SHA2-224") - 1 },
+ [ISC_MD_SHA256] = { "SHA2-256", sizeof("SHA2-256") - 1 },
+ [ISC_MD_SHA384] = { "SHA2-384", sizeof("SHA2-384") - 1 },
+ [ISC_MD_SHA512] = { "SHA2-512", sizeof("SHA2-512") - 1 },
+};
+
static OSSL_PARAM md_to_hmac_params[ISC_MD_MAX][2] = {
[ISC_MD_UNKNOWN] = { OSSL_PARAM_END },
[ISC_MD_MD5] = {
"EVP_MAC-HMAC implementation");
}
+ evp_tls_1_3_kdf = EVP_KDF_fetch(NULL, OSSL_KDF_NAME_TLS1_3_KDF, NULL);
+ if (evp_tls_1_3_kdf == NULL) {
+ FATAL_ERROR(
+ "OpenSSL failed to find an TLS 1.3 KDF implementation."
+ "Please make sure the default provider has an "
+ "EVP_KDF-TLS13_KDF implementation");
+ }
+
+ evp_hkdf = EVP_KDF_fetch(NULL, OSSL_KDF_NAME_HKDF, NULL);
+ if (evp_hkdf == NULL) {
+ ERR_clear_error();
+ FATAL_ERROR("OpenSSL failed to find an HKDF implementation. "
+ "Please make sure the default provider has an "
+ "EVP_KDF-HKDF implementation");
+ }
+
return ISC_R_SUCCESS;
}
static void
unregister_algorithms(void) {
+ INSIST(evp_hkdf != NULL);
+ EVP_KDF_free(evp_hkdf);
+ evp_hkdf = NULL;
+
+ INSIST(evp_tls_1_3_kdf != NULL);
+ EVP_KDF_free(evp_tls_1_3_kdf);
+ evp_tls_1_3_kdf = NULL;
+
for (size_t i = 0; i < ISC_MD_MAX; i++) {
if (isc__crypto_md[i] != NULL) {
EVP_MD_free(isc__crypto_md[i]);
return ISC_R_SUCCESS;
}
+isc_result_t
+isc_crypto_hkdf_extract(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret, isc_constregion_t salt) {
+ isc_result_t result;
+ EVP_KDF_CTX *ctx;
+ int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
+
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(salt.base != NULL && salt.length != 0);
+
+ if (isc__crypto_md[md] == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ const OSSL_PARAM params[] = {
+ OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ UNCONST(md_to_name[md].base),
+ md_to_name[md].length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY,
+ UNCONST(secret.base), secret.length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, UNCONST(salt.base),
+ salt.length),
+ OSSL_PARAM_END,
+ };
+
+ ctx = EVP_KDF_CTX_new(evp_hkdf);
+ if (ctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_CTX_new"));
+ }
+
+ if (EVP_KDF_derive(ctx, out.base, out.length, params) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_derive"));
+ }
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_KDF_CTX_free(ctx);
+ return result;
+}
+
+isc_result_t
+isc_crypto_hkdf_expand(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t prk, isc_constregion_t info) {
+ isc_result_t result;
+ EVP_KDF_CTX *ctx;
+ int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
+
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(prk.base != NULL && prk.length != 0);
+ REQUIRE(info.base != NULL && info.length != 0);
+
+ if (isc__crypto_md[md] == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ const OSSL_PARAM params[] = {
+ OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ UNCONST(md_to_name[md].base),
+ md_to_name[md].length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, UNCONST(prk.base),
+ prk.length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, UNCONST(info.base),
+ info.length),
+ OSSL_PARAM_END,
+ };
+
+ ctx = EVP_KDF_CTX_new(evp_hkdf);
+ if (ctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_CTX_new"));
+ }
+
+ if (EVP_KDF_derive(ctx, out.base, out.length, params) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_derive"));
+ }
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_KDF_CTX_free(ctx);
+ return result;
+}
+
+isc_result_t
+isc_crypto_hkdf_expand_label(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret,
+ isc_constregion_t label) {
+ isc_result_t result;
+ EVP_KDF_CTX *ctx;
+ int mode = EVP_PKEY_HKDEF_MODE_EXPAND_ONLY;
+
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(secret.base != NULL && secret.length != 0);
+ REQUIRE(label.base != NULL && label.length != 0);
+
+ if (isc__crypto_md[md] == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ const OSSL_PARAM params[] = {
+ OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ UNCONST(md_to_name[md].base),
+ md_to_name[md].length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX,
+ UNCONST("tls13 "),
+ sizeof("tls13 ") - 1),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY,
+ UNCONST(secret.base), secret.length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL,
+ UNCONST(label.base), label.length),
+ OSSL_PARAM_END,
+ };
+
+ /* Please see the comment in `evp_tls_1_3_kdf` */
+ ctx = EVP_KDF_CTX_new(evp_tls_1_3_kdf);
+ if (ctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_CTX_new"));
+ }
+
+ if (EVP_KDF_derive(ctx, out.base, out.length, params) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_derive"));
+ }
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_KDF_CTX_free(ctx);
+ return result;
+}
+
+isc_result_t
+isc_crypto_hkdf(isc_region_t out, isc_md_type_t md, isc_constregion_t ikm,
+ isc_constregion_t salt, isc_constregion_t info) {
+ isc_result_t result;
+ EVP_KDF_CTX *ctx;
+ int mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND;
+
+ REQUIRE(md != ISC_MD_UNKNOWN && md < ISC_MD_MAX);
+ REQUIRE(out.base != NULL && out.length != 0);
+ REQUIRE(ikm.base != NULL && ikm.length != 0);
+ REQUIRE(salt.base != NULL && salt.length != 0);
+ REQUIRE(info.base != NULL && info.length != 0);
+
+ if (isc__crypto_md[md] == NULL) {
+ return ISC_R_NOTIMPLEMENTED;
+ }
+
+ const OSSL_PARAM params[] = {
+ OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ UNCONST(md_to_name[md].base),
+ md_to_name[md].length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, UNCONST(ikm.base),
+ ikm.length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, UNCONST(info.base),
+ info.length),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, UNCONST(salt.base),
+ salt.length),
+ OSSL_PARAM_END,
+ };
+
+ ctx = EVP_KDF_CTX_new(evp_hkdf);
+ if (ctx == NULL) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_CTX_new"));
+ }
+
+ if (EVP_KDF_derive(ctx, out.base, out.length, params) != 1) {
+ CLEANUP(CRYPTO_ERROR("EVP_KDF_derive"));
+ }
+
+ result = ISC_R_SUCCESS;
+cleanup:
+ EVP_KDF_CTX_free(ctx);
+ return result;
+}
+
bool
isc_crypto_fips_mode(void) {
return EVP_default_properties_is_fips_enabled(NULL) != 0;
#pragma once
+#include <stddef.h>
+#include <stdint.h>
+
+#include <isc/md.h>
+#include <isc/region.h>
#include <isc/types.h>
+isc_result_t
+isc_crypto_hkdf_extract(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret, isc_constregion_t salt);
+/**<
+ * HKDF-Extract as specified by RFC5869.
+ *
+ * Requires:
+ * - `out.base != NULL` and `out.length != 0`
+ * - `md` must be a valid hash type.
+ * - `secret.base != NULL` and `secret.length != 0`
+ * - `salt.base != NULL` and `sakt.length != 0`
+ *
+ * \retval ISC_R_SUCCESS on success
+ * \retval ISC_R_NOTIMPLEMENTED if the hash function is not supported
+ * \retval ISC_R_CRYPTOFAILURE on libcrypto failure
+ */
+
+isc_result_t
+isc_crypto_hkdf_expand(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t prk, isc_constregion_t info);
+/**<
+ * HKDF-Expand as specified by RFC5869.
+ *
+ * Please note that `prk` can't be just substituted with the output of any
+ * secret function. Please refer to RFC5869 Section 3.3 for more information on
+ * what key values are appropriate for calling HKDF-Expand.
+ *
+ * Requires:
+ * - `out.base != NULL` and `out.length != 0`
+ * - `md` must be a valid hash type.
+ * - `prk.base != NULL` and `prk.length != 0`
+ * - `info.base != NULL` and `info.length != 0`
+ *
+ * \retval ISC_R_SUCCESS on success
+ * \retval ISC_R_NOTIMPLEMENTED if the hash function is not supported
+ * \retval ISC_R_CRYPTOFAILURE on libcrypto failure
+ */
+
+isc_result_t
+isc_crypto_hkdf_expand_label(isc_region_t out, isc_md_type_t md,
+ isc_constregion_t secret, isc_constregion_t label);
+/**<
+ * \brief
+ * HKDF-Expand-Label as specified by RFC 8446 Section 7.1.
+ * The context parameter is not supported and is passed as empty.
+ *
+ * Requires:
+ * - `out.base != NULL` and `out.length != 0`
+ * - `md` must be a valid hash type.
+ * - `secret.base != NULL` and `secret.length != 0`
+ * - `label.base != NULL` and `label.length != 0`
+ *
+ * \retval ISC_R_SUCCESS on success
+ * \retval ISC_R_NOTIMPLEMENTED if the hash function is not supported
+ * \retval ISC_R_CRYPTOFAILURE on libcrypto failure
+ */
+
+isc_result_t
+isc_crypto_hkdf(isc_region_t out, isc_md_type_t md, isc_constregion_t secret,
+ isc_constregion_t salt, isc_constregion_t info);
+/**<
+ * \brief
+ * HKDF-Extract-Expand as specified by RFC5869.
+ *
+ * Requires:
+ * - `out.base != NULL` and `out.length != 0`
+ * - `md` must be a valid hash type.
+ * - `ikm.base != NULL` and `ikm.length != 0`
+ * - `salt.base != NULL` and `ikm.length != 0`
+ * - `info.base != NULL` and `info.length != 0`
+ *
+ * \retval ISC_R_SUCCESS on success
+ * \retval ISC_R_NOTIMPLEMENTED if the hash function is not supported
+ * \retval ISC_R_CRYPTOFAILURE on libcrypto failure
+ */
+
bool
isc_crypto_fips_mode(void);
/*
)
endforeach
+config.set(
+ 'HAVE_OPENSSL_HKDF_H',
+ cc.has_header('openssl/hkdf.h', dependencies: openssl_dep),
+)
+
## libuv
uv_dep = dependency('libuv', version: '>=1.34.0')
if uv_dep.version().version_compare('<1.40.0')
--- /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 <inttypes.h>
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h> /* IWYU pragma: keep */
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/crypto.h>
+#include <isc/lib.h>
+#include <isc/md.h>
+
+#include <tests/isc.h>
+
+ISC_RUN_TEST_IMPL(hkdf) {
+ isc_result_t result;
+ uint8_t actual[42];
+
+ const uint8_t ikm[22] = { [0 ... 21] = 0x0B };
+ const uint8_t salt[13] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C,
+ };
+ const uint8_t info[10] = {
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9,
+ };
+ const uint8_t expected[42] = {
+ 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90,
+ 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d,
+ 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d,
+ 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08,
+ 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
+ };
+
+ result = isc_crypto_hkdf((isc_region_t){ actual, sizeof(actual) },
+ ISC_MD_SHA256,
+ (isc_constregion_t){ ikm, sizeof(ikm) },
+ (isc_constregion_t){ salt, sizeof(salt) },
+ (isc_constregion_t){ info, sizeof(info) });
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ assert_memory_equal(expected, actual, 42);
+}
+
+ISC_RUN_TEST_IMPL(hkdf_expand_label) {
+ isc_result_t result;
+ uint8_t actual[32];
+
+ /*
+ * Values are taken from RFC 9001 Appendix A.1.
+ */
+ const uint8_t secret[32] = {
+ 0x7d, 0xb5, 0xdf, 0x06, 0xe7, 0xa6, 0x9e, 0x43,
+ 0x24, 0x96, 0xad, 0xed, 0xb0, 0x08, 0x51, 0x92,
+ 0x35, 0x95, 0x22, 0x15, 0x96, 0xae, 0x2a, 0xe9,
+ 0xfb, 0x81, 0x15, 0xc1, 0xe9, 0xed, 0x0a, 0x44,
+ };
+
+ const uint8_t expected[32] = {
+ 0xc0, 0x0c, 0xf1, 0x51, 0xca, 0x5b, 0xe0, 0x75,
+ 0xed, 0x0e, 0xbf, 0xb5, 0xc8, 0x03, 0x23, 0xc4,
+ 0x2d, 0x6b, 0x7d, 0xb6, 0x78, 0x81, 0x28, 0x9a,
+ 0xf4, 0x00, 0x8f, 0x1f, 0x6c, 0x35, 0x7a, 0xea,
+ };
+
+ result = isc_crypto_hkdf_expand_label(
+ (isc_region_t){ actual, sizeof(actual) }, ISC_MD_SHA256,
+ (isc_constregion_t){ secret, sizeof(secret) },
+ (isc_constregion_t){ "client in", sizeof("client in") - 1 });
+ assert_int_equal(result, ISC_R_SUCCESS);
+ assert_memory_equal(expected, actual, 32);
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY(hkdf)
+ISC_TEST_ENTRY(hkdf_expand_label)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN
'async',
'buffer',
'counter',
+ 'crypto',
'dnsstream_utils',
'errno',
'file',