]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Refactor the OpenSSL DH usage to use newer APIs
authorAram Sargsyan <aram@isc.org>
Tue, 5 Oct 2021 09:11:33 +0000 (09:11 +0000)
committerAram Sargsyan <aram@isc.org>
Thu, 28 Oct 2021 07:39:37 +0000 (07:39 +0000)
OpenSSL 3 deprecates most of the DH* family and associated APIs.

Reimplement the existing functionality using a newer set of APIs
which will be used when compiling/linking with OpenSSL 3.0.0 or newer
versions.

lib/dns/openssldh_link.c

index 5d2f8b22d79a3d252a2052327daa4a132e9be57b..3703057b17d09a491f8280caced86562299d21e5 100644 (file)
 #include <inttypes.h>
 #include <stdbool.h>
 
+#include <openssl/bn.h>
 #include <openssl/opensslv.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/core_names.h>
+#endif
+#include <openssl/err.h>
+#include <openssl/objects.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/param_build.h>
+#endif
+#include <openssl/dh.h>
 
 #include <isc/mem.h>
 #include <isc/result.h>
        "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \
        "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF"
 
+#define DST_RET(a)        \
+       {                 \
+               ret = a;  \
+               goto err; \
+       }
+
 static BIGNUM *bn2 = NULL, *bn768 = NULL, *bn1024 = NULL, *bn1536 = NULL;
 
 static isc_result_t
 openssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
                        isc_buffer_t *secret) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dhpub, *dhpriv;
        const BIGNUM *pub_key = NULL;
-       int ret;
+       int secret_len = 0;
+#else
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *dhpub, *dhpriv;
+       size_t secret_len = 0;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        isc_region_t r;
        unsigned int len;
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        REQUIRE(pub->keydata.dh != NULL);
        REQUIRE(priv->keydata.dh != NULL);
 
@@ -84,28 +107,73 @@ openssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv,
        dhpriv = priv->keydata.dh;
 
        len = DH_size(dhpriv);
+#else
+       REQUIRE(pub->keydata.pkey != NULL);
+       REQUIRE(priv->keydata.pkey != NULL);
+
+       dhpub = pub->keydata.pkey;
+       dhpriv = priv->keydata.pkey;
+
+       len = EVP_PKEY_get_size(dhpriv);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
        isc_buffer_availableregion(secret, &r);
        if (r.length < len) {
                return (ISC_R_NOSPACE);
        }
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH_get0_key(dhpub, &pub_key, NULL);
-       ret = DH_compute_key(r.base, pub_key, dhpriv);
-       if (ret <= 0) {
+       secret_len = DH_compute_key(r.base, pub_key, dhpriv);
+       if (secret_len <= 0) {
                return (dst__openssl_toresult2("DH_compute_key",
                                               DST_R_COMPUTESECRETFAILURE));
        }
-       isc_buffer_add(secret, len);
+#else
+       ctx = EVP_PKEY_CTX_new_from_pkey(NULL, dhpriv, NULL);
+       if (ctx == NULL) {
+               return (dst__openssl_toresult2("EVP_PKEY_CTX_new_from_pkey",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_derive_init(ctx) != 1) {
+               EVP_PKEY_CTX_free(ctx);
+               return (dst__openssl_toresult2("EVP_PKEY_derive_init",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_derive_set_peer(ctx, dhpub) != 1) {
+               EVP_PKEY_CTX_free(ctx);
+               return (dst__openssl_toresult2("EVP_PKEY_derive_set_peer",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       secret_len = r.length;
+       if (EVP_PKEY_derive(ctx, r.base, &secret_len) != 1 || secret_len == 0) {
+               EVP_PKEY_CTX_free(ctx);
+               return (dst__openssl_toresult2("EVP_PKEY_derive",
+                                              DST_R_COMPUTESECRETFAILURE));
+       }
+       EVP_PKEY_CTX_free(ctx);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+       isc_buffer_add(secret, (unsigned int)secret_len);
+
        return (ISC_R_SUCCESS);
 }
 
 static bool
 openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh1, *dh2;
        const BIGNUM *pub_key1 = NULL, *pub_key2 = NULL;
        const BIGNUM *priv_key1 = NULL, *priv_key2 = NULL;
        const BIGNUM *p1 = NULL, *g1 = NULL, *p2 = NULL, *g2 = NULL;
-
+#else
+       EVP_PKEY *pkey1, *pkey2;
+       BIGNUM *pub_key1 = NULL, *pub_key2 = NULL;
+       BIGNUM *priv_key1 = NULL, *priv_key2 = NULL;
+       BIGNUM *p1 = NULL, *g1 = NULL, *p2 = NULL, *g2 = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        dh1 = key1->keydata.dh;
        dh2 = key2->keydata.dh;
 
@@ -119,6 +187,25 @@ openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
        DH_get0_key(dh2, &pub_key2, &priv_key2);
        DH_get0_pqg(dh1, &p1, NULL, &g1);
        DH_get0_pqg(dh2, &p2, NULL, &g2);
+#else
+       pkey1 = key1->keydata.pkey;
+       pkey2 = key2->keydata.pkey;
+
+       if (pkey1 == NULL && pkey2 == NULL) {
+               return (true);
+       } else if (pkey1 == NULL || pkey2 == NULL) {
+               return (false);
+       }
+
+       EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_FFC_P, &p1);
+       EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_FFC_P, &p2);
+       EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_FFC_G, &g1);
+       EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_FFC_G, &g2);
+       EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_PUB_KEY, &pub_key1);
+       EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_PUB_KEY, &pub_key2);
+       EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_PRIV_KEY, &priv_key1);
+       EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_PRIV_KEY, &priv_key2);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L*/
 
        if (BN_cmp(p1, p2) != 0 || BN_cmp(g1, g2) != 0 ||
            BN_cmp(pub_key1, pub_key2) != 0)
