From a3b6729a885c4776a969012fe67228f665ac1764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Wed, 28 Dec 2022 17:13:41 +0200 Subject: [PATCH] Refactor OpenSSL ECDSA to use pkeypair - Use separate EVP_PKEY for public and private keys - On private key load, generate public key allowing better consistency - Support OpenSSL3 providers - Clean up key construction abstraction - Various other clean ups --- lib/dns/openssl_shim.h | 7 + lib/dns/opensslecdsa_link.c | 881 +++++++++++------------------------- 2 files changed, 263 insertions(+), 625 deletions(-) diff --git a/lib/dns/openssl_shim.h b/lib/dns/openssl_shim.h index 120384952b..87a4136388 100644 --- a/lib/dns/openssl_shim.h +++ b/lib/dns/openssl_shim.h @@ -56,6 +56,13 @@ EVP_PKEY_get0_RSA(const EVP_PKEY *pkey) { } #endif +#if !HAVE_EVP_PKEY_GET0_EC_KEY && OPENSSL_VERSION_NUMBER < 0x10100000L +static inline const EC_KEY * +EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey) { + return (pkey->type == EVP_PKEY_EC ? pkey->pkey.ec : NULL); +} +#endif + #if !HAVE_RSA_SET0_KEY && OPENSSL_VERSION_NUMBER < 0x30000000L int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c index 957e1cfb5a..29dc6eb320 100644 --- a/lib/dns/opensslecdsa_link.c +++ b/lib/dns/opensslecdsa_link.c @@ -16,20 +16,15 @@ #include #include -#include -#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 -#include -#endif #include #include #include #include -#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 +#include +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +#include #include #endif -#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 -#include -#endif #include #include @@ -68,27 +63,65 @@ opensslecdsa_valid_key_alg(unsigned int key_alg) { } } +static int +opensslecdsa_key_alg_to_group_nid(unsigned int key_alg) { + switch (key_alg) { + case DST_ALG_ECDSA256: + return (NID_X9_62_prime256v1); + case DST_ALG_ECDSA384: + return (NID_secp384r1); + default: + UNREACHABLE(); + } +} + +/* + * OpenSSL requires us to set the public key portion, but since our private key + * file format does not contain it directly, we generate it as needed. + */ +static EC_POINT * +opensslecdsa_generate_public_key(const EC_GROUP *group, const BIGNUM *privkey) { + EC_POINT *pubkey = EC_POINT_new(group); + if (pubkey == NULL) { + return (NULL); + } + if (EC_POINT_mul(group, pubkey, privkey, NULL, NULL, NULL) != 1) { + EC_POINT_free(pubkey); + return (NULL); + } + return (pubkey); +} + #if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 + +static const char * +opensslecdsa_key_alg_to_group_name(unsigned int key_alg) { + switch (key_alg) { + case DST_ALG_ECDSA256: + return ("P-256"); + case DST_ALG_ECDSA384: + return ("P-384"); + default: + UNREACHABLE(); + } +} + static isc_result_t -raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, - size_t key_len, EVP_PKEY **pkey) { +opensslecdsa_create_pkey(unsigned int key_alg, bool private, + const unsigned char *key, size_t key_len, + EVP_PKEY **pkey) { isc_result_t ret; int status; - const char *groupname; + int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); + const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg); OSSL_PARAM_BLD *bld = NULL; OSSL_PARAM *params = NULL; EVP_PKEY_CTX *ctx = NULL; + EC_POINT *pubkey = NULL; + EC_GROUP *group = NULL; BIGNUM *priv = NULL; unsigned char buf[DNS_KEY_ECDSA384SIZE + 1]; - if (key_alg == DST_ALG_ECDSA256) { - groupname = "P-256"; - } else if (key_alg == DST_ALG_ECDSA384) { - groupname = "P-384"; - } else { - DST_RET(ISC_R_NOTIMPLEMENTED); - } - bld = OSSL_PARAM_BLD_new(); if (bld == NULL) { DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_new", @@ -103,6 +136,13 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, } if (private) { + group = EC_GROUP_new_by_curve_name(group_nid); + if (group == NULL) { + DST_RET(dst__openssl_toresult2("EC_GROUP_new_by_" + "curve_name", + DST_R_OPENSSLFAILURE)); + } + priv = BN_bin2bn(key, key_len, NULL); if (priv == NULL) { DST_RET(dst__openssl_toresult2("BN_bin2bn", @@ -115,18 +155,34 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN", DST_R_OPENSSLFAILURE)); } - } else { - INSIST(key_len < sizeof(buf)); - buf[0] = POINT_CONVERSION_UNCOMPRESSED; - memmove(buf + 1, key, key_len); - status = OSSL_PARAM_BLD_push_octet_string( - bld, OSSL_PKEY_PARAM_PUB_KEY, buf, 1 + key_len); - if (status != 1) { - DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_" - "octet_string", + pubkey = opensslecdsa_generate_public_key(group, priv); + if (pubkey == NULL) { + DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + + key = buf; + key_len = EC_POINT_point2oct(group, pubkey, + POINT_CONVERSION_UNCOMPRESSED, buf, + sizeof(buf), NULL); + if (key_len == 0) { + DST_RET(dst__openssl_toresult2("EC_POINT_point2oct", DST_R_OPENSSLFAILURE)); } + } else { + INSIST(key_len + 1 <= sizeof(buf)); + buf[0] = POINT_CONVERSION_UNCOMPRESSED; + memmove(buf + 1, key, key_len); + key = buf; + key_len = key_len + 1; + } + + status = OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, + key, key_len); + if (status != 1) { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_" + "octet_string", + DST_R_OPENSSLFAILURE)); } params = OSSL_PARAM_BLD_to_param(bld); @@ -155,21 +211,111 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key, ret = ISC_R_SUCCESS; err: - if (params != NULL) { - OSSL_PARAM_free(params); - } - if (bld != NULL) { - OSSL_PARAM_BLD_free(bld); - } - if (ctx != NULL) { - EVP_PKEY_CTX_free(ctx); - } - if (priv != NULL) { - BN_clear_free(priv); - } + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(ctx); + BN_clear_free(priv); + EC_POINT_free(pubkey); + EC_GROUP_free(group); return (ret); } + +static isc_result_t +opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) { + const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg); + char gname[64]; + + if (EVP_PKEY_get_group_name(pkey, gname, sizeof(gname), NULL) != 1) { + return (DST_R_INVALIDPRIVATEKEY); + } + if (strcmp(gname, groupname) != 0) { + return (DST_R_INVALIDPRIVATEKEY); + } + return (ISC_R_SUCCESS); +} + +#else + +static isc_result_t +opensslecdsa_create_pkey(unsigned int key_alg, bool private, + const unsigned char *key, size_t key_len, + EVP_PKEY **retkey) { + isc_result_t ret = ISC_R_SUCCESS; + EC_KEY *eckey = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *privkey = NULL; + EC_POINT *pubkey = NULL; + unsigned char buf[DNS_KEY_ECDSA384SIZE + 1]; + int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); + + eckey = EC_KEY_new_by_curve_name(group_nid); + if (eckey == NULL) { + DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + + if (private) { + const EC_GROUP *group = EC_KEY_get0_group(eckey); + + privkey = BN_bin2bn(key, key_len, NULL); + if (privkey == NULL) { + DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + if (!EC_KEY_set_private_key(eckey, privkey)) { + DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); + } + + pubkey = opensslecdsa_generate_public_key(group, privkey); + if (pubkey == NULL) { + DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + if (EC_KEY_set_public_key(eckey, pubkey) != 1) { + DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + } else { + const unsigned char *cp = buf; + INSIST(key_len + 1 <= sizeof(buf)); + buf[0] = POINT_CONVERSION_UNCOMPRESSED; + memmove(buf + 1, key, key_len); + if (o2i_ECPublicKey(&eckey, &cp, key_len + 1) == NULL) { + DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY)); + } + if (EC_KEY_check_key(eckey) != 1) { + DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY)); + } + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) { + DST_RET(dst__openssl_toresult(ISC_R_FAILURE)); + } + + *retkey = pkey; + pkey = NULL; + +err: + BN_clear_free(privkey); + EC_POINT_free(pubkey); + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return (ret); +} + +static isc_result_t +opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) { + const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); + int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); + + if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)) != group_nid) { + return (DST_R_INVALIDPRIVATEKEY); + } + + return (ISC_R_SUCCESS); +} + #endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 \ */ @@ -195,7 +341,7 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { if (dctx->use == DO_SIGN) { if (EVP_DigestSignInit(evp_md_ctx, NULL, type, NULL, - dctx->key->keydata.pkey) != 1) + dctx->key->keydata.pkeypair.priv) != 1) { EVP_MD_CTX_destroy(evp_md_ctx); DST_RET(dst__openssl_toresult3(dctx->category, @@ -204,7 +350,7 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { } } else { if (EVP_DigestVerifyInit(evp_md_ctx, NULL, type, NULL, - dctx->key->keydata.pkey) != 1) + dctx->key->keydata.pkeypair.pub) != 1) { EVP_MD_CTX_destroy(evp_md_ctx); DST_RET(dst__openssl_toresult3(dctx->category, @@ -415,76 +561,6 @@ err: return (ret); } -static bool -opensslecdsa_compare(const dst_key_t *key1, const dst_key_t *key2) { - bool ret; - EVP_PKEY *pkey1 = key1->keydata.pkey; - EVP_PKEY *pkey2 = key2->keydata.pkey; -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - EC_KEY *eckey1 = NULL; - EC_KEY *eckey2 = NULL; - const BIGNUM *priv1; - const BIGNUM *priv2; -#else - BIGNUM *priv1 = NULL; - BIGNUM *priv2 = NULL; -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - - if (pkey1 == NULL && pkey2 == NULL) { - return (true); - } else if (pkey1 == NULL || pkey2 == NULL) { - return (false); - } - - /* `EVP_PKEY_eq` checks only the public key components and paramters. */ - if (EVP_PKEY_eq(pkey1, pkey2) != 1) { - DST_RET(false); - } - -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - eckey1 = EVP_PKEY_get1_EC_KEY(pkey1); - eckey2 = EVP_PKEY_get1_EC_KEY(pkey2); - if (eckey1 == NULL && eckey2 == NULL) { - DST_RET(true); - } else if (eckey1 == NULL || eckey2 == NULL) { - DST_RET(false); - } - priv1 = EC_KEY_get0_private_key(eckey1); - priv2 = EC_KEY_get0_private_key(eckey2); -#else - EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_PRIV_KEY, &priv1); - EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_PRIV_KEY, &priv2); -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - - if (priv1 != NULL || priv2 != NULL) { - if (priv1 == NULL || priv2 == NULL || BN_cmp(priv1, priv2) != 0) - { - DST_RET(false); - } - } - - ret = true; - -err: -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - if (eckey1 != NULL) { - EC_KEY_free(eckey1); - } - if (eckey2 != NULL) { - EC_KEY_free(eckey2); - } -#else - if (priv1 != NULL) { - BN_clear_free(priv1); - } - if (priv2 != NULL) { - BN_clear_free(priv2); - } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - - return (ret); -} - static isc_result_t opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { isc_result_t ret; @@ -502,13 +578,7 @@ opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { UNUSED(unused); UNUSED(callback); - if (key->key_alg == DST_ALG_ECDSA256) { - group_nid = NID_X9_62_prime256v1; - key->key_size = DNS_KEY_ECDSA256SIZE * 4; - } else { - group_nid = NID_secp384r1; - key->key_size = DNS_KEY_ECDSA384SIZE * 4; - } + group_nid = opensslecdsa_key_alg_to_group_nid(key->key_alg); #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 eckey = EC_KEY_new_by_curve_name(group_nid); @@ -573,7 +643,9 @@ opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { } #endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - key->keydata.pkey = pkey; + key->key_size = EVP_PKEY_bits(pkey); + key->keydata.pkeypair.priv = pkey; + key->keydata.pkeypair.pub = pkey; pkey = NULL; ret = ISC_R_SUCCESS; @@ -599,48 +671,19 @@ err: static bool opensslecdsa_isprivate(const dst_key_t *key) { - bool ret; - EVP_PKEY *pkey; -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - EC_KEY *eckey; -#else - BIGNUM *priv = NULL; -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); - pkey = key->keydata.pkey; - if (pkey == NULL) { - return (false); - } - -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - eckey = EVP_PKEY_get1_EC_KEY(pkey); - - ret = (eckey != NULL && EC_KEY_get0_private_key(eckey) != NULL); - if (eckey != NULL) { - EC_KEY_free(eckey); - } -#else - ret = (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv) == - 1 && - priv != NULL); - if (priv != NULL) { - BN_clear_free(priv); - } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - - return (ret); + return (key->keydata.pkeypair.priv != NULL); } static void opensslecdsa_destroy(dst_key_t *key) { - EVP_PKEY *pkey = key->keydata.pkey; - - if (pkey != NULL) { - EVP_PKEY_free(pkey); - key->keydata.pkey = NULL; + if (key->keydata.pkeypair.priv != key->keydata.pkeypair.pub) { + EVP_PKEY_free(key->keydata.pkeypair.priv); } + EVP_PKEY_free(key->keydata.pkeypair.pub); + key->keydata.pkeypair.pub = NULL; + key->keydata.pkeypair.priv = NULL; } static isc_result_t @@ -661,9 +704,9 @@ opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) { isc_region_t r; unsigned char buf[DNS_KEY_ECDSA384SIZE + 1]; - REQUIRE(key->keydata.pkey != NULL); + REQUIRE(key->keydata.pkeypair.pub != NULL); - pkey = key->keydata.pkey; + pkey = key->keydata.pkeypair.pub; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 eckey = EVP_PKEY_get1_EC_KEY(pkey); @@ -738,15 +781,8 @@ opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) { isc_result_t ret; EVP_PKEY *pkey = NULL; isc_region_t r; -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - EC_KEY *eckey = NULL; - const unsigned char *cp; - unsigned int len; - unsigned char buf[DNS_KEY_ECDSA384SIZE + 1]; - int group_nid; -#else + size_t len; -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); @@ -764,56 +800,17 @@ opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) { DST_RET(DST_R_INVALIDPUBLICKEY); } -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - if (key->key_alg == DST_ALG_ECDSA256) { - group_nid = NID_X9_62_prime256v1; - } else { - group_nid = NID_secp384r1; - } - - eckey = EC_KEY_new_by_curve_name(group_nid); - if (eckey == NULL) { - DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - - buf[0] = POINT_CONVERSION_UNCOMPRESSED; - memmove(buf + 1, r.base, len); - cp = buf; - if (o2i_ECPublicKey(&eckey, (const unsigned char **)&cp, - (long)len + 1) == NULL) - { - DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY)); - } - if (EC_KEY_check_key(eckey) != 1) { - DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY)); - } - - pkey = EVP_PKEY_new(); - if (pkey == NULL) { - DST_RET(ISC_R_NOMEMORY); - } - if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) { - EVP_PKEY_free(pkey); - DST_RET(dst__openssl_toresult(ISC_R_FAILURE)); - } -#else - ret = raw_key_to_ossl(key->key_alg, 0, r.base, len, &pkey); + ret = opensslecdsa_create_pkey(key->key_alg, false, r.base, len, &pkey); if (ret != ISC_R_SUCCESS) { DST_RET(ret); } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ isc_buffer_forward(data, len); - key->keydata.pkey = pkey; - key->key_size = len * 4; + key->key_size = EVP_PKEY_bits(pkey); + key->keydata.pkeypair.pub = pkey; ret = ISC_R_SUCCESS; err: -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - if (eckey != NULL) { - EC_KEY_free(eckey); - } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ return (ret); } @@ -832,7 +829,7 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) { unsigned char *buf = NULL; unsigned short i; - if (key->keydata.pkey == NULL) { + if (key->keydata.pkeypair.pub == NULL) { DST_RET(DST_R_NULLKEY); } @@ -841,7 +838,11 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) { DST_RET(dst__privstruct_writefile(key, &priv, directory)); } - pkey = key->keydata.pkey; + if (key->keydata.pkeypair.priv == NULL) { + DST_RET(DST_R_NULLKEY); + } + + pkey = key->keydata.pkeypair.priv; #if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 eckey = EVP_PKEY_get1_EC_KEY(pkey); if (eckey == NULL) { @@ -908,271 +909,6 @@ err: return (ret); } -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 -static isc_result_t -ecdsa_check(EC_KEY *eckey, EC_KEY *pubeckey) { - const EC_POINT *pubkey; - - pubkey = EC_KEY_get0_public_key(eckey); - if (pubkey != NULL) { - return (ISC_R_SUCCESS); - } else if (pubeckey != NULL) { - pubkey = EC_KEY_get0_public_key(pubeckey); - if (pubkey == NULL) { - return (ISC_R_SUCCESS); - } - if (EC_KEY_set_public_key(eckey, pubkey) != 1) { - return (ISC_R_SUCCESS); - } - } - if (EC_KEY_check_key(eckey) == 1) { - return (ISC_R_SUCCESS); - } - - return (ISC_R_FAILURE); -} -#else -static isc_result_t -ecdsa_check(EVP_PKEY **pkey, EVP_PKEY *pubpkey) { - isc_result_t ret = ISC_R_FAILURE; - int status; - size_t pkey_len = 0; - BIGNUM *x = NULL; - BIGNUM *y = NULL; - BIGNUM *priv = NULL; - char groupname[80]; - unsigned char buf[DNS_KEY_ECDSA384SIZE + 1]; - size_t keysize; - OSSL_PARAM_BLD *bld = NULL; - OSSL_PARAM *params = NULL; - EVP_PKEY_CTX *ctx = NULL; - EVP_PKEY *pkey_new = NULL; - - /* Check if `pkey` has a public key. */ - status = EVP_PKEY_get_octet_string_param(*pkey, OSSL_PKEY_PARAM_PUB_KEY, - NULL, 0, &pkey_len); - - /* Check if `pubpkey` exists and that we can extract its public key. */ - if (pubpkey == NULL || - EVP_PKEY_get_bn_param(pubpkey, OSSL_PKEY_PARAM_EC_PUB_X, &x) != 1 || - x == NULL || - EVP_PKEY_get_bn_param(pubpkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) != 1 || - y == NULL) - { - if (status != 1 || pkey_len == 0) { - /* No public key both in `pkey` and in `pubpkey` */ - DST_RET(DST_R_INVALIDPRIVATEKEY); - } else { - /* - * `pkey` has a public key, but there is no public key - * in `pubpkey` to check against. - */ - DST_RET(ISC_R_SUCCESS); - } - } - - /* - * If `pkey` doesn't have a public key then we will copy it from - * `pubpkey`. - */ - if (status != 1 || pkey_len == 0) { - /* - * We can't (?) add a public key to an existing PKEY, so we - * have to create a new PKEY. - */ - - keysize = (EVP_PKEY_bits(*pkey) + 7) / 8; - /* - * The "raw" public key is created by combining the "x" and "y" - * parts. - */ - keysize *= 2; - buf[0] = POINT_CONVERSION_UNCOMPRESSED; - BN_bn2bin_fixed(x, &buf[1], keysize / 2); - BN_bn2bin_fixed(y, &buf[1 + keysize / 2], keysize / 2); - - groupname[0] = '\0'; - status = EVP_PKEY_get_utf8_string_param( - *pkey, OSSL_PKEY_PARAM_GROUP_NAME, groupname, - sizeof groupname, NULL); - if (status != 1 || strlen(groupname) == 0) { - DST_RET(ISC_R_FAILURE); - } - status = EVP_PKEY_get_bn_param(*pkey, OSSL_PKEY_PARAM_PRIV_KEY, - &priv); - if (status != 1) { - DST_RET(ISC_R_FAILURE); - } - - bld = OSSL_PARAM_BLD_new(); - if (bld == NULL) { - DST_RET(ISC_R_FAILURE); - } - if (OSSL_PARAM_BLD_push_utf8_string( - bld, OSSL_PKEY_PARAM_GROUP_NAME, groupname, 0) != 1) - { - DST_RET(ISC_R_FAILURE); - } - if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, - priv) != 1) - { - DST_RET(ISC_R_FAILURE); - } - if (OSSL_PARAM_BLD_push_octet_string(bld, - OSSL_PKEY_PARAM_PUB_KEY, - buf, 1 + keysize) != 1) - { - DST_RET(ISC_R_FAILURE); - } - params = OSSL_PARAM_BLD_to_param(bld); - if (params == NULL) { - DST_RET(ISC_R_FAILURE); - } - - ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); - if (ctx == NULL) { - DST_RET(ISC_R_FAILURE); - } - if (EVP_PKEY_fromdata_init(ctx) != 1) { - DST_RET(ISC_R_FAILURE); - } - status = EVP_PKEY_fromdata(ctx, &pkey_new, EVP_PKEY_KEYPAIR, - params); - if (status != 1 || pkey_new == NULL) { - DST_RET(ISC_R_FAILURE); - } - - /* Replace the old key with the new one. */ - EVP_PKEY_free(*pkey); - *pkey = pkey_new; - } - - if (EVP_PKEY_eq(*pkey, pubpkey) == 1) { - DST_RET(ISC_R_SUCCESS); - } - -err: - if (ctx != NULL) { - EVP_PKEY_CTX_free(ctx); - } - if (params != NULL) { - OSSL_PARAM_free(params); - } - if (bld != NULL) { - OSSL_PARAM_BLD_free(bld); - } - if (priv != NULL) { - BN_clear_free(priv); - } - if (x != NULL) { - BN_clear_free(x); - } - if (y != NULL) { - BN_clear_free(y); - } - - return (ret); -} -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 -static isc_result_t -load_privkey_from_privstruct(EC_KEY *eckey, dst_private_t *priv, - int privkey_index) { - BIGNUM *privkey = BN_bin2bn(priv->elements[privkey_index].data, - priv->elements[privkey_index].length, NULL); - isc_result_t result = ISC_R_SUCCESS; - - if (privkey == NULL) { - return (ISC_R_NOMEMORY); - } - - if (!EC_KEY_set_private_key(eckey, privkey)) { - result = ISC_R_NOMEMORY; - } - - BN_clear_free(privkey); - return (result); -} - -static isc_result_t -eckey_to_pkey(EC_KEY *eckey, EVP_PKEY **pkey) { - REQUIRE(pkey != NULL && *pkey == NULL); - - *pkey = EVP_PKEY_new(); - if (*pkey == NULL) { - return (ISC_R_NOMEMORY); - } - if (!EVP_PKEY_set1_EC_KEY(*pkey, eckey)) { - EVP_PKEY_free(*pkey); - *pkey = NULL; - return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - return (ISC_R_SUCCESS); -} -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - -static isc_result_t -finalize_eckey(dst_key_t *key, -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - EC_KEY *eckey, -#endif - const char *engine, const char *label) { - isc_result_t result = ISC_R_SUCCESS; -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - EVP_PKEY *pkey = NULL; - - REQUIRE(eckey != NULL); - - result = eckey_to_pkey(eckey, &pkey); - if (result != ISC_R_SUCCESS) { - return (result); - } - - key->keydata.pkey = pkey; -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - - if (label != NULL) { - key->label = isc_mem_strdup(key->mctx, label); - key->engine = isc_mem_strdup(key->mctx, engine); - } - - if (key->key_alg == DST_ALG_ECDSA256) { - key->key_size = DNS_KEY_ECDSA256SIZE * 4; - } else { - key->key_size = DNS_KEY_ECDSA384SIZE * 4; - } - - return (result); -} - -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 -static isc_result_t -dst__key_to_eckey(dst_key_t *key, EC_KEY **eckey) { - int group_nid; - - REQUIRE(eckey != NULL && *eckey == NULL); - - switch (key->key_alg) { - case DST_ALG_ECDSA256: - group_nid = NID_X9_62_prime256v1; - break; - case DST_ALG_ECDSA384: - group_nid = NID_secp384r1; - break; - default: - UNREACHABLE(); - } - - *eckey = EC_KEY_new_by_curve_name(group_nid); - if (*eckey == NULL) { - return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - - return (ISC_R_SUCCESS); -} -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - static isc_result_t opensslecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label, const char *pin); @@ -1181,14 +917,10 @@ static isc_result_t opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; isc_result_t ret; -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - EC_KEY *eckey = NULL; - EC_KEY *pubeckey = NULL; -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ + EVP_PKEY *pkey = NULL; const char *engine = NULL; const char *label = NULL; int i, privkey_index = -1; - bool finalize_key = false; REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); @@ -1203,8 +935,10 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (priv.nelements != 0 || pub == NULL) { DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); } - key->keydata.pkey = pub->keydata.pkey; - pub->keydata.pkey = NULL; + key->keydata.pkeypair.priv = pub->keydata.pkeypair.priv; + key->keydata.pkeypair.pub = pub->keydata.pkeypair.pub; + pub->keydata.pkeypair.priv = NULL; + pub->keydata.pkeypair.pub = NULL; DST_RET(ISC_R_SUCCESS); } @@ -1233,78 +967,37 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { if (ret != ISC_R_SUCCESS) { goto err; } - -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - eckey = EVP_PKEY_get1_EC_KEY(key->keydata.pkey); - if (eckey == NULL) { - DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + /* Check that the public component matches if given */ + if (pub != NULL && EVP_PKEY_eq(key->keydata.pkeypair.pub, + pub->keydata.pkeypair.pub) != 1) + { + DST_RET(DST_R_INVALIDPRIVATEKEY); } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - } else { -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - ret = dst__key_to_eckey(key, &eckey); - if (ret != ISC_R_SUCCESS) { - goto err; - } - - ret = load_privkey_from_privstruct(eckey, &priv, privkey_index); -#else - if (key->keydata.pkey != NULL) { - EVP_PKEY_free(key->keydata.pkey); - key->keydata.pkey = NULL; - } - - ret = raw_key_to_ossl(key->key_alg, 1, - priv.elements[privkey_index].data, - priv.elements[privkey_index].length, - &key->keydata.pkey); -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ - - if (ret != ISC_R_SUCCESS) { - goto err; - } - - finalize_key = true; + DST_RET(ISC_R_SUCCESS); } -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - if (pub != NULL && pub->keydata.pkey != NULL) { - pubeckey = EVP_PKEY_get1_EC_KEY(pub->keydata.pkey); + ret = opensslecdsa_create_pkey( + key->key_alg, true, priv.elements[privkey_index].data, + priv.elements[privkey_index].length, &pkey); + if (ret != ISC_R_SUCCESS) { + goto err; } - if (ecdsa_check(eckey, pubeckey) != ISC_R_SUCCESS) { - DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); + /* Check that the public component matches if given */ + if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) { + DST_RET(DST_R_INVALIDPRIVATEKEY); } - if (finalize_key) { - ret = finalize_eckey(key, eckey, engine, label); - } -#else - if (ecdsa_check(&key->keydata.pkey, - pub == NULL ? NULL : pub->keydata.pkey) != - ISC_R_SUCCESS) - { - DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); - } - - if (finalize_key) { - ret = finalize_eckey(key, engine, label); - } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ + key->key_size = EVP_PKEY_bits(pkey); + key->keydata.pkeypair.priv = pkey; + key->keydata.pkeypair.pub = pkey; + pkey = NULL; err: -#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 - if (pubeckey != NULL) { - EC_KEY_free(pubeckey); - } - if (eckey != NULL) { - EC_KEY_free(eckey); - } -#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */ + EVP_PKEY_free(pkey); if (ret != ISC_R_SUCCESS) { key->keydata.generic = NULL; } - dst__privstruct_free(&priv, key->mctx); isc_safe_memwipe(&priv, sizeof(priv)); @@ -1314,101 +1007,39 @@ err: static isc_result_t opensslecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label, const char *pin) { -#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 - isc_result_t ret = ISC_R_SUCCESS; - ENGINE *e; - EC_KEY *eckey = NULL; - EC_KEY *pubeckey = NULL; - int group_nid; - EVP_PKEY *pkey = NULL; - EVP_PKEY *pubpkey = NULL; + EVP_PKEY *privpkey = NULL, *pubpkey = NULL; + isc_result_t ret; REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); - UNUSED(pin); - if (engine == NULL || label == NULL) { - return (DST_R_NOENGINE); - } - e = dst__openssl_getengine(engine); - if (e == NULL) { - DST_RET(DST_R_NOENGINE); + ret = dst__openssl_fromlabel(EVP_PKEY_EC, engine, label, pin, &pubpkey, + &privpkey); + if (ret != ISC_R_SUCCESS) { + goto err; } - if (key->key_alg == DST_ALG_ECDSA256) { - group_nid = NID_X9_62_prime256v1; - } else { - group_nid = NID_secp384r1; + ret = opensslecdsa_validate_pkey_group(key->key_alg, privpkey); + if (ret != ISC_R_SUCCESS) { + goto err; + } + ret = opensslecdsa_validate_pkey_group(key->key_alg, pubpkey); + if (ret != ISC_R_SUCCESS) { + goto err; } - /* Load private key. */ - pkey = ENGINE_load_private_key(e, label, NULL, NULL); - if (pkey == NULL) { - DST_RET(dst__openssl_toresult2("ENGINE_load_private_key", - DST_R_OPENSSLFAILURE)); - } - /* Check base id, group nid */ - if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { - DST_RET(DST_R_INVALIDPRIVATEKEY); - } - eckey = EVP_PKEY_get1_EC_KEY(pkey); - if (eckey == NULL) { - DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)) != group_nid) { - DST_RET(DST_R_INVALIDPRIVATEKEY); - } - - /* Load public key. */ - pubpkey = ENGINE_load_public_key(e, label, NULL, NULL); - if (pubpkey == NULL) { - DST_RET(dst__openssl_toresult2("ENGINE_load_public_key", - DST_R_OPENSSLFAILURE)); - } - /* Check base id, group nid */ - if (EVP_PKEY_base_id(pubpkey) != EVP_PKEY_EC) { - DST_RET(DST_R_INVALIDPUBLICKEY); - } - pubeckey = EVP_PKEY_get1_EC_KEY(pubpkey); - if (pubeckey == NULL) { - DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - if (EC_GROUP_get_curve_name(EC_KEY_get0_group(pubeckey)) != group_nid) { - DST_RET(DST_R_INVALIDPUBLICKEY); - } - - if (ecdsa_check(eckey, pubeckey) != ISC_R_SUCCESS) { - DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); - } - - key->label = isc_mem_strdup(key->mctx, label); key->engine = isc_mem_strdup(key->mctx, engine); - key->key_size = EVP_PKEY_bits(pkey); - key->keydata.pkey = pkey; - pkey = NULL; + key->label = isc_mem_strdup(key->mctx, label); + key->key_size = EVP_PKEY_bits(privpkey); + key->keydata.pkeypair.priv = privpkey; + key->keydata.pkeypair.pub = pubpkey; + privpkey = NULL; + pubpkey = NULL; err: - if (pubpkey != NULL) { - EVP_PKEY_free(pubpkey); - } - if (pkey != NULL) { - EVP_PKEY_free(pkey); - } - if (pubeckey != NULL) { - EC_KEY_free(pubeckey); - } - if (eckey != NULL) { - EC_KEY_free(eckey); - } - + EVP_PKEY_free(privpkey); + EVP_PKEY_free(pubpkey); return (ret); -#else - UNUSED(key); - UNUSED(engine); - UNUSED(label); - UNUSED(pin); - return (DST_R_NOENGINE); -#endif /* !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000 */ } static dst_func_t opensslecdsa_functions = { @@ -1420,7 +1051,7 @@ static dst_func_t opensslecdsa_functions = { opensslecdsa_verify, NULL, /*%< verify2 */ NULL, /*%< computesecret */ - opensslecdsa_compare, + dst__openssl_compare_keypair, NULL, /*%< paramcompare */ opensslecdsa_generate, opensslecdsa_isprivate,