@@ -134,14 +221,48 @@ openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) {
                        return (false);
                }
        }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       if (p1 != NULL) {
+               BN_free(p1);
+       }
+       if (p2 != NULL) {
+               BN_free(p2);
+       }
+       if (g1 != NULL) {
+               BN_free(g1);
+       }
+       if (g2 != NULL) {
+               BN_free(g2);
+       }
+       if (pub_key1 != NULL) {
+               BN_free(pub_key1);
+       }
+       if (pub_key2 != NULL) {
+               BN_free(pub_key2);
+       }
+       if (priv_key1 != NULL) {
+               BN_clear_free(priv_key1);
+       }
+       if (priv_key2 != NULL) {
+               BN_clear_free(priv_key2);
+       }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
        return (true);
 }
 
 static bool
 openssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh1, *dh2;
        const BIGNUM *p1 = NULL, *g1 = NULL, *p2 = NULL, *g2 = NULL;
+#else
+       EVP_PKEY *pkey1, *pkey2;
+       BIGNUM *p1 = NULL, *g1 = NULL, *p2 = NULL, *g2 = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        dh1 = key1->keydata.dh;
        dh2 = key2->keydata.dh;
 
@@ -153,13 +274,45 @@ openssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) {
 
        DH_get0_pqg(dh1, &p1, NULL, &g1);
        DH_get0_pqg(dh2, &p2, NULL, &g2);
+#else
+       pkey1 = key1->keydata.pkey;
+       pkey2 = key2->keydata.pkey;
+
+       if (pkey1 == NULL && pkey2 == NULL) {
+               return (true);
+       } else if (pkey1 == NULL || pkey2 == NULL) {
+               return (false);
+       }
+
+       EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_FFC_P, &p1);
+       EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_FFC_P, &p2);
+       EVP_PKEY_get_bn_param(pkey1, OSSL_PKEY_PARAM_FFC_G, &g1);
+       EVP_PKEY_get_bn_param(pkey2, OSSL_PKEY_PARAM_FFC_G, &g2);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        if (BN_cmp(p1, p2) != 0 || BN_cmp(g1, g2) != 0) {
                return (false);
        }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       if (p1 != NULL) {
+               BN_free(p1);
+       }
+       if (p2 != NULL) {
+               BN_free(p2);
+       }
+       if (g1 != NULL) {
+               BN_free(g1);
+       }
+       if (g2 != NULL) {
+               BN_free(g2);
+       }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
        return (true);
 }
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 static int
 progress_cb(int p, int n, BN_GENCB *cb) {
        union {
@@ -175,25 +328,70 @@ progress_cb(int p, int n, BN_GENCB *cb) {
        }
        return (1);
 }
+#else
+static int
+progress_cb(EVP_PKEY_CTX *ctx) {
+       union {
+               void *dptr;
+               void (*fptr)(int);
+       } u;
+
+       u.dptr = EVP_PKEY_CTX_get_app_data(ctx);
+       if (u.fptr != NULL) {
+               int p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
+               u.fptr(p);
+       }
+       return (1);
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
 static isc_result_t
 openssldh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
+       isc_result_t ret;
+       union {
+               void *dptr;
+               void (*fptr)(int);
+       } u;
+       BIGNUM *p = NULL, *g = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh = NULL;
-       BN_GENCB *cb;
+       BN_GENCB *cb = NULL;
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
        BN_GENCB _cb;
 #endif /* if OPENSSL_VERSION_NUMBER < 0x10100000L || \
        * defined(LIBRESSL_VERSION_NUMBER) */
-       union {
-               void *dptr;
-               void (*fptr)(int);
-       } u;
+#else
+       OSSL_PARAM_BLD *bld = NULL;
+       OSSL_PARAM *params = NULL;
+       EVP_PKEY_CTX *param_ctx = NULL;
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *param_pkey = NULL;
+       EVP_PKEY *pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       dh = DH_new();
+       if (dh == NULL) {
+               DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
+       }
+#else
+       bld = OSSL_PARAM_BLD_new();
+       if (bld == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+       param_ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+       if (param_ctx == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        if (generator == 0) {
+               /*
+                * When `generator` is 0, we have three pre-computed `p` and `g`
+                * static parameters which we can use.
+                */
                if (key->key_size == 768 || key->key_size == 1024 ||
                    key->key_size == 1536) {
-                       BIGNUM *p, *g;
-                       dh = DH_new();
                        if (key->key_size == 768) {
                                p = BN_dup(bn768);
                        } else if (key->key_size == 1024) {
@@ -202,34 +400,51 @@ openssldh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
                                p = BN_dup(bn1536);
                        }
                        g = BN_dup(bn2);
-                       if (dh == NULL || p == NULL || g == NULL) {
-                               if (dh != NULL) {
-                                       DH_free(dh);
-                               }
-                               if (p != NULL) {
-                                       BN_free(p);
-                               }
-                               if (g != NULL) {
-                                       BN_free(g);
-                               }
-                               return (dst__openssl_toresult(ISC_R_NOMEMORY));
+                       if (p == NULL || g == NULL) {
+                               DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
                        }
-                       DH_set0_pqg(dh, p, NULL, g);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+                       if (DH_set0_pqg(dh, p, NULL, g) != 1) {
+                               DST_RET(dst__openssl_toresult2(
+                                       "DH_set0_pqg", DST_R_OPENSSLFAILURE));
+                       }
+#else
+                       if (OSSL_PARAM_BLD_push_uint(bld,
+                                                    OSSL_PKEY_PARAM_FFC_PBITS,
+                                                    key->key_size) != 1)
+                       {
+                               DST_RET(dst__openssl_toresult2(
+                                       "OSSL_PARAM_BLD_push_uint",
+                                       DST_R_OPENSSLFAILURE));
+                       }
+                       if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P,
+                                                  p) != 1 ||
+                           OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G,
+                                                  g) != 1)
+                       {
+                               DST_RET(dst__openssl_toresult2(
+                                       "OSSL_PARAM_BLD_push_BN",
+                                       DST_R_OPENSSLFAILURE));
+                       }
+                       params = OSSL_PARAM_BLD_to_param(bld);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
                } else {
+                       /*
+                        * If the requested size is not present in our
+                        * pre-computed set, we will use `generator` 2 to
+                        * generate new parameters.
+                        */
                        generator = 2;
                }
        }
 
        if (generator != 0) {
-               dh = DH_new();
-               if (dh == NULL) {
-                       return (dst__openssl_toresult(ISC_R_NOMEMORY));
-               }
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
                cb = BN_GENCB_new();
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
                if (cb == NULL) {
-                       DH_free(dh);
-                       return (dst__openssl_toresult(ISC_R_NOMEMORY));
+                       DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
                }
 #endif /* if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
        * !defined(LIBRESSL_VERSION_NUMBER) */
@@ -242,38 +457,178 @@ openssldh_generate(dst_key_t *key, int generator, void (*callback)(int)) {
 
                if (!DH_generate_parameters_ex(dh, key->key_size, generator,
                                               cb)) {
-                       DH_free(dh);
-                       BN_GENCB_free(cb);
-                       return (dst__openssl_toresult2("DH_generate_parameters_"
+                       DST_RET(dst__openssl_toresult2("DH_generate_parameters_"
                                                       "ex",
                                                       DST_R_OPENSSLFAILURE));
                }
-               BN_GENCB_free(cb);
-               cb = NULL;
+#else
+               if (OSSL_PARAM_BLD_push_int(bld, OSSL_PKEY_PARAM_DH_GENERATOR,
+                                           generator) != 1) {
+                       DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
+                                                      "int",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+               if (OSSL_PARAM_BLD_push_utf8_string(
+                           bld, OSSL_PKEY_PARAM_FFC_TYPE, "generator", 0) != 1)
+               {
+                       DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
+                                                      "utf8_string",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+               if (OSSL_PARAM_BLD_push_uint(bld, OSSL_PKEY_PARAM_FFC_PBITS,
+                                            key->key_size) != 1)
+               {
+                       DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
+                                                      "uint",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+               params = OSSL_PARAM_BLD_to_param(bld);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        }
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        if (DH_generate_key(dh) == 0) {
-               DH_free(dh);
-               return (dst__openssl_toresult2("DH_generate_key",
+               DST_RET(dst__openssl_toresult2("DH_generate_key",
                                               DST_R_OPENSSLFAILURE));
        }
        DH_clear_flags(dh, DH_FLAG_CACHE_MONT_P);
        key->keydata.dh = dh;
+       dh = NULL;
+#else
+       if (params == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
 
-       return (ISC_R_SUCCESS);
+       if (generator == 0) {
+               if (EVP_PKEY_fromdata_init(param_ctx) != 1) {
+                       DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata_init",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+               if (EVP_PKEY_fromdata(param_ctx, &param_pkey,
+                                     OSSL_KEYMGMT_SELECT_ALL, params) != 1 ||
+                   param_pkey == NULL)
+               {
+                       DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+       } else {
+               if (EVP_PKEY_paramgen_init(param_ctx) != 1) {
+                       DST_RET(dst__openssl_toresult2("EVP_PKEY_paramgen_init",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+               if (EVP_PKEY_CTX_set_params(param_ctx, params) != 1) {
+                       DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_"
+                                                      "params",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+               if (EVP_PKEY_paramgen(param_ctx, &param_pkey) != 1 ||
+                   param_pkey == NULL) {
+                       DST_RET(dst__openssl_toresult2("EVP_PKEY_paramgen",
+                                                      DST_R_OPENSSLFAILURE));
+               }
+       }
+
+       /*
+        * Now `param_pkey` holds the DH parameters (either pre-coumputed or
+        * newly generated) so we will generate a new public/private key-pair
+        * using those parameters and put it into `pkey`.
+        */
+       ctx = EVP_PKEY_CTX_new_from_pkey(NULL, param_pkey, NULL);
+       if (ctx == NULL) {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_pkey",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (callback != NULL) {
+               u.fptr = callback;
+               EVP_PKEY_CTX_set_app_data(ctx, u.dptr);
+               EVP_PKEY_CTX_set_cb(ctx, progress_cb);
+       }
+       if (EVP_PKEY_keygen_init(ctx) != 1) {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_keygen(ctx, &pkey) != 1 || pkey == NULL) {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
+                                              DST_R_OPENSSLFAILURE));
+       }
+
+       key->keydata.pkey = pkey;
+       pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+       ret = ISC_R_SUCCESS;
+
+err:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       if (dh != NULL) {
+               DH_free(dh);
+       }
+       if (cb != NULL) {
+               BN_GENCB_free(cb);
+       }
+#else
+       if (param_pkey != NULL) {
+               EVP_PKEY_free(param_pkey);
+       }
+       if (pkey != NULL) {
+               EVP_PKEY_free(pkey);
+       }
+       if (param_ctx != NULL) {
+               EVP_PKEY_CTX_free(param_ctx);
+       }
+       if (ctx != NULL) {
+               EVP_PKEY_CTX_free(ctx);
+       }
+       if (params != NULL) {
+               OSSL_PARAM_free(params);
+       }
+       if (bld != NULL) {
+               OSSL_PARAM_BLD_free(bld);
+       }
+       if (p != NULL) {
+               BN_free(p);
+       }
+       if (g != NULL) {
+               BN_free(g);
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+       return (ret);
 }
 
 static bool
 openssldh_isprivate(const dst_key_t *key) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh = key->keydata.dh;
        const BIGNUM *priv_key = NULL;
 
        DH_get0_key(dh, NULL, &priv_key);
+
        return (dh != NULL && priv_key != NULL);
+#else
+       bool ret;
+       EVP_PKEY *pkey;
+       BIGNUM *priv_key = NULL;
+
+       pkey = key->keydata.pkey;
+       if (pkey == NULL) {
+               return (false);
+       }
+
+       ret = (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+                                    &priv_key) == 1 &&
+              priv_key != NULL);
+       if (priv_key != NULL) {
+               BN_clear_free(priv_key);
+       }
+
+       return (ret);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 }
 
 static void
 openssldh_destroy(dst_key_t *key) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh = key->keydata.dh;
 
        if (dh == NULL) {
@@ -282,6 +637,16 @@ openssldh_destroy(dst_key_t *key) {
 
        DH_free(dh);
        key->keydata.dh = NULL;
+#else
+       EVP_PKEY *pkey = key->keydata.pkey;
+
+       if (pkey == NULL) {
+               return;
+       }
+
+       EVP_PKEY_free(pkey);
+       key->keydata.pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 }
 
 static void
@@ -307,18 +672,33 @@ uint16_fromregion(isc_region_t *region) {
 
 static isc_result_t
 openssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh;
        const BIGNUM *pub_key = NULL, *p = NULL, *g = NULL;
+#else
+       EVP_PKEY *pkey;
+       BIGNUM *pub_key = NULL, *p = NULL, *g = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        isc_region_t r;
        uint16_t dnslen, plen, glen, publen;
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        REQUIRE(key->keydata.dh != NULL);
 
        dh = key->keydata.dh;
+       DH_get0_pqg(dh, &p, NULL, &g);
+       DH_get0_key(dh, &pub_key, NULL);
+#else
+       REQUIRE(key->keydata.pkey != NULL);
+
+       pkey = key->keydata.pkey;
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, &p);
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, &g);
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        isc_buffer_availableregion(data, &r);
 
-       DH_get0_pqg(dh, &p, NULL, &g);
        if (BN_cmp(g, bn2) == 0 &&
            (BN_cmp(p, bn768) == 0 || BN_cmp(p, bn1024) == 0 ||
             BN_cmp(p, bn1536) == 0))
@@ -329,7 +709,7 @@ openssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
                plen = BN_num_bytes(p);
                glen = BN_num_bytes(g);
        }
-       DH_get0_key(dh, &pub_key, NULL);
+
        publen = BN_num_bytes(pub_key);
        dnslen = plen + glen + publen + 6;
        if (r.length < (unsigned int)dnslen) {
@@ -362,13 +742,34 @@ openssldh_todns(const dst_key_t *key, isc_buffer_t *data) {
 
        isc_buffer_add(data, dnslen);
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       if (p != NULL) {
+               BN_free(p);
+       }
+       if (g != NULL) {
+               BN_free(g);
+       }
+       if (pub_key != NULL) {
+               BN_free(pub_key);
+       }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
        return (ISC_R_SUCCESS);
 }
 
 static isc_result_t
 openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
+       isc_result_t ret;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh;
+#else
+       OSSL_PARAM_BLD *bld = NULL;
+       OSSL_PARAM *params = NULL;
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        BIGNUM *pub_key = NULL, *p = NULL, *g = NULL;
+       int key_size;
        isc_region_t r;
        uint16_t plen, glen, publen;
        int special = 0;
@@ -378,28 +779,36 @@ openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
                return (ISC_R_SUCCESS);
        }
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        dh = DH_new();
        if (dh == NULL) {
-               return (dst__openssl_toresult(ISC_R_NOMEMORY));
+               DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
        }
        DH_clear_flags(dh, DH_FLAG_CACHE_MONT_P);
+#else
+       bld = OSSL_PARAM_BLD_new();
+       if (bld == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+       ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+       if (ctx == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        /*
         * Read the prime length.  1 & 2 are table entries, > 16 means a
         * prime follows, otherwise an error.
         */
        if (r.length < 2) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        plen = uint16_fromregion(&r);
        if (plen < 16 && plen != 1 && plen != 2) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        if (r.length < plen) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        if (plen == 1 || plen == 2) {
                if (plen == 1) {
@@ -419,8 +828,7 @@ openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
                        p = BN_dup(bn1536);
                        break;
                default:
-                       DH_free(dh);
-                       return (DST_R_INVALIDPUBLICKEY);
+                       DST_RET(DST_R_INVALIDPUBLICKEY);
                }
        } else {
                p = BN_bin2bn(r.base, plen, NULL);
@@ -433,13 +841,11 @@ openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
         * special, we have a problem.
         */
        if (r.length < 2) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        glen = uint16_fromregion(&r);
        if (r.length < glen) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        if (special != 0) {
                if (glen == 0) {
@@ -447,46 +853,58 @@ openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
                } else {
                        g = BN_bin2bn(r.base, glen, NULL);
                        if (g != NULL && BN_cmp(g, bn2) != 0) {
-                               DH_free(dh);
-                               BN_free(g);
-                               return (DST_R_INVALIDPUBLICKEY);
+                               DST_RET(DST_R_INVALIDPUBLICKEY);
                        }
                }
        } else {
                if (glen == 0) {
-                       DH_free(dh);
-                       return (DST_R_INVALIDPUBLICKEY);
+                       DST_RET(DST_R_INVALIDPUBLICKEY);
                }
                g = BN_bin2bn(r.base, glen, NULL);
        }
        isc_region_consume(&r, glen);
 
        if (p == NULL || g == NULL) {
-               DH_free(dh);
-               if (p != NULL) {
-                       BN_free(p);
-               }
-               if (g != NULL) {
-                       BN_free(g);
-               }
-               return (dst__openssl_toresult(ISC_R_NOMEMORY));
+               DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
        }
-       DH_set0_pqg(dh, p, NULL, g);
+
+       key_size = BN_num_bits(p);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       if (DH_set0_pqg(dh, p, NULL, g) != 1) {
+               DST_RET(dst__openssl_toresult2("DH_set0_pqg",
+                                              DST_R_OPENSSLFAILURE));
+       }
+
+       /* These are now managed by OpenSSL */
+       p = NULL;
+       g = NULL;
+#else
+       if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) != 1 ||
+           OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) != 1)
+       {
+               DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
+                                              DST_R_OPENSSLFAILURE));
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        if (r.length < 2) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        publen = uint16_fromregion(&r);
        if (r.length < publen) {
-               DH_free(dh);
-               return (DST_R_INVALIDPUBLICKEY);
+               DST_RET(DST_R_INVALIDPUBLICKEY);
        }
        pub_key = BN_bin2bn(r.base, publen, NULL);
        if (pub_key == NULL) {
-               DH_free(dh);
-               return (dst__openssl_toresult(ISC_R_NOMEMORY));
+               DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
        }
+
+       isc_region_consume(&r, publen);
+
+       isc_buffer_forward(data, plen + glen + publen + 6);
+
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
 #if (LIBRESSL_VERSION_NUMBER >= 0x2070000fL) && \
        (LIBRESSL_VERSION_NUMBER <= 0x2070200fL)
        /*
@@ -495,67 +913,141 @@ openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) {
         */
        dh->pub_key = pub_key;
 #else  /* LIBRESSL_VERSION_NUMBER */
-       DH_set0_key(dh, pub_key, NULL);
+       if (DH_set0_key(dh, pub_key, NULL) != 1) {
+               DST_RET(dst__openssl_toresult2("DH_set0_key",
+                                              DST_R_OPENSSLFAILURE));
+       }
 #endif /* LIBRESSL_VERSION_NUMBER */
-       isc_region_consume(&r, publen);
 
-       key->key_size = BN_num_bits(p);
-
-       isc_buffer_forward(data, plen + glen + publen + 6);
+       /* This is now managed by OpenSSL */
+       pub_key = NULL;
 
        key->keydata.dh = dh;
+       dh = NULL;
+#else
+       if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key) != 1)
+       {
+               DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       params = OSSL_PARAM_BLD_to_param(bld);
+       if (params == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_fromdata_init(ctx) != 1) {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata_init",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_fromdata(ctx, &pkey, OSSL_KEYMGMT_SELECT_ALL, params) !=
+                   1 ||
+           pkey == NULL)
+       {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata",
+                                              DST_R_OPENSSLFAILURE));
+       }
 
-       return (ISC_R_SUCCESS);
+       key->keydata.pkey = pkey;
+       pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+       key->key_size = (unsigned int)key_size;
+
+       ret = ISC_R_SUCCESS;
+
+err:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       if (dh != NULL) {
+               DH_free(dh);
+       }
+#else
+       if (pkey != NULL) {
+               EVP_PKEY_free(pkey);
+       }
+       if (ctx != NULL) {
+               EVP_PKEY_CTX_free(ctx);
+       }
+       if (params != NULL) {
+               OSSL_PARAM_free(params);
+       }
+       if (bld != NULL) {
+               OSSL_PARAM_BLD_free(bld);
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+       if (p != NULL) {
+               BN_free(p);
+       }
+       if (g != NULL) {
+               BN_free(g);
+       }
+       if (pub_key != NULL) {
+               BN_free(pub_key);
+       }
+
+       return (ret);
 }
 
 static isc_result_t
 openssldh_tofile(const dst_key_t *key, const char *directory) {
-       int i;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh;
        const BIGNUM *pub_key = NULL, *priv_key = NULL, *p = NULL, *g = NULL;
+#else
+       EVP_PKEY *pkey;
+       BIGNUM *pub_key = NULL, *priv_key = NULL, *p = NULL, *g = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        dst_private_t priv;
-       unsigned char *bufs[4];
+       unsigned char *bufs[4] = { NULL };
+       unsigned short i = 0;
        isc_result_t result;
 
-       if (key->keydata.dh == NULL) {
-               return (DST_R_NULLKEY);
-       }
-
        if (key->external) {
                return (DST_R_EXTERNALKEY);
        }
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       if (key->keydata.dh == NULL) {
+               return (DST_R_NULLKEY);
+       }
+
        dh = key->keydata.dh;
        DH_get0_key(dh, &pub_key, &priv_key);
        DH_get0_pqg(dh, &p, NULL, &g);
-
-       memset(bufs, 0, sizeof(bufs));
-       for (i = 0; i < 4; i++) {
-               bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(p));
+#else
+       if (key->keydata.pkey == NULL) {
+               return (DST_R_NULLKEY);
        }
 
-       i = 0;
+       pkey = key->keydata.pkey;
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_P, &p);
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_FFC_G, &g);
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+       EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv_key);
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        priv.elements[i].tag = TAG_DH_PRIME;
        priv.elements[i].length = BN_num_bytes(p);
+       bufs[i] = isc_mem_get(key->mctx, priv.elements[i].length);
        BN_bn2bin(p, bufs[i]);
        priv.elements[i].data = bufs[i];
        i++;
 
        priv.elements[i].tag = TAG_DH_GENERATOR;
        priv.elements[i].length = BN_num_bytes(g);
+       bufs[i] = isc_mem_get(key->mctx, priv.elements[i].length);
        BN_bn2bin(g, bufs[i]);
        priv.elements[i].data = bufs[i];
        i++;
 
        priv.elements[i].tag = TAG_DH_PRIVATE;
        priv.elements[i].length = BN_num_bytes(priv_key);
+       bufs[i] = isc_mem_get(key->mctx, priv.elements[i].length);
        BN_bn2bin(priv_key, bufs[i]);
        priv.elements[i].data = bufs[i];
        i++;
 
        priv.elements[i].tag = TAG_DH_PUBLIC;
        priv.elements[i].length = BN_num_bytes(pub_key);
+       bufs[i] = isc_mem_get(key->mctx, priv.elements[i].length);
        BN_bn2bin(pub_key, bufs[i]);
        priv.elements[i].data = bufs[i];
        i++;
@@ -563,12 +1055,28 @@ openssldh_tofile(const dst_key_t *key, const char *directory) {
        priv.nelements = i;
        result = dst__privstruct_writefile(key, &priv, directory);
 
-       for (i = 0; i < 4; i++) {
-               if (bufs[i] == NULL) {
-                       break;
+       while (i--) {
+               if (bufs[i] != NULL) {
+                       isc_mem_put(key->mctx, bufs[i],
+                                   priv.elements[i].length);
                }
-               isc_mem_put(key->mctx, bufs[i], BN_num_bytes(p));
        }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+       if (p != NULL) {
+               BN_free(p);
+       }
+       if (g != NULL) {
+               BN_free(g);
+       }
+       if (pub_key != NULL) {
+               BN_free(pub_key);
+       }
+       if (priv_key != NULL) {
+               BN_clear_free(priv_key);
+       }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
        return (result);
 }
 
@@ -577,14 +1085,17 @@ openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
        dst_private_t priv;
        isc_result_t ret;
        int i;
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        DH *dh = NULL;
+#else
+       OSSL_PARAM_BLD *bld = NULL;
+       OSSL_PARAM *params = NULL;
+       EVP_PKEY_CTX *ctx = NULL;
+       EVP_PKEY *pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        BIGNUM *pub_key = NULL, *priv_key = NULL, *p = NULL, *g = NULL;
+       int key_size = 0;
        isc_mem_t *mctx;
-#define DST_RET(a)        \
-       {                 \
-               ret = a;  \
-               goto err; \
-       }
 
        UNUSED(pub);
        mctx = key->mctx;
@@ -599,12 +1110,24 @@ openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
                DST_RET(DST_R_EXTERNALKEY);
        }
 
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
        dh = DH_new();
        if (dh == NULL) {
                DST_RET(ISC_R_NOMEMORY);
        }
        DH_clear_flags(dh, DH_FLAG_CACHE_MONT_P);
        key->keydata.dh = dh;
+       dh = NULL;
+#else
+       bld = OSSL_PARAM_BLD_new();
+       if (bld == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+       ctx = EVP_PKEY_CTX_new_from_name(NULL, "DH", NULL);
+       if (ctx == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
 
        for (i = 0; i < priv.nelements; i++) {
                BIGNUM *bn;
@@ -617,6 +1140,7 @@ openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
                switch (priv.elements[i].tag) {
                case TAG_DH_PRIME:
                        p = bn;
+                       key_size = BN_num_bits(p);
                        break;
                case TAG_DH_GENERATOR:
                        g = bn;
@@ -629,14 +1153,75 @@ openssldh_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
                        break;
                }
        }
-       dst__privstruct_free(&priv, mctx);
-       DH_set0_key(dh, pub_key, priv_key);
-       DH_set0_pqg(dh, p, NULL, g);
 
-       key->key_size = BN_num_bits(p);
-       return (ISC_R_SUCCESS);
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       if (DH_set0_key(key->keydata.dh, pub_key, priv_key) != 1) {
+               DST_RET(dst__openssl_toresult2("DH_set0_key",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (DH_set0_pqg(key->keydata.dh, p, NULL, g) != 1) {
+               DST_RET(dst__openssl_toresult2("DH_set0_pqg",
+                                              DST_R_OPENSSLFAILURE));
+       }
+
+       /* These are now managed by OpenSSL */
+       pub_key = NULL;
+       priv_key = NULL;
+       p = NULL;
+       g = NULL;
+#else
+       if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key) !=
+                   1 ||
+           OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv_key) !=
+                   1 ||
+           OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p) != 1 ||
+           OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g) != 1)
+       {
+               DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       params = OSSL_PARAM_BLD_to_param(bld);
+       if (params == NULL) {
+               DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_fromdata_init(ctx) != 1) {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata_init",
+                                              DST_R_OPENSSLFAILURE));
+       }
+       if (EVP_PKEY_fromdata(ctx, &pkey, OSSL_KEYMGMT_SELECT_ALL, params) !=
+                   1 ||
+           pkey == NULL)
+       {
+               DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata",
+                                              DST_R_OPENSSLFAILURE));
+       }
+
+       key->keydata.pkey = pkey;
+       pkey = NULL;
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
+
+       key->key_size = (unsigned int)key_size;
+       ret = ISC_R_SUCCESS;
 
 err:
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+       if (dh != NULL) {
+               DH_free(dh);
+       }
+#else
+       if (pkey != NULL) {
+               EVP_PKEY_free(pkey);
+       }
+       if (ctx != NULL) {
+               EVP_PKEY_CTX_free(ctx);
+       }
+       if (params != NULL) {
+               OSSL_PARAM_free(params);
+       }
+       if (bld != NULL) {
+               OSSL_PARAM_BLD_free(bld);
+       }
+#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
        if (p != NULL) {
                BN_free(p);
        }
@@ -647,11 +1232,14 @@ err:
                BN_free(pub_key);
        }
        if (priv_key != NULL) {
-               BN_free(priv_key);
+               BN_clear_free(priv_key);
+       }
+       if (ret != ISC_R_SUCCESS) {
+               openssldh_destroy(key);
        }
-       openssldh_destroy(key);
        dst__privstruct_free(&priv, mctx);
        isc_safe_memwipe(&priv, sizeof(priv));
+
        return (ret);
 }