[openssl] master update

Richard Levitte levitte at openssl.org
Tue Feb 23 12:45:36 UTC 2021


The branch master has been updated
       via  f5b00834dd11d766b9232e89e40884db8f3cd7ec (commit)
       via  bbf4dc96fc4344e333d4e73bc2aba848e5bff84b (commit)
       via  13f91a7245d4271486c018b440940a696eaaa12d (commit)
       via  df4592cbec2321bccd23393328f53894a08bf403 (commit)
       via  5524580b5c0796d3bcab55c4e5378c6ece4df63b (commit)
       via  6fcd92d3d72540bddb738e2b037dda9a157cfc5c (commit)
       via  513731299398f4597aa575154a973654bbc2e0ef (commit)
       via  9a1c4e41e8d3fd8fe9d1bd8eeb8b1e1df21da37f (commit)
       via  4d4928edd0758753e43294816ae6095975a6e5fa (commit)
       via  e19246dc721a7a57c62d7dd39c70b6c87140b0ec (commit)
       via  6179dfc7c4bd850004c3b4b8220f3559573130d5 (commit)
      from  f627561cf5cc4963698bf975df8694543bcf826c (commit)


- Log -----------------------------------------------------------------
commit f5b00834dd11d766b9232e89e40884db8f3cd7ec
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Feb 10 19:00:05 2021 +0100

    EVP: Adapt the EC_KEY specific EVP_PKEY_CTX setter / getter functions
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit bbf4dc96fc4344e333d4e73bc2aba848e5bff84b
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Feb 10 18:58:01 2021 +0100

    EVP: Make checks in evp_pkey_ctx_store_cached_data() more restricted
    
    It would check the keytype and optype before determining if it even
    supported the ctrl command number.  This turned out to be disruptive,
    so we make it check that it supports the request ctrl command number
    first.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 13f91a7245d4271486c018b440940a696eaaa12d
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jan 25 15:38:32 2021 +0100

    EVP: Adapt the RSA specific EVP_PKEY_CTX setter / getter functions
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit df4592cbec2321bccd23393328f53894a08bf403
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jan 25 15:31:01 2021 +0100

    EVP: Adapt the DH specific EVP_PKEY_CTX setter / getter functions
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 5524580b5c0796d3bcab55c4e5378c6ece4df63b
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jan 20 23:13:45 2021 +0100

    EVP: Adapt the EVP_PKEY_CTX ctrl functions
    
    legacy_ctrl_to_param() and legacy_ctrl_str_to_param() are now
    replaced with calls to evp_pkey_ctx_ctrl_to_param() and
    evp_pkey_ctx_ctrl_str_to_param().
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 6fcd92d3d72540bddb738e2b037dda9a157cfc5c
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jan 20 23:10:48 2021 +0100

    EVP: Adapt diverse OSSL_PARAM setters and getters
    
    EVP_PKEY_get_group_name() now simply calls EVP_PKEY_get_utf8_string_param().
    EVP_PKEY_CTX_set_group_name() now simply calls EVP_PKEY_CTX_set_params().
    
    EVP_PKEY_get_bn_param(), EVP_PKEY_get_octet_string_param(),
    EVP_PKEY_get_utf8_string_param() and EVP_PKEY_get_int_param() can now
    handle legacy EVP_PKEYs by calling evp_pkey_get_params_to_ctrl().
    
    EVP_PKEY_CTX_get_params() can now handle a legacy backed EVP_PKEY_CTX
    by calling evp_pkey_ctx_get_params_to_ctrl().
    
    Note: EVP_PKEY_CTX_set_params() doesn't call the translator yet.
          Should it ever?
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 513731299398f4597aa575154a973654bbc2e0ef
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Feb 10 16:56:57 2021 +0100

    EVP: Make evp_pkey_ctx_{set,get}_params_strict() legacy aware
    
    In the interest of calling these functions on legacy EVP_PKEY
    contexts, only check the settable / gettable params for provider side
    keys, leaving to the translated EVP_PKEY_CTX_ctrl() call check the
    ctrl commands on its own.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 9a1c4e41e8d3fd8fe9d1bd8eeb8b1e1df21da37f
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Jan 20 23:04:53 2021 +0100

    EVP: Implement data-driven translation between known ctrl and OSSL_PARAMs
    
    The idea is to make it as transparent as possible to call things like
    EVP_PKEY_CTX_ctrl() with a provider backed EVP_PKEY_CTX, or things
    like EVP_PKEY_get_bn_param() with a legacy EVP_PKEY.
    
    All these sorts of calls demand that we translate between ctrl
    commands and OSSL_PARAM keys, and treat the arguments appropriately.
    
    This implementation has it being as data driven as possible, thereby
    centralizing everything into one table of translation data, which
    supports both directions.
    
    Fixes #13528
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 4d4928edd0758753e43294816ae6095975a6e5fa
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Feb 8 17:25:41 2021 +0100

    EVP: make evp_pkey_is_assigned() usable in the FIPS module
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit e19246dc721a7a57c62d7dd39c70b6c87140b0ec
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Jan 25 15:24:46 2021 +0100

    EVP: Make evp_pkey_ctx_state() available to all of EVP
    
    This will help with transitioning diverse functions to be able to use the
    ctrl<->OSSL_PARAM translators.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

commit 6179dfc7c4bd850004c3b4b8220f3559573130d5
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Feb 10 16:55:19 2021 +0100

    EVP: Implement EVP_PKEY_CTX_is_a()
    
    This does what was previously done by looking at pctx->pmeth->pkey_id,
    but handles both legacy and provider side contexts, and is supposed to
    become a replacement for the old way.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/13913)

-----------------------------------------------------------------------

Summary of changes:
 crypto/evp/build.info              |    2 +-
 crypto/evp/ctrl_params_translate.c | 2689 ++++++++++++++++++++++++++++++++++++
 crypto/evp/dh_ctrl.c               |  386 ++----
 crypto/evp/ec_ctrl.c               |  377 ++---
 crypto/evp/evp_lib.c               |   30 +-
 crypto/evp/p_lib.c                 |   99 +-
 crypto/evp/pmeth_lib.c             |  543 ++------
 crypto/rsa/rsa_lib.c               |  671 +++------
 doc/man3/EVP_PKEY_CTX_new.pod      |    8 +-
 include/crypto/evp.h               |   32 +-
 include/openssl/evp.h              |    1 +
 util/libcrypto.num                 |    1 +
 12 files changed, 3288 insertions(+), 1551 deletions(-)
 create mode 100644 crypto/evp/ctrl_params_translate.c

diff --git a/crypto/evp/build.info b/crypto/evp/build.info
index 4b3057873f..34551df4a3 100644
--- a/crypto/evp/build.info
+++ b/crypto/evp/build.info
@@ -15,7 +15,7 @@ SOURCE[../../libcrypto]=$COMMON\
         evp_pkey.c evp_pbe.c p5_crpt.c p5_crpt2.c pbe_scrypt.c \
         e_aes_cbc_hmac_sha1.c e_aes_cbc_hmac_sha256.c e_rc4_hmac_md5.c \
         e_chacha20_poly1305.c \
-        legacy_sha.c
+        legacy_sha.c ctrl_params_translate.c
 
 # Diverse type specific ctrl functions.  They are kinda sorta legacy, kinda
 # sorta not.
diff --git a/crypto/evp/ctrl_params_translate.c b/crypto/evp/ctrl_params_translate.c
new file mode 100644
index 0000000000..1e7001809b
--- /dev/null
+++ b/crypto/evp/ctrl_params_translate.c
@@ -0,0 +1,2689 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Some ctrls depend on deprecated functionality.  We trust that this is
+ * functionality that remains internally even when 'no-deprecated' is
+ * configured.  When we drop #legacy EVP_PKEYs, this source should be
+ * possible to drop as well.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+/* The following includes get us all the EVP_PKEY_CTRL macros */
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/rsa.h>
+#include <openssl/kdf.h>
+
+/* This include gets us all the OSSL_PARAM key string macros */
+#include <openssl/core_names.h>
+
+#include <openssl/err.h>
+#include <openssl/evperr.h>
+#include <openssl/params.h>
+#include "internal/nelem.h"
+#include "internal/cryptlib.h"
+#include "internal/ffc.h"
+#include "crypto/evp.h"
+#include "crypto/dh.h"
+#include "crypto/ec.h"
+
+#include "e_os.h"                /* strcasecmp() for Windows */
+
+struct translation_ctx_st;       /* Forwarding */
+struct translation_st;           /* Forwarding */
+
+/*
+ * The fixup_args functions are called with the following parameters:
+ *
+ * |state|              The state we're called in, explained further at the
+ *                      end of this comment.
+ * |translation|        The translation item, to be pilfered for data as
+ *                      necessary.
+ * |ctx|                The translation context, which contains copies of
+ *                      the following arguments, applicable according to
+ *                      the caller.  All of the attributes in this context
+ *                      may be freely modified by the fixup_args function.
+ *                      For cleanup, call cleanup_translation_ctx().
+ *
+ * The |state| tells the fixup_args function something about the caller and
+ * what they may expect:
+ *
+ * PKEY                         The fixup_args function has been called
+ *                              from an EVP_PKEY payload getter / setter,
+ *                              and is fully responsible for getting or
+ *                              setting the requested data.  With this
+ *                              state, the fixup_args function is expected
+ *                              to use or modify |*params|, depending on
+ *                              |action_type|.
+ *
+ * PRE_CTRL_TO_PARAMS           The fixup_args function has been called
+ * POST_CTRL_TO_PARAMS          from EVP_PKEY_CTX_ctrl(), to help with
+ *                              translating the ctrl data to an OSSL_PARAM
+ *                              element or back.  The calling sequence is
+ *                              as follows:
+ *
+ *                              1. fixup_args(PRE_CTRL_TO_PARAMS, ...)
+ *                              2. EVP_PKEY_CTX_set_params() or
+ *                                 EVP_PKEY_CTX_get_params()
+ *                              3. fixup_args(POST_CTRL_TO_PARAMS, ...)
+ *
+ *                              With the PRE_CTRL_TO_PARAMS state, the
+ *                              fixup_args function is expected to modify
+ *                              the passed |*params| in whatever way
+ *                              necessary, when |action_type == SET|.
+ *                              With the POST_CTRL_TO_PARAMS state, the
+ *                              fixup_args function is expected to modify
+ *                              the passed |p2| in whatever way necessary,
+ *                              when |action_type == GET|.
+ *
+ *                              The return value from the fixup_args call
+ *                              with the POST_CTRL_TO_PARAMS state becomes
+ *                              the return value back to EVP_PKEY_CTX_ctrl().
+ *
+ * CLEANUP_CTRL_TO_PARAMS       The cleanup_args functions has been called
+ *                              from EVP_PKEY_CTX_ctrl(), to clean up what
+ *                              the fixup_args function has done, if needed.
+ *
+ *
+ * PRE_CTRL_STR_TO_PARAMS       The fixup_args function has been called
+ * POST_CTRL_STR_TO_PARAMS      from EVP_PKEY_CTX_ctrl_str(), to help with
+ *                              translating the ctrl_str data to an
+ *                              OSSL_PARAM element or back.  The calling
+ *                              sequence is as follows:
+ *
+ *                              1. fixup_args(PRE_CTRL_STR_TO_PARAMS, ...)
+ *                              2. EVP_PKEY_CTX_set_params() or
+ *                                 EVP_PKEY_CTX_get_params()
+ *                              3. fixup_args(POST_CTRL_STR_TO_PARAMS, ...)
+ *
+ *                              With the PRE_CTRL_STR_TO_PARAMS state,
+ *                              the fixup_args function is expected to
+ *                              modify the passed |*params| in whatever
+ *                              way necessary, when |action_type == SET|.
+ *                              With the POST_CTRL_STR_TO_PARAMS state,
+ *                              the fixup_args function is only expected
+ *                              to return a value.
+ *
+ * CLEANUP_CTRL_STR_TO_PARAMS   The cleanup_args functions has been called
+ *                              from EVP_PKEY_CTX_ctrl_str(), to clean up
+ *                              what the fixup_args function has done, if
+ *                              needed.
+ *
+ * PRE_PARAMS_TO_CTRL           The fixup_args function has been called
+ * POST_PARAMS_TO_CTRL          from EVP_PKEY_CTX_get_params() or
+ *                              EVP_PKEY_CTX_set_params(), to help with
+ *                              translating the OSSL_PARAM data to the
+ *                              corresponding EVP_PKEY_CTX_ctrl() arguments
+ *                              or the other way around.  The calling
+ *                              sequence is as follows:
+ *
+ *                              1. fixup_args(PRE_PARAMS_TO_CTRL, ...)
+ *                              2. EVP_PKEY_CTX_ctrl()
+ *                              3. fixup_args(POST_PARAMS_TO_CTRL, ...)
+ *
+ *                              With the PRE_PARAMS_TO_CTRL state, the
+ *                              fixup_args function is expected to modify
+ *                              the passed |p1| and |p2| in whatever way
+ *                              necessary, when |action_type == SET|.
+ *                              With the POST_PARAMS_TO_CTRL state, the
+ *                              fixup_args function is expected to
+ *                              modify the passed |*params| in whatever
+ *                              way necessary, when |action_type == GET|.
+ *
+ * CLEANUP_PARAMS_TO_CTRL       The cleanup_args functions has been called
+ *                              from EVP_PKEY_CTX_get_params() or
+ *                              EVP_PKEY_CTX_set_params(), to clean up what
+ *                              the fixup_args function has done, if needed.
+ */
+enum state {
+    PKEY,
+    PRE_CTRL_TO_PARAMS, POST_CTRL_TO_PARAMS, CLEANUP_CTRL_TO_PARAMS,
+    PRE_CTRL_STR_TO_PARAMS, POST_CTRL_STR_TO_PARAMS, CLEANUP_CTRL_STR_TO_PARAMS,
+    PRE_PARAMS_TO_CTRL, POST_PARAMS_TO_CTRL, CLEANUP_PARAMS_TO_CTRL,
+};
+enum action {
+    NONE = 0, GET = 1, SET = 2
+};
+typedef int fixup_args_fn(enum state state,
+                          const struct translation_st *translation,
+                          struct translation_ctx_st *ctx);
+typedef int cleanup_args_fn(enum state state,
+                            const struct translation_st *translation,
+                            struct translation_ctx_st *ctx);
+
+struct translation_ctx_st {
+    /*
+     * The EVP_PKEY_CTX, for calls on that structure, to be pilfered for data
+     * as necessary.
+     */
+    EVP_PKEY_CTX *pctx;
+    /*
+     * The action type (GET or SET).  This may be 0 in some cases, and should
+     * be modified by the fixup_args function in the PRE states.  It should
+     * otherwise remain untouched once set.
+     */
+    enum action action_type;
+    /*
+     * For ctrl to params translation, the actual ctrl command number used.
+     * For params to ctrl translation, 0.
+     */
+    int ctrl_cmd;
+    /*
+     * For ctrl_str to params translation, the actual ctrl command string
+     * used.  In this case, the (string) value is always passed as |p2|.
+     * For params to ctrl translation, this is NULL.  Along with it is also
+     * and indicator whether it matched |ctrl_str| or |ctrl_hexstr| in the
+     * translation item.
+     */
+    const char *ctrl_str;
+    int ishex;
+    /* the ctrl-style int argument. */
+    int p1;
+    /* the ctrl-style void* argument. */
+    void *p2;
+    /* a size, for passing back the |p2| size where applicable */
+    size_t sz;
+    /* pointer to the OSSL_PARAM-style params array. */
+    OSSL_PARAM *params;
+
+    /*-
+     * The following are used entirely internally by the fixup_args functions
+     * and should not be touched by the callers, at all.
+     */
+
+    /*
+     * Copy of the ctrl-style void* argument, if the the fixup_args function
+     * needs to manipulate |p2| but wants to remember original.
+     */
+    void *orig_p2;
+    /* Diverse types of storage for the needy. */
+    char name_buf[OSSL_MAX_NAME_SIZE];
+    void *allocated_buf;
+    void *bufp;
+    size_t buflen;
+};
+
+struct translation_st {
+    /*-
+     * What this table item does.
+     *
+     * If the item has this set to 0, it means that both GET and SET are
+     * supported, and |fixup_args| will determine which it is.  This is to
+     * support translations of ctrls where the action type depends on the
+     * value of |p1| or |p2| (ctrls are really bi-directional, but are
+     * seldom used that way).
+     *
+     * This can be also used in the lookup template when it looks up by
+     * OSSL_PARAM key, to indicate if a setter or a getter called.
+     */
+    enum action action_type;
+
+    /*-
+     * Conditions, for params->ctrl translations.
+     *
+     * In table item, |keytype1| and |keytype2| can be set to -1 to indicate
+     * that this item supports all key types (or rather, that |fixup_args|
+     * will check and return an error if it's not supported).
+     * Any of these may be set to 0 to indicate that they are unset.
+     */
+    int keytype1;    /* The EVP_PKEY_XXX type, i.e. NIDs. #legacy */
+    int keytype2;    /* Another EVP_PKEY_XXX type, used for aliases */
+    int optype;      /* The operation type */
+
+    /*
+     * Lookup and translation attributes
+     *
+     * |ctrl_num|, |ctrl_str|, |ctrl_hexstr| and |param_key| are lookup
+     * attributes.
+     *
+     * |ctrl_num| may be 0 or that |param_key| may be NULL in the table item,
+     * but not at the same time.  If they are, they are simply not used for
+     * lookup.
+     * When |ctrl_num| == 0, no ctrl will be called.  Likewise, when
+     * |param_key| == NULL, no OSSL_PARAM setter/getter will be called.
+     * In that case the treatment of the translation item relies entirely on
+     * |fixup_args|, which is then assumed to have side effects.
+     *
+     * As a special case, it's possible to set |ctrl_hexstr| and assign NULL
+     * to |ctrl_str|.  That will signal to default_fixup_args() that the
+     * value must always be interpreted as hex.
+     */
+    int ctrl_num;            /* EVP_PKEY_CTRL_xxx */
+    const char *ctrl_str;    /* The corresponding ctrl string */
+    const char *ctrl_hexstr; /* The alternative "hex{str}" ctrl string */
+    const char *param_key;   /* The corresponding OSSL_PARAM key */
+    /*
+     * The appropriate OSSL_PARAM data type.  This may be 0 to indicate that
+     * this OSSL_PARAM may have more than one data type, depending on input
+     * material.  In this case, |fixup_args| is expected to check and handle
+     * it.
+     */
+    unsigned int param_data_type;
+
+    /*
+     * Fixer functions
+     *
+     * |fixup_args| is always called before (for SET) or after (for GET)
+     * the actual ctrl / OSSL_PARAM function.
+     */
+    fixup_args_fn *fixup_args;
+};
+
+/*-
+ * Fixer function implementations
+ * ==============================
+ */
+
+/*
+ * default_check isn't a fixer per se, but rather a helper function to
+ * perform certain standard checks.
+ */
+static int default_check(enum state state,
+                         const struct translation_st *translation,
+                         const struct translation_ctx_st *ctx)
+{
+    switch (state) {
+    default:
+        break;
+    case PRE_CTRL_TO_PARAMS:
+        if (!ossl_assert(translation != NULL)) {
+            ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+            return -2;
+        }
+        if (!ossl_assert(translation->param_key != 0)
+            || !ossl_assert(translation->param_data_type != 0)) {
+            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
+        break;
+    case PRE_CTRL_STR_TO_PARAMS:
+        /*
+         * For ctrl_str to params translation, we allow direct use of
+         * OSSL_PARAM keys as ctrl_str keys.  Therefore, it's possible that
+         * we end up with |translation == NULL|, which is fine.  The fixup
+         * function will have to deal with it carefully.
+         */
+        if (translation != NULL) {
+            if (!ossl_assert(translation->action_type != GET)) {
+                ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+                return -2;
+            }
+            if (!ossl_assert(translation->param_key != NULL)
+                || !ossl_assert(translation->param_data_type != 0)) {
+                ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+                return 0;
+            }
+        }
+        break;
+    case PRE_PARAMS_TO_CTRL:
+    case POST_PARAMS_TO_CTRL:
+        if (!ossl_assert(translation != NULL)) {
+            ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+            return -2;
+        }
+        if (!ossl_assert(translation->ctrl_num != 0)
+            || !ossl_assert(translation->param_data_type != 0)) {
+            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
+    }
+
+    /* Nothing else to check */
+    return 1;
+}
+
+/*-
+ * default_fixup_args fixes up all sorts of arguments, governed by the
+ * diverse attributes in the translation item.  It covers all "standard"
+ * base ctrl functionality, meaning it can handle basic conversion of
+ * data between p1+p2 (SET) or return value+p2 (GET) as long as the values
+ * don't have extra semantics (such as NIDs, OIDs, that sort of stuff).
+ * Extra semantics must be handled via specific fixup_args functions.
+ *
+ * The following states and action type combinations have standard handling
+ * done in this function:
+ *
+ * PRE_CTRL_TO_PARAMS, 0                - ERROR.  action type must be
+ *                                        determined by a fixup function.
+ * PRE_CTRL_TO_PARAMS, SET | GET        - |p1| and |p2| are converted to an
+ *                                        OSSL_PARAM according to the data
+ *                                        type given in |translattion|.
+ *                                        For OSSL_PARAM_UNSIGNED_INTEGER,
+ *                                        a BIGNUM passed as |p2| is accepted.
+ * POST_CTRL_TO_PARAMS, GET             - If the OSSL_PARAM data type is a
+ *                                        STRING or PTR type, |p1| is set
+ *                                        to the OSSL_PARAM return size, and
+ *                                        |p2| is set to the string.
+ * PRE_CTRL_STR_TO_PARAMS, !SET         - ERROR.  That combination is not
+ *                                        supported.
+ * PRE_CTRL_STR_TO_PARAMS, SET          - |p2| is taken as a string, and is
+ *                                        converted to an OSSL_PARAM in a
+ *                                        standard manner, guided by the
+ *                                        param key and data type from
+ *                                        |translation|.
+ * PRE_PARAMS_TO_CTRL, SET              - the OSSL_PARAM is converted to
+ *                                        |p1| and |p2| according to the
+ *                                        data type given in |translation|
+ *                                        For OSSL_PARAM_UNSIGNED_INTEGER,
+ *                                        if |p2| is non-NULL, then |*p2|
+ *                                        is assigned a BIGNUM, otherwise
+ *                                        |p1| is assigned an unsigned int.
+ * POST_PARAMS_TO_CTRL, GET             - |p1| and |p2| are converted to
+ *                                        an OSSL_PARAM, in the same manner
+ *                                        as for the combination of
+ *                                        PRE_CTRL_TO_PARAMS, SET.
+ */
+static int default_fixup_args(enum state state,
+                              const struct translation_st *translation,
+                              struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) < 0)
+        return ret;
+
+    switch (state) {
+    default:
+        /* For states this function should never have been called with */
+        ERR_raise_data(ERR_LIB_EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED,
+                       "[action:%d, state:%d]", ctx->action_type, state);
+        return 0;
+
+    /*
+     * PRE_CTRL_TO_PARAMS and POST_CTRL_TO_PARAMS handle ctrl to params
+     * translations.  PRE_CTRL_TO_PARAMS is responsible for preparing
+     * |*params|, and POST_CTRL_TO_PARAMS is responsible for bringing the
+     * result back to |*p2| and the return value.
+     */
+    case PRE_CTRL_TO_PARAMS:
+        /* This is ctrl to params translation, so we need an OSSL_PARAM key */
+        if (ctx->action_type == NONE) {
+            /*
+             * No action type is an error here.  That's a case for a
+             * special fixup function.
+             */
+            ERR_raise_data(ERR_LIB_EVP, ERR_R_UNSUPPORTED,
+                           "[action:%d, state:%d]", ctx->action_type, state);
+            return 0;
+        }
+
+        if (translation->optype != 0) {
+            if ((EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx->pctx)
+                 && ctx->pctx->op.sig.sigprovctx == NULL)
+                || (EVP_PKEY_CTX_IS_DERIVE_OP(ctx->pctx)
+                    && ctx->pctx->op.kex.exchprovctx == NULL)
+                || (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx->pctx)
+                    && ctx->pctx->op.ciph.ciphprovctx == NULL)
+                || (EVP_PKEY_CTX_IS_KEM_OP(ctx->pctx)
+                    && ctx->pctx->op.encap.kemprovctx == NULL)
+                /*
+                 * The following may be unnecessary, but we have them
+                 * for good measure...
+                 */
+                || (EVP_PKEY_CTX_IS_GEN_OP(ctx->pctx)
+                    && ctx->pctx->op.keymgmt.genctx == NULL)
+                || (EVP_PKEY_CTX_IS_FROMDATA_OP(ctx->pctx)
+                    && ctx->pctx->op.keymgmt.genctx == NULL)) {
+                ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+                /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+                return -2;
+            }
+        }
+
+        /*
+         * OSSL_PARAM_construct_TYPE() works equally well for both SET and GET.
+         */
+        switch (translation->param_data_type) {
+        case OSSL_PARAM_INTEGER:
+            *ctx->params = OSSL_PARAM_construct_int(translation->param_key,
+                                                    &ctx->p1);
+            break;
+        case OSSL_PARAM_UNSIGNED_INTEGER:
+            /*
+             * BIGNUMs are passed via |p2|.  For all ctrl's that just want
+             * to pass a simple integer via |p1|, |p2| is expected to be
+             * NULL.
+             *
+             * Note that this allocates a buffer, which the cleanup function
+             * must deallocate.
+             */
+            if (ctx->p2 != NULL) {
+                if (ctx->action_type == SET) {
+                    ctx->buflen = BN_num_bytes(ctx->p2);
+                    if ((ctx->allocated_buf =
+                         OPENSSL_malloc(ctx->buflen)) == NULL) {
+                        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+                        return 0;
+                    }
+                    if (!BN_bn2nativepad(ctx->p2,
+                                         ctx->allocated_buf, ctx->buflen)) {
+                        OPENSSL_free(ctx->allocated_buf);
+                        ctx->allocated_buf = NULL;
+                        return 0;
+                    }
+                    *ctx->params =
+                        OSSL_PARAM_construct_BN(translation->param_key,
+                                                ctx->allocated_buf,
+                                                ctx->buflen);
+                } else {
+                    /*
+                     * No support for getting a BIGNUM by ctrl, this needs
+                     * fixup_args function support.
+                     */
+                    ERR_raise_data(ERR_LIB_EVP, ERR_R_UNSUPPORTED,
+                                   "[action:%d, state:%d] trying to get a "
+                                   "BIGNUM via ctrl call",
+                                   ctx->action_type, state);
+                    return 0;
+                }
+            } else {
+                *ctx->params =
+                    OSSL_PARAM_construct_uint(translation->param_key,
+                                              (unsigned int *)&ctx->p1);
+            }
+            break;
+        case OSSL_PARAM_UTF8_STRING:
+            *ctx->params =
+                OSSL_PARAM_construct_utf8_string(translation->param_key,
+                                                 ctx->p2, (size_t)ctx->p1);
+            break;
+        case OSSL_PARAM_UTF8_PTR:
+            *ctx->params =
+                OSSL_PARAM_construct_utf8_ptr(translation->param_key,
+                                              ctx->p2, (size_t)ctx->p1);
+            break;
+        case OSSL_PARAM_OCTET_STRING:
+            *ctx->params =
+                OSSL_PARAM_construct_octet_string(translation->param_key,
+                                                  ctx->p2, (size_t)ctx->p1);
+            break;
+        case OSSL_PARAM_OCTET_PTR:
+            *ctx->params =
+                OSSL_PARAM_construct_octet_ptr(translation->param_key,
+                                               ctx->p2, (size_t)ctx->p1);
+            break;
+        }
+        break;
+    case POST_CTRL_TO_PARAMS:
+        /*
+         * Because EVP_PKEY_CTX_ctrl() returns the length of certain objects
+         * as its return value, we need to ensure that we do it here as well,
+         * for the OSSL_PARAM data types where this makes sense.
+         */
+        if (ctx->action_type == GET) {
+            switch (translation->param_data_type) {
+            case OSSL_PARAM_UTF8_STRING:
+            case OSSL_PARAM_UTF8_PTR:
+            case OSSL_PARAM_OCTET_STRING:
+            case OSSL_PARAM_OCTET_PTR:
+                ctx->p1 = (int)ctx->params[0].return_size;
+                break;
+            }
+        }
+        break;
+
+    /*
+     * PRE_CTRL_STR_TO_PARAMS and POST_CTRL_STR_TO_PARAMS handle ctrl_str to
+     * params translations.  PRE_CTRL_TO_PARAMS is responsible for preparing
+     * |*params|, and POST_CTRL_TO_PARAMS currently has nothing to do, since
+     * there's no support for getting data via ctrl_str calls.
+     */
+    case PRE_CTRL_STR_TO_PARAMS:
+        {
+            /* This is ctrl_str to params translation */
+            const char *tmp_ctrl_str = ctx->ctrl_str;
+            const char *orig_ctrl_str = ctx->ctrl_str;
+            const char *orig_value = ctx->p2;
+            const OSSL_PARAM *settable = NULL;
+            int exists = 0;
+
+            /* Only setting is supported here */
+            if (ctx->action_type != SET) {
+                ERR_raise_data(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED,
+                                   "[action:%d, state:%d] only setting allowed",
+                                   ctx->action_type, state);
+                return 0;
+            }
+
+            /*
+             * If no translation exists, we simply pass the control string
+             * unmodified.
+             */
+            if (translation != NULL) {
+                tmp_ctrl_str = ctx->ctrl_str = translation->param_key;
+
+                if (ctx->ishex) {
+                    strcpy(ctx->name_buf, "hex");
+                    if (OPENSSL_strlcat(ctx->name_buf, tmp_ctrl_str,
+                                        sizeof(ctx->name_buf)) <= 3) {
+                        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+                        return -1;
+                    }
+                    tmp_ctrl_str = ctx->name_buf;
+                }
+            }
+
+            settable = EVP_PKEY_CTX_settable_params(ctx->pctx);
+            if (!OSSL_PARAM_allocate_from_text(ctx->params, settable,
+                                               tmp_ctrl_str,
+                                               ctx->p2, strlen(ctx->p2),
+                                               &exists)) {
+                if (!exists) {
+                    ERR_raise_data(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED,
+                                   "[action:%d, state:%d] name=%s, value=%s",
+                                   ctx->action_type, state,
+                                   orig_ctrl_str, orig_value);
+                    return -2;
+                }
+                return 0;
+            }
+            ctx->allocated_buf = ctx->params->data;
+            ctx->buflen = ctx->params->data_size;
+        }
+        break;
+    case POST_CTRL_STR_TO_PARAMS:
+        /* Nothing to be done */
+        break;
+
+    /*
+     * PRE_PARAMS_TO_CTRL and POST_PARAMS_TO_CTRL handle params to ctrl
+     * translations.  PRE_PARAMS_TO_CTRL is responsible for preparing
+     * |p1| and |p2|, and POST_PARAMS_TO_CTRL is responsible for bringing
+     * the EVP_PKEY_CTX_ctrl() return value (passed as |p1|) and |p2| back
+     * to |*params|.
+     *
+     * PKEY is treated just like POST_PARAMS_TO_CTRL, making it easy
+     * for the related fixup_args functions to just set |p1| and |p2|
+     * appropriately and leave it to this section of code to fix up
+     * |ctx->params| accordingly.
+     */
+    case PKEY:
+    case POST_PARAMS_TO_CTRL:
+        ret = ctx->p1;
+        /* FALLTHRU */
+    case PRE_PARAMS_TO_CTRL:
+        {
+            /* This is params to ctrl translation */
+            if (state == PRE_PARAMS_TO_CTRL && ctx->action_type == SET) {
+                /* For the PRE state, only setting needs some work to be done */
+
+                /* When setting, we populate |p1| and |p2| from |*params| */
+                switch (translation->param_data_type) {
+                case OSSL_PARAM_INTEGER:
+                    return OSSL_PARAM_get_int(ctx->params, &ctx->p1);
+                case OSSL_PARAM_UNSIGNED_INTEGER:
+                    if (ctx->p2 != NULL) {
+                        /* BIGNUM passed down with p2 */
+                        if (!OSSL_PARAM_get_BN(ctx->params, ctx->p2))
+                            return 0;
+                    } else {
+                        /* Normal C unsigned int passed down */
+                        if (!OSSL_PARAM_get_uint(ctx->params,
+                                                 (unsigned int *)&ctx->p1))
+                            return 0;
+                    }
+                    return 1;
+                case OSSL_PARAM_UTF8_STRING:
+                    return OSSL_PARAM_get_utf8_string(ctx->params,
+                                                      ctx->p2, ctx->sz);
+                case OSSL_PARAM_OCTET_STRING:
+                    return OSSL_PARAM_get_octet_string(ctx->params,
+                                                       ctx->p2, ctx->sz,
+                                                       &ctx->sz);
+                case OSSL_PARAM_OCTET_PTR:
+                    return OSSL_PARAM_get_octet_ptr(ctx->params,
+                                                    ctx->p2, &ctx->sz);
+                default:
+                    ERR_raise_data(ERR_LIB_EVP, ERR_R_UNSUPPORTED,
+                                   "[action:%d, state:%d] "
+                                   "unknown OSSL_PARAM data type %d",
+                                   ctx->action_type, state,
+                                   translation->param_data_type);
+                    return 0;
+                }
+            } else if ((state == POST_PARAMS_TO_CTRL || state == PKEY)
+                       && ctx->action_type == GET) {
+                /* For the POST state, only getting needs some work to be done */
+
+                /* When getting, we populate |*params| from |p1| and |p2| */
+                switch (translation->param_data_type) {
+                case OSSL_PARAM_INTEGER:
+                    return OSSL_PARAM_set_int(ctx->params, ctx->p1);
+                case OSSL_PARAM_UNSIGNED_INTEGER:
+                    if (ctx->p2 != NULL) {
+                        /* BIGNUM passed back */
+                        return OSSL_PARAM_set_BN(ctx->params, ctx->p2);
+                    } else {
+                        /* Normal C unsigned int passed back */
+                        return OSSL_PARAM_set_uint(ctx->params,
+                                                   (unsigned int)ctx->p1);
+                    }
+                    return 0;
+                case OSSL_PARAM_UTF8_STRING:
+                    return OSSL_PARAM_set_utf8_string(ctx->params, ctx->p2);
+                case OSSL_PARAM_OCTET_STRING:
+                    return OSSL_PARAM_set_octet_string(ctx->params, ctx->p2,
+                                                       (size_t)ctx->p1);
+                case OSSL_PARAM_OCTET_PTR:
+                    return OSSL_PARAM_set_octet_ptr(ctx->params, ctx->p2,
+                                                    (size_t)ctx->p1);
+                default:
+                    ERR_raise_data(ERR_LIB_EVP, ERR_R_UNSUPPORTED,
+                                   "[action:%d, state:%d] "
+                                   "unsupported OSSL_PARAM data type %d",
+                                   ctx->action_type, state,
+                                   translation->param_data_type);
+                    return 0;
+                }
+            }
+        }
+        /* Any other combination is simply pass-through */
+        break;
+    }
+    return ret;
+}
+
+static int
+cleanup_translation_ctx(enum state state,
+                        const struct translation_st *translation,
+                        struct translation_ctx_st *ctx)
+{
+    if (ctx->allocated_buf != NULL)
+        OPENSSL_free(ctx->allocated_buf);
+    ctx->allocated_buf = NULL;
+    return 1;
+}
+
+/*
+ * fix_cipher_md fixes up an EVP_CIPHER / EVP_MD to its name on SET,
+ * and cipher / md name to EVP_MD on GET.
+ */
+static const char *get_cipher_name(void *cipher)
+{
+    return EVP_CIPHER_name(cipher);
+}
+
+static const char *get_md_name(void *md)
+{
+    return EVP_MD_name(md);
+}
+
+static const void *get_cipher_by_name(OSSL_LIB_CTX *libctx, const char *name)
+{
+    return evp_get_cipherbyname_ex(libctx, name);
+}
+
+static const void *get_md_by_name(OSSL_LIB_CTX *libctx, const char *name)
+{
+    return evp_get_digestbyname_ex(libctx, name);
+}
+
+static int fix_cipher_md(enum state state,
+                         const struct translation_st *translation,
+                         struct translation_ctx_st *ctx,
+                         const char *(*get_name)(void *algo),
+                         const void *(*get_algo_by_name)(OSSL_LIB_CTX *libctx,
+                                                         const char *name))
+{
+    int ret = 1;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_CTRL_TO_PARAMS && ctx->action_type == GET) {
+        /*
+         * |ctx->p2| contains the address to an EVP_CIPHER or EVP_MD pointer
+         * to be filled in.  We need to remember it, then make |ctx->p2|
+         * point at a buffer to be filled in with the name, and |ctx->p1|
+         * with its size.  default_fixup_args() will take care of the rest
+         * for us.
+         */
+        ctx->orig_p2 = ctx->p2;
+        ctx->p2 = ctx->name_buf;
+        ctx->p1 = sizeof(ctx->name_buf);
+    } else if (state == PRE_CTRL_TO_PARAMS && ctx->action_type == SET) {
+        /*
+         * In different parts of OpenSSL, this ctrl command is used
+         * differently.  Some calls pass a NID as p1, others pass an
+         * EVP_CIPHER pointer as p2...
+         */
+        ctx->p2 = (char *)(ctx->p2 == NULL
+                           ? OBJ_nid2sn(ctx->p1)
+                           : get_name(ctx->p2));
+        ctx->p1 = strlen(ctx->p2);
+    } else if (state == POST_PARAMS_TO_CTRL && ctx->action_type == GET) {
+        ctx->p2 = (ctx->p2 == NULL ? "" : (char *)get_name(ctx->p2));
+        ctx->p1 = strlen(ctx->p2);
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == POST_CTRL_TO_PARAMS && ctx->action_type == GET) {
+        /*
+         * Here's how we re-use |ctx->orig_p2| that was set in the
+         * PRE_CTRL_TO_PARAMS state above.
+         */
+        *(void **)ctx->orig_p2 =
+            (void *)get_algo_by_name(ctx->pctx->libctx, ctx->p2);
+        ctx->p1 = 1;
+    } else if (state == PRE_PARAMS_TO_CTRL && ctx->action_type == SET) {
+        ctx->p2 = (void *)get_algo_by_name(ctx->pctx->libctx, ctx->p2);
+        ctx->p1 = 0;
+    }
+
+    return ret;
+}
+
+static int fix_cipher(enum state state,
+                      const struct translation_st *translation,
+                      struct translation_ctx_st *ctx)
+{
+    return fix_cipher_md(state, translation, ctx,
+                         get_cipher_name, get_cipher_by_name);
+}
+
+static int fix_md(enum state state,
+                  const struct translation_st *translation,
+                  struct translation_ctx_st *ctx)
+{
+    return fix_cipher_md(state, translation, ctx,
+                         get_md_name, get_md_by_name);
+}
+
+static int fix_distid_len(enum state state,
+                          const struct translation_st *translation,
+                          struct translation_ctx_st *ctx)
+{
+    int ret = default_fixup_args(state, translation, ctx);
+
+    if (ret > 0) {
+        ret = 0;
+        if ((state == POST_CTRL_TO_PARAMS
+             || state == POST_CTRL_STR_TO_PARAMS) && ctx->action_type == GET) {
+            *(size_t *)ctx->p2 = ctx->sz;
+            ret = 1;
+        }
+    }
+    return ret;
+}
+
+struct kdf_type_map_st {
+    int kdf_type_num;
+    const char *kdf_type_str;
+};
+
+static int fix_kdf_type(enum state state,
+                        const struct translation_st *translation,
+                        struct translation_ctx_st *ctx,
+                        const struct kdf_type_map_st *kdf_type_map)
+{
+    /*
+     * The EVP_PKEY_CTRL_DH_KDF_TYPE ctrl command is a bit special, in
+     * that it's used both for setting a value, and for getting it, all
+     * depending on the value if |p1|; if |p1| is -2, the backend is
+     * supposed to place the current kdf type in |p2|, and if not, |p1|
+     * is interpreted as the new kdf type.
+     */
+    int ret = 0;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_CTRL_TO_PARAMS) {
+        /*
+         * In |translations|, the initial value for |ctx->action_type| must
+         * be NONE.
+         */
+        if (!ossl_assert(ctx->action_type == NONE))
+            return 0;
+
+        /* The action type depends on the value of *p1 */
+        if (ctx->p1 == -2) {
+            /*
+             * The OSSL_PARAMS getter needs space to store a copy of the kdf
+             * type string.  We use |ctx->name_buf|, which has enough space
+             * allocated.
+             *
+             * (this wouldn't be needed if the OSSL_xxx_PARAM_KDF_TYPE
+             * had the data type OSSL_PARAM_UTF8_PTR)
+             */
+            ctx->p2 = ctx->name_buf;
+            ctx->p1 = sizeof(ctx->name_buf);
+            ctx->action_type = GET;
+        } else {
+            ctx->action_type = SET;
+        }
+    }
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((state == PRE_CTRL_TO_PARAMS && ctx->action_type == SET)
+        || (state == POST_PARAMS_TO_CTRL && ctx->action_type == GET)) {
+        ret = -2;
+        /* Convert KDF type numbers to strings */
+        for (; kdf_type_map->kdf_type_str != NULL; kdf_type_map++)
+            if (ctx->p1 == kdf_type_map->kdf_type_num) {
+                ctx->p2 = (char *)kdf_type_map->kdf_type_str;
+                ret = 1;
+                break;
+            }
+        if (ret <= 0)
+            goto end;
+        ctx->p1 = strlen(ctx->p2);
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((state == POST_CTRL_TO_PARAMS && ctx->action_type == GET)
+        || (state == PRE_PARAMS_TO_CTRL && ctx->action_type == SET)) {
+        ctx->p1 = ret = -1;
+
+        /* Convert KDF type strings to numbers */
+        for (; kdf_type_map->kdf_type_str != NULL; kdf_type_map++)
+            if (strcmp(ctx->p2, kdf_type_map->kdf_type_str) == 0) {
+                ctx->p1 = kdf_type_map->kdf_type_num;
+                ret = 1;
+                break;
+            }
+        ctx->p2 = NULL;
+    } else if (state == PRE_PARAMS_TO_CTRL && ctx->action_type == GET) {
+        ctx->p1 = -2;
+    }
+ end:
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_DH_KDF_TYPE */
+static int fix_dh_kdf_type(enum state state,
+                           const struct translation_st *translation,
+                           struct translation_ctx_st *ctx)
+{
+    static const struct kdf_type_map_st kdf_type_map[] = {
+        { EVP_PKEY_DH_KDF_NONE, "" },
+        { EVP_PKEY_DH_KDF_X9_42, OSSL_KDF_NAME_X942KDF_ASN1 },
+        { 0, NULL }
+    };
+
+    return fix_kdf_type(state, translation, ctx, kdf_type_map);
+}
+
+/* EVP_PKEY_CTRL_EC_KDF_TYPE */
+static int fix_ec_kdf_type(enum state state,
+                           const struct translation_st *translation,
+                           struct translation_ctx_st *ctx)
+{
+    static const struct kdf_type_map_st kdf_type_map[] = {
+        { EVP_PKEY_ECDH_KDF_NONE, "" },
+        { EVP_PKEY_ECDH_KDF_X9_63, OSSL_KDF_NAME_X963KDF },
+        { 0, NULL }
+    };
+
+    return fix_kdf_type(state, translation, ctx, kdf_type_map);
+}
+
+/* EVP_PKEY_CTRL_DH_KDF_OID, EVP_PKEY_CTRL_GET_DH_KDF_OID, ...??? */
+static int fix_oid(enum state state,
+                   const struct translation_st *translation,
+                   struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((state == PRE_CTRL_TO_PARAMS && ctx->action_type == SET)
+        || (state == POST_PARAMS_TO_CTRL && ctx->action_type == GET)) {
+        /*
+         * We're translating from ctrl to params and setting the OID, or
+         * we're translating from params to ctrl and getting the OID.
+         * Either way, |ctx->p2| points at an ASN1_OBJECT, and needs to have
+         * that replaced with the corresponding name.
+         * default_fixup_args() will then be able to convert that to the
+         * corresponding OSSL_PARAM.
+         */
+        ctx->p2 = (char *)OBJ_nid2sn(OBJ_obj2nid(ctx->p2));
+        ctx->p1 = 0; /* let default_fixup_args() figure out the length */
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((state == PRE_PARAMS_TO_CTRL && ctx->action_type == SET)
+        || (state == POST_CTRL_TO_PARAMS && ctx->action_type == GET)) {
+        /*
+         * We're translating from ctrl to params and setting the OID name,
+         * or we're translating from params to ctrl and getting the OID
+         * name.  Either way, default_fixup_args() has placed the OID name
+         * in |ctx->p2|, all we need to do now is to replace that with the
+         * corresponding ASN1_OBJECT.
+         */
+        ctx->p2 = (ASN1_OBJECT *)OBJ_txt2obj(ctx->p2, 0);
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_DH_NID, ...??? */
+static int fix_dh_nid(enum state state,
+                      const struct translation_st *translation,
+                      struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    /* This is currently only settable */
+    if (ctx->action_type != SET)
+        return 0;
+
+    if (state == PRE_CTRL_TO_PARAMS) {
+        ctx->p2 = (char *)ossl_ffc_named_group_get_name
+            (ossl_ffc_uid_to_dh_named_group(ctx->p1));
+        ctx->p1 = 0;
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_PARAMS_TO_CTRL) {
+        ctx->p1 =
+            ossl_ffc_named_group_get_uid(ossl_ffc_name_to_dh_named_group(ctx->p2));
+        ctx->p2 = NULL;
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_DH_PARAMGEN_TYPE */
+static int fix_dh_paramgen_type(enum state state,
+                                const struct translation_st *translation,
+                                struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    /* This is currently only settable */
+    if (ctx->action_type != SET)
+        return 0;
+
+    if (state == PRE_CTRL_TO_PARAMS) {
+        ctx->p2 = (char *)dh_gen_type_id2name(ctx->p1);
+        ctx->p1 = 0;
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_PARAMS_TO_CTRL) {
+        ctx->p1 = dh_gen_type_name2id(ctx->p2);
+        ctx->p2 = NULL;
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_EC_PARAM_ENC */
+static int fix_ec_param_enc(enum state state,
+                            const struct translation_st *translation,
+                            struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    /* This is currently only settable */
+    if (ctx->action_type != SET)
+        return 0;
+
+    if (state == PRE_CTRL_TO_PARAMS) {
+        switch (ctx->p1) {
+        case OPENSSL_EC_EXPLICIT_CURVE:
+            ctx->p2 = OSSL_PKEY_EC_ENCODING_EXPLICIT;
+            break;
+        case OPENSSL_EC_NAMED_CURVE:
+            ctx->p2 = OSSL_PKEY_EC_ENCODING_GROUP;
+            break;
+        default:
+            ret = -2;
+            goto end;
+        }
+        ctx->p1 = 0;
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_PARAMS_TO_CTRL) {
+        if (strcmp(ctx->p2, OSSL_PKEY_EC_ENCODING_EXPLICIT) == 0)
+            ctx->p1 = OPENSSL_EC_EXPLICIT_CURVE;
+        else if (strcmp(ctx->p2, OSSL_PKEY_EC_ENCODING_GROUP) == 0)
+            ctx->p1 = OPENSSL_EC_NAMED_CURVE;
+        else
+            ctx->p1 = ret = -2;
+        ctx->p2 = NULL;
+    }
+
+ end:
+    if (ret == -2)
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID */
+static int fix_ec_paramgen_curve_nid(enum state state,
+                                     const struct translation_st *translation,
+                                     struct translation_ctx_st *ctx)
+{
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    /* This is currently only settable */
+    if (ctx->action_type != SET)
+        return 0;
+
+    if (state == PRE_CTRL_TO_PARAMS) {
+        ctx->p2 = (char *)OBJ_nid2sn(ctx->p1);
+        ctx->p1 = 0;
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_PARAMS_TO_CTRL) {
+        ctx->p1 = OBJ_sn2nid(ctx->p2);
+        ctx->p2 = NULL;
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_EC_ECDH_COFACTOR */
+static int fix_ecdh_cofactor(enum state state,
+                             const struct translation_st *translation,
+                             struct translation_ctx_st *ctx)
+{
+    /*
+     * The EVP_PKEY_CTRL_EC_ECDH_COFACTOR ctrl command is a bit special, in
+     * that it's used both for setting a value, and for getting it, all
+     * depending on the value if |ctx->p1|; if |ctx->p1| is -2, the backend is
+     * supposed to place the current cofactor mode in |ctx->p2|, and if not,
+     * |ctx->p1| is interpreted as the new cofactor mode.
+     */
+    int ret = 0;
+
+    if (state == PRE_CTRL_TO_PARAMS) {
+        /*
+         * The initial value for |ctx->action_type| must be zero.
+         * evp_pkey_ctrl_to_params() takes it from the translation item.
+         */
+        if (!ossl_assert(ctx->action_type == NONE))
+            return 0;
+
+        /* The action type depends on the value of ctx->p1 */
+        if (ctx->p1 == -2)
+            ctx->action_type = GET;
+        else
+            ctx->action_type = SET;
+    } else if (state == PRE_CTRL_STR_TO_PARAMS) {
+        ctx->action_type = SET;
+    } else if (state == PRE_PARAMS_TO_CTRL) {
+        /* The initial value for |ctx->action_type| must not be zero. */
+        if (!ossl_assert(ctx->action_type != NONE))
+            return 0;
+    }
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_CTRL_TO_PARAMS && ctx->action_type == SET) {
+        if (ctx->p1 < -1 || ctx->p1 > 1) {
+            /* Uses the same return value of pkey_ec_ctrl() */
+            return -2;
+        }
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == POST_CTRL_TO_PARAMS && ctx->action_type == GET) {
+        if (ctx->p1 < 0 || ctx->p1 > 1) {
+            /*
+             * The provider should return either 0 or 1, any other value is a
+             * provider error.
+             */
+            ctx->p1 = ret = -1;
+        }
+    } else if (state == PRE_PARAMS_TO_CTRL && ctx->action_type == GET) {
+        ctx->p1 = -2;
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_RSA_PADDING, EVP_PKEY_CTRL_GET_RSA_PADDING */
+static int fix_rsa_padding_mode(enum state state,
+                                const struct translation_st *translation,
+                                struct translation_ctx_st *ctx)
+{
+    static const OSSL_ITEM str_value_map[] = {
+        { RSA_PKCS1_PADDING,            "pkcs1"  },
+        { RSA_SSLV23_PADDING,           "sslv23" },
+        { RSA_NO_PADDING,               "none"   },
+        { RSA_PKCS1_OAEP_PADDING,       "oaep"   },
+        { RSA_PKCS1_OAEP_PADDING,       "oeap"   },
+        { RSA_X931_PADDING,             "x931"   },
+        { RSA_PKCS1_PSS_PADDING,        "pss"    },
+        /* Special case, will pass directly as an integer */
+        { RSA_PKCS1_WITH_TLS_PADDING,   NULL     }
+    };
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_CTRL_TO_PARAMS && ctx->action_type == GET) {
+        /*
+         * EVP_PKEY_CTRL_GET_RSA_PADDING returns the padding mode in the
+         * weirdest way for a ctrl.  Instead of doing like all other ctrls
+         * that return a simple, i.e. just have that as a return value,
+         * this particular ctrl treats p2 as the address for the int to be
+         * returned.  We must therefore remember |ctx->p2|, then make
+         * |ctx->p2| point at a buffer to be filled in with the name, and
+         * |ctx->p1| with its size.  default_fixup_args() will take care
+         * of the rest for us, along with the POST_CTRL_TO_PARAMS && GET
+         * code section further down.
+         */
+        ctx->orig_p2 = ctx->p2;
+        ctx->p2 = ctx->name_buf;
+        ctx->p1 = sizeof(ctx->name_buf);
+    } else if (state == PRE_CTRL_TO_PARAMS && ctx->action_type == SET) {
+        /*
+         * Ideally, we should use utf8 strings for the diverse padding modes.
+         * We only came here because someone called EVP_PKEY_CTX_ctrl(),
+         * though, and since that can reasonably be seen as legacy code
+         * that uses the diverse RSA macros for the padding mode, and we
+         * know that at least our providers can handle the numeric modes,
+         * we take the cheap route for now.
+         *
+         * The other solution would be to match |ctx->p1| against entries
+         * in str_value_map and pass the corresponding string.  However,
+         * since we don't have a string for RSA_PKCS1_WITH_TLS_PADDING,
+         * we have to do this same hack at least for that one.
+         *
+         * Since the "official" data type for the RSA padding mode is utf8
+         * string, we cannot count on default_fixup_args().  Instead, we
+         * build the OSSL_PARAM item ourselves and return immediately.
+         */
+        ctx->params[0] = OSSL_PARAM_construct_int(translation->param_key,
+                                                  &ctx->p1);
+        return 1;
+    } else if (state == POST_PARAMS_TO_CTRL && ctx->action_type == GET) {
+        size_t i;
+
+        /*
+         * The EVP_PKEY_CTX_get_params() caller may have asked for a utf8
+         * string, or may have asked for an integer of some sort.  If they
+         * ask for an integer, we respond directly.  If not, we translate
+         * the response from the ctrl function into a string.
+         */
+        switch (ctx->params->data_type) {
+        case OSSL_PARAM_INTEGER:
+            return OSSL_PARAM_get_int(ctx->params, &ctx->p1);
+        case OSSL_PARAM_UNSIGNED_INTEGER:
+            return OSSL_PARAM_get_uint(ctx->params, (unsigned int *)&ctx->p1);
+        default:
+            break;
+        }
+
+        for (i = 0; i < OSSL_NELEM(str_value_map); i++) {
+            if (ctx->p1 == (int)str_value_map[i].id)
+                break;
+        }
+        if (i == OSSL_NELEM(str_value_map)) {
+            ERR_raise_data(ERR_LIB_RSA, RSA_R_UNKNOWN_PADDING_TYPE,
+                           "[action:%d, state:%d] padding number %d",
+                           ctx->action_type, state, ctx->p1);
+            return -2;
+        }
+        /*
+         * If we don't have a string, we can't do anything.  The caller
+         * should have asked for a number...
+         */
+        if (str_value_map[i].ptr == NULL) {
+            ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+            return -2;
+        }
+        ctx->p2 = str_value_map[i].ptr;
+        ctx->p1 = strlen(ctx->p2);
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((ctx->action_type == SET && state == PRE_PARAMS_TO_CTRL)
+        || (ctx->action_type == GET && state == POST_CTRL_TO_PARAMS)) {
+        size_t i;
+
+        for (i = 0; i < OSSL_NELEM(str_value_map); i++) {
+            if (strcmp(ctx->p2, str_value_map[i].ptr) == 0)
+                break;
+        }
+
+        if (i == OSSL_NELEM(str_value_map)) {
+            ERR_raise_data(ERR_LIB_RSA, RSA_R_UNKNOWN_PADDING_TYPE,
+                           "[action:%d, state:%d] padding name %s",
+                           ctx->action_type, state, ctx->p1);
+            ctx->p1 = ret = -2;
+        } else if (state == POST_CTRL_TO_PARAMS) {
+            /* EVP_PKEY_CTRL_GET_RSA_PADDING weirdness explained further up */
+            *(int *)ctx->orig_p2 = str_value_map[i].id;
+        } else {
+            ctx->p1 = str_value_map[i].id;
+        }
+        ctx->p2 = NULL;
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_RSA_PSS_SALTLEN, EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN */
+static int fix_rsa_pss_saltlen(enum state state,
+                               const struct translation_st *translation,
+                               struct translation_ctx_st *ctx)
+{
+    static const OSSL_ITEM str_value_map[] = {
+        { (unsigned int)RSA_PSS_SALTLEN_DIGEST, "digest" },
+        { (unsigned int)RSA_PSS_SALTLEN_MAX,    "max"    },
+        { (unsigned int)RSA_PSS_SALTLEN_AUTO,   "auto"   }
+    };
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if (state == PRE_CTRL_TO_PARAMS && ctx->action_type == GET) {
+        /*
+         * EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN returns the saltlen by filling
+         * in the int pointed at by p2.  This is potentially as weird as
+         * the way EVP_PKEY_CTRL_GET_RSA_PADDING works, except that saltlen
+         * might be a negative value, so it wouldn't work as a legitimate
+         * return value.
+         * In any case, we must therefore remember |ctx->p2|, then make
+         * |ctx->p2| point at a buffer to be filled in with the name, and
+         * |ctx->p1| with its size.  default_fixup_args() will take care
+         * of the rest for us, along with the POST_CTRL_TO_PARAMS && GET
+         * code section further down.
+         */
+        ctx->orig_p2 = ctx->p2;
+        ctx->p2 = ctx->name_buf;
+        ctx->p1 = sizeof(ctx->name_buf);
+    } else if ((ctx->action_type == SET && state == PRE_CTRL_TO_PARAMS)
+        || (ctx->action_type == GET && state == POST_PARAMS_TO_CTRL)) {
+        size_t i;
+
+        for (i = 0; i < OSSL_NELEM(str_value_map); i++) {
+            if (ctx->p1 == (int)str_value_map[i].id)
+                break;
+        }
+        if (i == OSSL_NELEM(str_value_map)) {
+            BIO_snprintf(ctx->name_buf, 5, "%d", ctx->p1);
+        } else {
+            strcpy(ctx->name_buf, str_value_map[i].ptr);
+        }
+        ctx->p2 = ctx->name_buf;
+        ctx->p1 = strlen(ctx->p2);
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((ctx->action_type == SET && state == PRE_PARAMS_TO_CTRL)
+        || (ctx->action_type == GET && state == POST_CTRL_TO_PARAMS)) {
+        size_t i;
+
+        for (i = 0; i < OSSL_NELEM(str_value_map); i++) {
+            if (strcmp(ctx->p2, str_value_map[i].ptr) == 0)
+                break;
+        }
+        if (i == OSSL_NELEM(str_value_map)) {
+            ctx->p1 = atoi(ctx->p2);
+        } else if (state == POST_CTRL_TO_PARAMS) {
+            /*
+             * EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN weirdness explained further
+             * up
+             */
+            *(int *)ctx->orig_p2 = str_value_map[i].id;
+        } else {
+            ctx->p1 = (int)str_value_map[i].id;
+        }
+        ctx->p2 = NULL;
+    }
+
+    return ret;
+}
+
+/* EVP_PKEY_CTRL_HKDF_MODE */
+static int fix_hkdf_mode(enum state state,
+                         const struct translation_st *translation,
+                         struct translation_ctx_st *ctx)
+{
+    static const OSSL_ITEM str_value_map[] = {
+        { EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND, "EXTRACT_AND_EXPAND" },
+        { EVP_KDF_HKDF_MODE_EXTRACT_ONLY,       "EXTRACT_ONLY"       },
+        { EVP_KDF_HKDF_MODE_EXPAND_ONLY,        "EXPAND_ONLY"        }
+    };
+    int ret;
+
+    if ((ret = default_check(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((ctx->action_type == SET && state == PRE_CTRL_TO_PARAMS)
+        || (ctx->action_type == GET && state == POST_PARAMS_TO_CTRL)) {
+        size_t i;
+
+        for (i = 0; i < OSSL_NELEM(str_value_map); i++) {
+            if (ctx->p1 == (int)str_value_map[i].id)
+                break;
+        }
+        if (i == OSSL_NELEM(str_value_map))
+            return 0;
+        ctx->p2 = str_value_map[i].ptr;
+        ctx->p1 = strlen(ctx->p2);
+    }
+
+    if ((ret = default_fixup_args(state, translation, ctx)) <= 0)
+        return ret;
+
+    if ((ctx->action_type == SET && state == PRE_PARAMS_TO_CTRL)
+        || (ctx->action_type == GET && state == POST_CTRL_TO_PARAMS)) {
+        size_t i;
+
+        for (i = 0; i < OSSL_NELEM(str_value_map); i++) {
+            if (strcmp(ctx->p2, str_value_map[i].ptr) == 0)
+                break;
+        }
+        if (i == OSSL_NELEM(str_value_map))
+            return 0;
+        if (state == POST_CTRL_TO_PARAMS)
+            ret = str_value_map[i].id;
+        else
+            ctx->p1 = str_value_map[i].id;
+        ctx->p2 = NULL;
+    }
+
+    return 1;
+}
+
+static int hack_pkcs7_cms(enum state state,
+                          const struct translation_st *translation,
+                          struct translation_ctx_st *ctx)
+{
+    int ret = 1;
+
+    /* Make sure that this has no further effect */
+    ctx->action_type = 0;
+
+    switch (state) {
+    case PRE_CTRL_TO_PARAMS:
+        /* TODO (3.0) Temporary hack, this should probe */
+        if (EVP_PKEY_is_a(EVP_PKEY_CTX_get0_pkey(ctx->pctx), "RSASSA-PSS")) {
+            ERR_raise(ERR_LIB_EVP,
+                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+            ret = -2;
+        }
+        break;
+    case POST_CTRL_TO_PARAMS:
+        break;
+    default:
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        ret = -2;
+        break;
+    }
+    return ret;
+}
+
+/*-
+ * Payload getters
+ * ===============
+ *
+ * These all get the data they want, then call default_fixup_args() as
+ * a post-ctrl GET fixup.  They all get NULL ctx, ctrl_cmd, ctrl_str,
+ * p1, sz
+ */
+
+/* Pilfering DH, DSA and EC_KEY */
+static int get_payload_group_name(enum state state,
+                                  const struct translation_st *translation,
+                                  struct translation_ctx_st *ctx)
+{
+    EVP_PKEY *pkey = ctx->p2;
+
+    ctx->p2 = NULL;
+    switch (EVP_PKEY_base_id(pkey)) {
+#ifndef OPENSSL_NO_DH
+    case EVP_PKEY_DH:
+        {
+            DH *dh = EVP_PKEY_get0_DH(pkey);
+            int uid = DH_get_nid(dh);
+
+            if (uid != NID_undef) {
+                const DH_NAMED_GROUP *dh_group =
+                    ossl_ffc_uid_to_dh_named_group(uid);
+
+                ctx->p2 = (char *)ossl_ffc_named_group_get_name(dh_group);
+            }
+        }
+        break;
+#endif
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_EC:
+        {
+            const EC_GROUP *grp =
+                EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey));
+            int nid = NID_undef;
+
+            if (grp != NULL)
+                nid = EC_GROUP_get_curve_name(grp);
+            if (nid != NID_undef)
+                ctx->p2 = (char *)ec_curve_nid2name(nid);
+        }
+        break;
+#endif
+    default:
+        ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE);
+        return 0;
+    }
+
+    if (ctx->p2 != NULL)
+        ctx->p1 = strlen(ctx->p2);
+    return default_fixup_args(state, translation, ctx);
+}
+
+static int get_payload_private_key(enum state state,
+                                   const struct translation_st *translation,
+                                   struct translation_ctx_st *ctx)
+{
+    EVP_PKEY *pkey = ctx->p2;
+
+    ctx->p2 = NULL;
+    if (ctx->params->data_type != OSSL_PARAM_UNSIGNED_INTEGER)
+        return 0;
+
+    switch (EVP_PKEY_base_id(pkey)) {
+#ifndef OPENSSL_NO_DH
+    case EVP_PKEY_DH:
+        {
+            DH *dh = EVP_PKEY_get0_DH(pkey);
+
+            ctx->p2 = (BIGNUM *)DH_get0_priv_key(dh);
+        }
+        break;
+#endif
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_EC:
+        {
+            EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+
+            ctx->p2 = (BIGNUM *)EC_KEY_get0_private_key(ec);
+        }
+        break;
+#endif
+    default:
+        ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE);
+        return 0;
+    }
+
+    return default_fixup_args(state, translation, ctx);
+}
+
+static int get_payload_public_key(enum state state,
+                                  const struct translation_st *translation,
+                                  struct translation_ctx_st *ctx)
+{
+    EVP_PKEY *pkey = ctx->p2;
+    unsigned char *buf = NULL;
+    int ret;
+
+    ctx->p2 = NULL;
+    switch (EVP_PKEY_base_id(pkey)) {
+#ifndef OPENSSL_NO_DH
+    case EVP_PKEY_DH:
+        switch (ctx->params->data_type) {
+        case OSSL_PARAM_OCTET_STRING:
+            ctx->sz = dh_key2buf(EVP_PKEY_get0_DH(pkey), &buf, 0, 1);
+            ctx->p2 = buf;
+            break;
+        case OSSL_PARAM_UNSIGNED_INTEGER:
+            ctx->p2 = (void *)DH_get0_pub_key(EVP_PKEY_get0_DH(pkey));
+            break;
+        default:
+            return 0;
+        }
+        break;
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        if (ctx->params->data_type == OSSL_PARAM_UNSIGNED_INTEGER) {
+            ctx->p2 = (void *)DSA_get0_pub_key(EVP_PKEY_get0_DSA(pkey));
+            break;
+        }
+        return 0;
+#endif
+#ifndef OPENSSL_NO_EC
+    case EVP_PKEY_EC:
+        if (ctx->params->data_type == OSSL_PARAM_OCTET_STRING) {
+            EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey);
+            BN_CTX *bnctx = BN_CTX_new_ex(ec_key_get_libctx(eckey));
+            const EC_GROUP *ecg = EC_KEY_get0_group(eckey);
+            const EC_POINT *point = EC_KEY_get0_public_key(eckey);
+
+            ctx->sz = EC_POINT_point2buf(ecg, point,
+                                         POINT_CONVERSION_COMPRESSED,
+                                         &buf, bnctx);
+            ctx->p2 = buf;
+            break;
+        }
+        return 0;
+#endif
+    default:
+        ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE);
+        return 0;
+    }
+
+    ret = default_fixup_args(state, translation, ctx);
+    OPENSSL_free(buf);
+    return ret;
+}
+
+static int get_payload_bn(enum state state,
+                          const struct translation_st *translation,
+                          struct translation_ctx_st *ctx, const BIGNUM *bn)
+{
+    if (bn == NULL)
+        return 0;
+    if (ctx->params->data_type != OSSL_PARAM_UNSIGNED_INTEGER)
+        return 0;
+    ctx->p2 = (BIGNUM *)bn;
+
+    return default_fixup_args(state, translation, ctx);
+}
+
+static int get_dh_dsa_payload_p(enum state state,
+                                const struct translation_st *translation,
+                                struct translation_ctx_st *ctx)
+{
+    const BIGNUM *bn = NULL;
+    EVP_PKEY *pkey = ctx->p2;
+
+    switch (EVP_PKEY_base_id(pkey)) {
+#ifndef OPENSSL_NO_DH
+    case EVP_PKEY_DH:
+        bn = DH_get0_p(EVP_PKEY_get0_DH(pkey));
+        break;
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        bn = DSA_get0_p(EVP_PKEY_get0_DSA(pkey));
+        break;
+#endif
+    default:
+        ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE);
+    }
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_dh_dsa_payload_q(enum state state,
+                                const struct translation_st *translation,
+                                struct translation_ctx_st *ctx)
+{
+    const BIGNUM *bn = NULL;
+
+    switch (EVP_PKEY_base_id(ctx->p2)) {
+#ifndef OPENSSL_NO_DH
+    case EVP_PKEY_DH:
+        bn = DH_get0_q(EVP_PKEY_get0_DH(ctx->p2));
+        break;
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        bn = DSA_get0_q(EVP_PKEY_get0_DSA(ctx->p2));
+        break;
+#endif
+    }
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_dh_dsa_payload_g(enum state state,
+                                const struct translation_st *translation,
+                                struct translation_ctx_st *ctx)
+{
+    const BIGNUM *bn = NULL;
+
+    switch (EVP_PKEY_base_id(ctx->p2)) {
+#ifndef OPENSSL_NO_DH
+    case EVP_PKEY_DH:
+        bn = DH_get0_g(EVP_PKEY_get0_DH(ctx->p2));
+        break;
+#endif
+#ifndef OPENSSL_NO_DSA
+    case EVP_PKEY_DSA:
+        bn = DSA_get0_g(EVP_PKEY_get0_DSA(ctx->p2));
+        break;
+#endif
+    }
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_rsa_payload_n(enum state state,
+                             const struct translation_st *translation,
+                             struct translation_ctx_st *ctx)
+{
+    const BIGNUM *bn = NULL;
+
+    if (EVP_PKEY_base_id(ctx->p2) != EVP_PKEY_RSA)
+        return 0;
+    bn = RSA_get0_n(EVP_PKEY_get0_RSA(ctx->p2));
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_rsa_payload_e(enum state state,
+                             const struct translation_st *translation,
+                             struct translation_ctx_st *ctx)
+{
+    const BIGNUM *bn = NULL;
+
+    if (EVP_PKEY_base_id(ctx->p2) != EVP_PKEY_RSA)
+        return 0;
+    bn = RSA_get0_e(EVP_PKEY_get0_RSA(ctx->p2));
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_rsa_payload_d(enum state state,
+                             const struct translation_st *translation,
+                             struct translation_ctx_st *ctx)
+{
+    const BIGNUM *bn = NULL;
+
+    if (EVP_PKEY_base_id(ctx->p2) != EVP_PKEY_RSA)
+        return 0;
+    bn = RSA_get0_d(EVP_PKEY_get0_RSA(ctx->p2));
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_rsa_payload_factor(enum state state,
+                                  const struct translation_st *translation,
+                                  struct translation_ctx_st *ctx,
+                                  size_t factornum)
+{
+    const RSA *r = EVP_PKEY_get0_RSA(ctx->p2);
+    const BIGNUM *bn = NULL;
+
+    switch (factornum) {
+    case 0:
+        bn = RSA_get0_p(r);
+        break;
+    case 1:
+        bn = RSA_get0_q(r);
+        break;
+    default:
+        {
+            size_t pnum = RSA_get_multi_prime_extra_count(r);
+            const BIGNUM *factors[10];
+
+            if (factornum - 2 < pnum
+                && RSA_get0_multi_prime_factors(r, factors))
+                bn = factors[factornum - 2];
+        }
+        break;
+    }
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_rsa_payload_exponent(enum state state,
+                                    const struct translation_st *translation,
+                                    struct translation_ctx_st *ctx,
+                                    size_t exponentnum)
+{
+    const RSA *r = EVP_PKEY_get0_RSA(ctx->p2);
+    const BIGNUM *bn = NULL;
+
+    switch (exponentnum) {
+    case 0:
+        bn = RSA_get0_dmp1(r);
+        break;
+    case 1:
+        bn = RSA_get0_dmq1(r);
+        break;
+    default:
+        {
+            size_t pnum = RSA_get_multi_prime_extra_count(r);
+            const BIGNUM *exps[10], *coeffs[10];
+
+            if (exponentnum - 2 < pnum
+                && RSA_get0_multi_prime_crt_params(r, exps, coeffs))
+                bn = exps[exponentnum - 2];
+        }
+        break;
+    }
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+static int get_rsa_payload_coefficient(enum state state,
+                                       const struct translation_st *translation,
+                                       struct translation_ctx_st *ctx,
+                                       size_t coefficientnum)
+{
+    const RSA *r = EVP_PKEY_get0_RSA(ctx->p2);
+    const BIGNUM *bn = NULL;
+
+    switch (coefficientnum) {
+    case 0:
+        bn = RSA_get0_iqmp(r);
+        break;
+    default:
+        {
+            size_t pnum = RSA_get_multi_prime_extra_count(r);
+            const BIGNUM *exps[10], *coeffs[10];
+
+            if (coefficientnum - 1 < pnum
+                && RSA_get0_multi_prime_crt_params(r, exps, coeffs))
+                bn = coeffs[coefficientnum - 1];
+        }
+        break;
+    }
+
+    return get_payload_bn(state, translation, ctx, bn);
+}
+
+#define IMPL_GET_RSA_PAYLOAD_FACTOR(n)                                  \
+    static int                                                          \
+    get_rsa_payload_f##n(enum state state,                              \
+                         const struct translation_st *translation,      \
+                         struct translation_ctx_st *ctx)                \
+    {                                                                   \
+        if (EVP_PKEY_base_id(ctx->p2) != EVP_PKEY_RSA)                  \
+            return 0;                                                   \
+        return get_rsa_payload_factor(state, translation, ctx, n - 1);  \
+    }
+
+#define IMPL_GET_RSA_PAYLOAD_EXPONENT(n)                                \
+    static int                                                          \
+    get_rsa_payload_e##n(enum state state,                              \
+                         const struct translation_st *translation,      \
+                         struct translation_ctx_st *ctx)                \
+    {                                                                   \
+        if (EVP_PKEY_base_id(ctx->p2) != EVP_PKEY_RSA)                  \
+            return 0;                                                   \
+        return get_rsa_payload_exponent(state, translation, ctx,        \
+                                        n - 1);                         \
+    }
+
+#define IMPL_GET_RSA_PAYLOAD_COEFFICIENT(n)                             \
+    static int                                                          \
+    get_rsa_payload_c##n(enum state state,                              \
+                         const struct translation_st *translation,      \
+                         struct translation_ctx_st *ctx)                \
+    {                                                                   \
+        if (EVP_PKEY_base_id(ctx->p2) != EVP_PKEY_RSA)                  \
+            return 0;                                                   \
+        return get_rsa_payload_coefficient(state, translation, ctx,     \
+                                           n - 1);                      \
+    }
+
+IMPL_GET_RSA_PAYLOAD_FACTOR(1)
+IMPL_GET_RSA_PAYLOAD_FACTOR(2)
+IMPL_GET_RSA_PAYLOAD_FACTOR(3)
+IMPL_GET_RSA_PAYLOAD_FACTOR(4)
+IMPL_GET_RSA_PAYLOAD_FACTOR(5)
+IMPL_GET_RSA_PAYLOAD_FACTOR(6)
+IMPL_GET_RSA_PAYLOAD_FACTOR(7)
+IMPL_GET_RSA_PAYLOAD_FACTOR(8)
+IMPL_GET_RSA_PAYLOAD_FACTOR(9)
+IMPL_GET_RSA_PAYLOAD_FACTOR(10)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(1)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(2)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(3)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(4)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(5)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(6)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(7)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(8)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(9)
+IMPL_GET_RSA_PAYLOAD_EXPONENT(10)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(1)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(2)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(3)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(4)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(5)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(6)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(7)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(8)
+IMPL_GET_RSA_PAYLOAD_COEFFICIENT(9)
+
+/*-
+ * The translation table itself
+ * ============================
+ */
+
+static const struct translation_st evp_pkey_ctx_translations[] = {
+    /*
+     * DistID: we pass it to the backend as an octet string,
+     * but get it back as a pointer to an octet string.
+     *
+     * Note that the EVP_PKEY_CTRL_GET1_ID_LEN is purely for legacy purposes
+     * that has no separate counterpart in OSSL_PARAM terms, since we get
+     * the length of the DistID automatically when getting the DistID itself.
+     */
+    { SET, -1, -1, EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_SET1_ID, "distid", "hexdistid",
+      OSSL_PKEY_PARAM_DIST_ID, OSSL_PARAM_OCTET_STRING, NULL },
+    { GET, -1, -1, -1,
+      EVP_PKEY_CTRL_GET1_ID, "distid", "hexdistid",
+      OSSL_PKEY_PARAM_DIST_ID, OSSL_PARAM_OCTET_PTR, NULL },
+    { GET, -1, -1, -1,
+      EVP_PKEY_CTRL_GET1_ID_LEN, NULL, NULL,
+      OSSL_PKEY_PARAM_DIST_ID, OSSL_PARAM_OCTET_PTR, fix_distid_len },
+
+    /*-
+     * DH & DHX
+     * ========
+     */
+
+    /*
+     * EVP_PKEY_CTRL_DH_KDF_TYPE is used both for setting and getting.  The
+     * fixup function has to handle this...
+     */
+    { NONE, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_KDF_TYPE, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_TYPE, OSSL_PARAM_UTF8_STRING,
+      fix_dh_kdf_type },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_KDF_MD, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { GET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_DH_KDF_MD, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_KDF_OUTLEN, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_OUTLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { GET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_OUTLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_KDF_UKM, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_STRING, NULL },
+    { GET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_DH_KDF_UKM, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR, NULL },
+    { SET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_KDF_OID, NULL, NULL,
+      OSSL_KDF_PARAM_CEK_ALG, OSSL_PARAM_UTF8_STRING, fix_oid },
+    { GET, EVP_PKEY_DHX, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_DH_KDF_OID, NULL, NULL,
+      OSSL_KDF_PARAM_CEK_ALG, OSSL_PARAM_UTF8_STRING, fix_oid },
+
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_DH_PAD, "dh_pad", NULL,
+      OSSL_EXCHANGE_PARAM_PAD, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_DH_NID, "dh_param", NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING, fix_dh_nid },
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, NULL, NULL,
+      OSSL_PKEY_PARAM_FFC_PBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN, "dh_paramgen_subprime_len", NULL,
+      OSSL_PKEY_PARAM_FFC_QBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR, "dh_paramgen_generator", NULL,
+      OSSL_PKEY_PARAM_DH_GENERATOR, OSSL_PARAM_INTEGER, NULL },
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, "dh_paramgen_type", NULL,
+      OSSL_PKEY_PARAM_FFC_TYPE, OSSL_PARAM_UTF8_STRING, fix_dh_paramgen_type },
+ /*
+  * This is know to be incorrect, will be fixed and enabled when the
+  * underlying code is corrected.
+  * Until then, we simply don't support it here.
+  */
+#if 0
+    { SET, EVP_PKEY_DH, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DH_RFC5114, "dh_rfc5114", NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_INTEGER, NULL },
+#endif
+
+    /*-
+     * DSA
+     * ===
+     */
+    { SET, EVP_PKEY_DSA, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DSA_PARAMGEN_BITS, "dsa_paramgen_bits", NULL,
+      OSSL_PKEY_PARAM_FFC_PBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DSA, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS, "dsa_paramgen_q_bits", NULL,
+      OSSL_PKEY_PARAM_FFC_QBITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_DSA, 0, EVP_PKEY_OP_PARAMGEN,
+      EVP_PKEY_CTRL_DSA_PARAMGEN_MD, "dsa_paramgen_md", NULL,
+      OSSL_PKEY_PARAM_FFC_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+
+    /*-
+     * EC
+     * ==
+     */
+    { SET, EVP_PKEY_EC, 0, EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_EC_PARAM_ENC, "ec_param_enc", NULL,
+      OSSL_PKEY_PARAM_EC_ENCODING, OSSL_PARAM_UTF8_STRING, fix_ec_param_enc },
+    { SET, EVP_PKEY_EC, 0, EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID, "ec_paramgen_curve", NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING,
+      fix_ec_paramgen_curve_nid },
+    /*
+     * EVP_PKEY_CTRL_EC_ECDH_COFACTOR and EVP_PKEY_CTRL_EC_KDF_TYPE are used
+     * both for setting and getting.  The fixup function has to handle this...
+     */
+    { NONE, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_EC_ECDH_COFACTOR, "ecdh_cofactor_mode", NULL,
+      OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, OSSL_PARAM_INTEGER,
+      fix_ecdh_cofactor },
+    { NONE, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_EC_KDF_TYPE, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_TYPE, OSSL_PARAM_UTF8_STRING, fix_ec_kdf_type },
+    { SET, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_EC_KDF_MD, "ecdh_kdf_md", NULL,
+      OSSL_EXCHANGE_PARAM_KDF_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { GET, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_EC_KDF_MD, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { SET, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_EC_KDF_OUTLEN, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_OUTLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { GET, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_OUTLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_EC_KDF_UKM, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_STRING, NULL },
+    { GET, EVP_PKEY_EC, 0, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_GET_EC_KDF_UKM, NULL, NULL,
+      OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR, NULL },
+
+    /*-
+     * RSA
+     * ===
+     */
+
+    /*
+     * RSA padding modes are numeric with ctrls, strings with ctrl_strs,
+     * and can be both with OSSL_PARAM.  We standardise on strings here,
+     * fix_rsa_padding_mode() does the work when the caller has a different
+     * idea.
+     */
+    { SET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS,
+      EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_RSA_PADDING, "rsa_padding_mode", NULL,
+      OSSL_PKEY_PARAM_PAD_MODE, OSSL_PARAM_UTF8_STRING, fix_rsa_padding_mode },
+    { GET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS,
+      EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_GET_RSA_PADDING, NULL, NULL,
+      OSSL_PKEY_PARAM_PAD_MODE, OSSL_PARAM_UTF8_STRING, fix_rsa_padding_mode },
+
+    { SET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS,
+      EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_RSA_MGF1_MD, "rsa_mgf1_md", NULL,
+      OSSL_PKEY_PARAM_MGF1_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { GET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS,
+      EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_GET_RSA_MGF1_MD, NULL, NULL,
+      OSSL_PKEY_PARAM_MGF1_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+
+    /*
+     * RSA-PSS saltlen is essentially numeric, but certain values can be
+     * expressed as keywords (strings) with ctrl_str.  The corresponding
+     * OSSL_PARAM allows both forms.
+     * fix_rsa_pss_saltlen() takes care of the distinction.
+     */
+    { SET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_RSA_PSS_SALTLEN, "rsa_pss_saltlen", NULL,
+      OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, OSSL_PARAM_UTF8_STRING,
+      fix_rsa_pss_saltlen },
+    { GET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, OSSL_PARAM_UTF8_STRING,
+      fix_rsa_pss_saltlen },
+
+    { SET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_TYPE_CRYPT,
+      EVP_PKEY_CTRL_RSA_OAEP_MD, "rsa_oaep_md", NULL,
+      OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { GET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_TYPE_CRYPT,
+      EVP_PKEY_CTRL_GET_RSA_OAEP_MD, NULL, NULL,
+      OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    /*
+     * The "rsa_oaep_label" ctrl_str expects the value to always be hex.
+     * This is accomodated by default_fixup_args() above, which mimics that
+     * expectation for any translation item where |ctrl_str| is NULL and
+     * |ctrl_hexstr| is non-NULL.
+     */
+    { SET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_TYPE_CRYPT,
+      EVP_PKEY_CTRL_RSA_OAEP_LABEL, NULL, "rsa_oaep_label",
+      OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, OSSL_PARAM_OCTET_STRING, NULL },
+    { GET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_TYPE_CRYPT,
+      EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL, NULL, NULL,
+      OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, OSSL_PARAM_OCTET_STRING, NULL },
+
+    { SET, EVP_PKEY_RSA_PSS, 0, EVP_PKEY_OP_TYPE_GEN,
+      EVP_PKEY_CTRL_MD, "rsa_pss_keygen_md", NULL,
+      OSSL_ALG_PARAM_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { SET, EVP_PKEY_RSA_PSS, 0, EVP_PKEY_OP_TYPE_GEN,
+      EVP_PKEY_CTRL_RSA_MGF1_MD, "rsa_pss_keygen_mgf1_md", NULL,
+      OSSL_PKEY_PARAM_MGF1_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { SET, EVP_PKEY_RSA_PSS, 0, EVP_PKEY_OP_TYPE_GEN,
+      EVP_PKEY_CTRL_RSA_PSS_SALTLEN, "rsa_pss_keygen_saltlen", NULL,
+      OSSL_SIGNATURE_PARAM_PSS_SALTLEN, OSSL_PARAM_INTEGER, NULL },
+    { SET, EVP_PKEY_RSA, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_RSA_KEYGEN_BITS, "rsa_keygen_bits", NULL,
+      OSSL_PKEY_PARAM_RSA_BITS, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP, "rsa_keygen_pubexp", NULL,
+      OSSL_PKEY_PARAM_RSA_E, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, EVP_PKEY_RSA, 0, EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_RSA_KEYGEN_PRIMES, "rsa_keygen_primes", NULL,
+      OSSL_PKEY_PARAM_RSA_PRIMES, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+
+    /* PKCS#7 and CMS hacks */
+    { SET, -1, -1, EVP_PKEY_OP_ENCRYPT,
+      EVP_PKEY_CTRL_PKCS7_ENCRYPT, NULL, NULL, NULL, 0, hack_pkcs7_cms },
+    { SET, -1, -1, EVP_PKEY_OP_DECRYPT,
+      EVP_PKEY_CTRL_PKCS7_DECRYPT, NULL, NULL, NULL, 0, hack_pkcs7_cms },
+    { SET, -1, -1, EVP_PKEY_OP_ENCRYPT,
+      EVP_PKEY_CTRL_CMS_ENCRYPT, NULL, NULL, NULL, 0, hack_pkcs7_cms },
+    { SET, -1, -1, EVP_PKEY_OP_DECRYPT,
+      EVP_PKEY_CTRL_CMS_DECRYPT, NULL, NULL, NULL, 0, hack_pkcs7_cms },
+
+    /*-
+     * TLS1-PRF
+     * ========
+     */
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_TLS_MD, "md", NULL,
+      OSSL_KDF_PARAM_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_TLS_SECRET, "secret", "hexsecret",
+      OSSL_KDF_PARAM_SECRET, OSSL_PARAM_OCTET_STRING, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_TLS_SEED, "seed", "hexseed",
+      OSSL_KDF_PARAM_SEED, OSSL_PARAM_OCTET_STRING, NULL },
+
+    /*-
+     * HKDF
+     * ====
+     */
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_HKDF_MD, "md", NULL,
+      OSSL_KDF_PARAM_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_HKDF_SALT, "salt", "hexsalt",
+      OSSL_KDF_PARAM_SALT, OSSL_PARAM_OCTET_STRING, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_HKDF_KEY, "key", "hexkey",
+      OSSL_KDF_PARAM_KEY, OSSL_PARAM_OCTET_STRING, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_HKDF_INFO, "info", "hexinfo",
+      OSSL_KDF_PARAM_INFO, OSSL_PARAM_OCTET_STRING, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_HKDF_MODE, "mode", NULL,
+      OSSL_KDF_PARAM_MODE, OSSL_PARAM_INTEGER, fix_hkdf_mode },
+
+    /*-
+     * Scrypt
+     * ======
+     */
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_PASS, "pass", "hexpass",
+      OSSL_KDF_PARAM_PASSWORD, OSSL_PARAM_OCTET_STRING, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_SCRYPT_SALT, "salt", "hexsalt",
+      OSSL_KDF_PARAM_SALT, OSSL_PARAM_OCTET_STRING, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_SCRYPT_N, "N", NULL,
+      OSSL_KDF_PARAM_SCRYPT_N, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_SCRYPT_R, "r", NULL,
+      OSSL_KDF_PARAM_SCRYPT_R, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_SCRYPT_P, "p", NULL,
+      OSSL_KDF_PARAM_SCRYPT_P, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+    { SET, -1, -1, EVP_PKEY_OP_DERIVE,
+      EVP_PKEY_CTRL_SCRYPT_MAXMEM_BYTES, "maxmem_bytes", NULL,
+      OSSL_KDF_PARAM_SCRYPT_MAXMEM, OSSL_PARAM_UNSIGNED_INTEGER, NULL },
+
+    { SET, -1, -1, EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_CIPHER, NULL, NULL,
+      OSSL_PKEY_PARAM_CIPHER, OSSL_PARAM_UTF8_STRING, fix_cipher },
+    { SET, -1, -1, EVP_PKEY_OP_KEYGEN,
+      EVP_PKEY_CTRL_SET_MAC_KEY, NULL, NULL,
+      OSSL_PKEY_PARAM_PRIV_KEY, OSSL_PARAM_OCTET_STRING, NULL },
+
+    { SET, -1, -1, EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_MD, NULL, NULL,
+      OSSL_SIGNATURE_PARAM_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+    { GET, -1, -1, EVP_PKEY_OP_TYPE_SIG,
+      EVP_PKEY_CTRL_GET_MD, NULL, NULL,
+      OSSL_SIGNATURE_PARAM_DIGEST, OSSL_PARAM_UTF8_STRING, fix_md },
+};
+
+static const struct translation_st evp_pkey_translations[] = {
+    /*
+     * The following contain no ctrls, they are exclusively here to extract
+     * key payloads from legacy keys, using OSSL_PARAMs, and rely entirely
+     * on |fixup_args| to pass the actual data.  The |fixup_args| should
+     * expect to get the EVP_PKEY pointer through |ctx->p2|.
+     */
+
+    /* DH, DSA & EC */
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_GROUP_NAME, OSSL_PARAM_UTF8_STRING,
+      get_payload_group_name },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_PRIV_KEY, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_payload_private_key },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_PUB_KEY,
+      0 /* no data type, let get_payload_pub_key() handle that */,
+      get_payload_public_key },
+
+    /* DH and DSA */
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_FFC_P, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_dh_dsa_payload_p },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_FFC_G, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_dh_dsa_payload_g },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_FFC_Q, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_dh_dsa_payload_q },
+
+    /* RSA */
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_N, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_n },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_E, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_D, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_d },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR1, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f1 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR2, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f2 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR3, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f3 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR4, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f4 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR5, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f5 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR6, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f6 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR7, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f7 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR8, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f8 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR9, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f9 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_FACTOR10, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_f10 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT1, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e1 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT2, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e2 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT3, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e3 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT4, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e4 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT5, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e5 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT6, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e6 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT7, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e7 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT8, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e8 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT9, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e9 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_EXPONENT10, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_e10 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT1, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c1 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT2, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c2 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT3, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c3 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT4, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c4 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT5, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c5 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT6, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c6 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT7, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c7 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT8, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c8 },
+    { GET, -1, -1, -1, 0, NULL, NULL,
+      OSSL_PKEY_PARAM_RSA_COEFFICIENT9, OSSL_PARAM_UNSIGNED_INTEGER,
+      get_rsa_payload_c9 },
+};
+
+static const struct translation_st *
+lookup_translation(struct translation_st *tmpl,
+                   const struct translation_st *translations,
+                   size_t translations_num)
+{
+    size_t i;
+
+    for (i = 0; i < translations_num; i++) {
+        const struct translation_st *item = &translations[i];
+
+        /*
+         * Sanity check the translation table item.
+         *
+         * 1.  Either both keytypes are -1, or neither of them are.
+         * 2.  TBA...
+         */
+        if (!ossl_assert((item->keytype1 == -1) == (item->keytype2 == -1)))
+            continue;
+
+
+        /*
+         * Base search criteria: check that the optype and keytypes match,
+         * if relevant.  All callers must synthesise these bits somehow.
+         */
+        if (item->optype != -1 && (tmpl->optype & item->optype) == 0)
+            continue;
+        /*
+         * This expression is stunningly simple thanks to the sanity check
+         * above.
+         */
+        if (item->keytype1 != -1
+            && tmpl->keytype1 != item->keytype1
+            && tmpl->keytype2 != item->keytype2)
+            continue;
+
+        /*
+         * Done with the base search criteria, now we check the criteria for
+         * the individual types of translations:
+         * ctrl->params, ctrl_str->params, and params->ctrl
+         */
+        if (tmpl->ctrl_num != 0) {
+            if (tmpl->ctrl_num != item->ctrl_num)
+                continue;
+        } else if (tmpl->ctrl_str != NULL) {
+            const char *ctrl_str = NULL;
+            const char *ctrl_hexstr = NULL;
+
+            /*
+             * Search criteria that originates from a ctrl_str is only used
+             * for setting, never for getting.  Therefore, we only look at
+             * the setter items.
+             */
+            if (item->action_type != NONE
+                && item->action_type != SET)
+                continue;
+            /*
+             * At least one of the ctrl cmd names must be match the ctrl
+             * cmd name in the template.
+             */
+            if (item->ctrl_str != NULL
+                && strcasecmp(tmpl->ctrl_str, item->ctrl_str) == 0)
+                ctrl_str = tmpl->ctrl_str;
+            else if (item->ctrl_hexstr != NULL
+                     && strcasecmp(tmpl->ctrl_hexstr, item->ctrl_hexstr) == 0)
+                ctrl_hexstr = tmpl->ctrl_hexstr;
+            else
+                continue;
+
+            /* Modify the template to signal which string matched */
+            tmpl->ctrl_str = ctrl_str;
+            tmpl->ctrl_hexstr = ctrl_hexstr;
+        } else if (tmpl->param_key != NULL) {
+            /*
+             * Search criteria that originates from a OSSL_PARAM setter or
+             * getter.
+             *
+             * Ctrls were fundamentally bidirectional, with only the ctrl
+             * command macro name implying direction (if you're lucky).
+             * A few ctrl commands were even taking advantage of the
+             * bidirectional nature, making the direction depend in the
+             * value of the numeric argument.
+             *
+             * OSSL_PARAM functions are fundamentally different, in that
+             * setters and getters are separated, so the data direction is
+             * implied by the function that's used.  The same OSSL_PARAM
+             * key name can therefore be used in both directions.  We must
+             * therefore take the action type into account in this case.
+             */
+            if ((item->action_type != NONE
+                 && tmpl->action_type != item->action_type)
+                || (item->param_key != NULL
+                    && strcasecmp(tmpl->param_key, item->param_key) != 0))
+                continue;
+        } else {
+            return NULL;
+        }
+
+        return item;
+    }
+
+    return NULL;
+}
+
+static const struct translation_st *
+lookup_evp_pkey_ctx_translation(struct translation_st *tmpl)
+{
+    return lookup_translation(tmpl, evp_pkey_ctx_translations,
+                              OSSL_NELEM(evp_pkey_ctx_translations));
+}
+
+static const struct translation_st *
+lookup_evp_pkey_translation(struct translation_st *tmpl)
+{
+    return lookup_translation(tmpl, evp_pkey_translations,
+                              OSSL_NELEM(evp_pkey_translations));
+}
+
+/* This must ONLY be called for provider side operations */
+int evp_pkey_ctx_ctrl_to_param(EVP_PKEY_CTX *pctx,
+                               int keytype, int optype,
+                               int cmd, int p1, void *p2)
+{
+    struct translation_ctx_st ctx = { 0, };
+    struct translation_st tmpl = { 0, };
+    const struct translation_st *translation = NULL;
+    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    int ret;
+    fixup_args_fn *fixup = default_fixup_args;
+
+    if (keytype == -1)
+        keytype = pctx->legacy_keytype;
+    tmpl.ctrl_num = cmd;
+    tmpl.keytype1 = tmpl.keytype2 = keytype;
+    tmpl.optype = optype;
+    translation = lookup_evp_pkey_ctx_translation(&tmpl);
+
+    if (translation == NULL) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        return -2;
+    }
+
+    if (pctx->pmeth != NULL
+        && pctx->pmeth->pkey_id != translation->keytype1
+        && pctx->pmeth->pkey_id != translation->keytype2)
+        return -1;
+
+    if (translation->fixup_args != NULL)
+        fixup = translation->fixup_args;
+    ctx.action_type = translation->action_type;
+    ctx.ctrl_cmd = cmd;
+    ctx.p1 = p1;
+    ctx.p2 = p2;
+    ctx.pctx = pctx;
+    ctx.params = params;
+
+    ret = fixup(PRE_CTRL_TO_PARAMS, translation, &ctx);
+
+    if (ret > 0) {
+        switch (ctx.action_type) {
+        default:
+            /* fixup_args is expected to make sure this is dead code */
+            break;
+        case GET:
+            ret = evp_pkey_ctx_get_params_strict(pctx, ctx.params);
+            break;
+        case SET:
+            ret = evp_pkey_ctx_set_params_strict(pctx, ctx.params);
+            break;
+        }
+    }
+
+    /*
+     * In POST, we pass the return value as p1, allowing the fixup_args
+     * function to affect it by changing its value.
+     */
+    if (ret > 0) {
+        ctx.p1 = ret;
+        fixup(POST_CTRL_TO_PARAMS, translation, &ctx);
+        ret = ctx.p1;
+    }
+
+    cleanup_translation_ctx(POST_CTRL_TO_PARAMS, translation, &ctx);
+
+    return ret;
+}
+
+/* This must ONLY be called for provider side operations */
+int evp_pkey_ctx_ctrl_str_to_param(EVP_PKEY_CTX *pctx,
+                                   const char *name, const char *value)
+{
+    struct translation_ctx_st ctx = { 0, };
+    struct translation_st tmpl = { 0, };
+    const struct translation_st *translation = NULL;
+    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+    int keytype = pctx->legacy_keytype;
+    int optype = pctx->operation == 0 ? -1 : pctx->operation;
+    int ret;
+    fixup_args_fn *fixup = default_fixup_args;
+
+    tmpl.action_type = SET;
+    tmpl.keytype1 = tmpl.keytype2 = keytype;
+    tmpl.optype = optype;
+    tmpl.ctrl_str = name;
+    tmpl.ctrl_hexstr = name;
+    translation = lookup_evp_pkey_ctx_translation(&tmpl);
+
+    if (translation != NULL) {
+        if (translation->fixup_args != NULL)
+            fixup = translation->fixup_args;
+        ctx.action_type = translation->action_type;
+        ctx.ishex = (tmpl.ctrl_hexstr != NULL);
+    } else {
+        /* String controls really only support setting */
+        ctx.action_type = SET;
+    }
+    ctx.ctrl_str = name;
+    ctx.p1 = (int)strlen(value);
+    ctx.p2 = (char *)value;
+    ctx.pctx = pctx;
+    ctx.params = params;
+
+    ret = fixup(PRE_CTRL_STR_TO_PARAMS, translation, &ctx);
+
+    if (ret > 0) {
+        switch (ctx.action_type) {
+        default:
+            /* fixup_args is expected to make sure this is dead code */
+            break;
+        case GET:
+            /*
+             * this is dead code, but must be present, or some compilers
+             * will complain
+             */
+            break;
+        case SET:
+            ret = evp_pkey_ctx_set_params_strict(pctx, ctx.params);
+            break;
+        }
+    }
+
+    if (ret > 0)
+        ret = fixup(POST_CTRL_STR_TO_PARAMS, translation, &ctx);
+
+    cleanup_translation_ctx(CLEANUP_CTRL_STR_TO_PARAMS, translation, &ctx);
+
+    return ret;
+}
+
+/* This must ONLY be called for legacy operations */
+static int evp_pkey_ctx_setget_params_to_ctrl(EVP_PKEY_CTX *pctx,
+                                              enum action action_type,
+                                              OSSL_PARAM *params)
+{
+    int keytype = pctx->legacy_keytype;
+    int optype = pctx->operation == 0 ? -1 : pctx->operation;
+
+    for (; params != NULL && params->key != NULL; params++) {
+        struct translation_ctx_st ctx = { 0, };
+        struct translation_st tmpl = { 0, };
+        const struct translation_st *translation = NULL;
+        fixup_args_fn *fixup = default_fixup_args;
+        int ret;
+
+        tmpl.action_type = action_type;
+        tmpl.keytype1 = tmpl.keytype2 = keytype;
+        tmpl.optype = optype;
+        tmpl.param_key = params->key;
+        translation = lookup_evp_pkey_ctx_translation(&tmpl);
+
+        if (translation != NULL) {
+            if (translation->fixup_args != NULL)
+                fixup = translation->fixup_args;
+            ctx.action_type = translation->action_type;
+        }
+        ctx.pctx = pctx;
+        ctx.params = params;
+
+        ret = fixup(PRE_PARAMS_TO_CTRL, translation, &ctx);
+
+        if (ret > 0 && action_type != NONE)
+            ret = EVP_PKEY_CTX_ctrl(pctx, keytype, optype,
+                                    ctx.ctrl_cmd, ctx.p1, ctx.p2);
+
+        /*
+         * In POST, we pass the return value as p1, allowing the fixup_args
+         * function to put it to good use, or maybe affect it.
+         */
+        if (ret > 0) {
+            ctx.p1 = ret;
+            fixup(POST_PARAMS_TO_CTRL, translation, &ctx);
+            ret = ctx.p1;
+        }
+
+        cleanup_translation_ctx(CLEANUP_PARAMS_TO_CTRL, translation, &ctx);
+
+        if (ret <= 0)
+            return 0;
+    }
+    return 1;
+}
+
+int evp_pkey_ctx_set_params_to_ctrl(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
+{
+    return evp_pkey_ctx_setget_params_to_ctrl(ctx, SET, params);
+}
+
+int evp_pkey_ctx_get_params_to_ctrl(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
+{
+    return evp_pkey_ctx_setget_params_to_ctrl(ctx, GET, params);
+}
+
+/* This must ONLY be called for legacy EVP_PKEYs */
+static int evp_pkey_setget_params_to_ctrl(const EVP_PKEY *pkey,
+                                          enum action action_type,
+                                          OSSL_PARAM *params)
+{
+    int ret = 1;
+
+    for (; params != NULL && params->key != NULL; params++) {
+        struct translation_ctx_st ctx = { 0, };
+        struct translation_st tmpl = { 0, };
+        const struct translation_st *translation = NULL;
+        fixup_args_fn *fixup = default_fixup_args;
+
+        tmpl.action_type = action_type;
+        tmpl.param_key = params->key;
+        translation = lookup_evp_pkey_translation(&tmpl);
+
+        if (translation != NULL) {
+            if (translation->fixup_args != NULL)
+                fixup = translation->fixup_args;
+            ctx.action_type = translation->action_type;
+        }
+        ctx.p2 = (void *)pkey;
+        ctx.params = params;
+
+        /*
+         * EVP_PKEY doesn't have any ctrl function, so we rely completely
+         * on fixup_args to do the whole work.  Also, we currently only
+         * support getting.
+         */
+        if (!ossl_assert(translation != NULL)
+            || !ossl_assert(translation->action_type == GET)
+            || !ossl_assert(translation->fixup_args != NULL)) {
+            return -2;
+        }
+
+        ret = fixup(PKEY, translation, &ctx);
+
+        cleanup_translation_ctx(PKEY, translation, &ctx);
+    }
+    return ret;
+}
+
+int evp_pkey_get_params_to_ctrl(const EVP_PKEY *pkey, OSSL_PARAM *params)
+{
+    return evp_pkey_setget_params_to_ctrl(pkey, GET, params);
+}
+
diff --git a/crypto/evp/dh_ctrl.c b/crypto/evp/dh_ctrl.c
index c0268cb42c..abb724f72b 100644
--- a/crypto/evp/dh_ctrl.c
+++ b/crypto/evp/dh_ctrl.c
@@ -24,7 +24,7 @@ static int dh_paramgen_check(EVP_PKEY_CTX *ctx)
         return -2;
     }
     /* If key type not DH return error */
-    if (ctx->pmeth != NULL
+    if (evp_pkey_ctx_is_legacy(ctx)
         && ctx->pmeth->pkey_id != EVP_PKEY_DH
         && ctx->pmeth->pkey_id != EVP_PKEY_DHX)
         return -1;
@@ -39,7 +39,7 @@ static int dh_param_derive_check(EVP_PKEY_CTX *ctx)
         return -2;
     }
     /* If key type not DH return error */
-    if (ctx->pmeth != NULL
+    if (evp_pkey_ctx_is_legacy(ctx)
         && ctx->pmeth->pkey_id != EVP_PKEY_DH
         && ctx->pmeth->pkey_id != EVP_PKEY_DHX)
         return -1;
@@ -57,7 +57,7 @@ int EVP_PKEY_CTX_set_dh_paramgen_gindex(EVP_PKEY_CTX *ctx, int gindex)
     *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_FFC_GINDEX, &gindex);
     *p = OSSL_PARAM_construct_end();
 
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
 int EVP_PKEY_CTX_set_dh_paramgen_seed(EVP_PKEY_CTX *ctx,
@@ -74,31 +74,17 @@ int EVP_PKEY_CTX_set_dh_paramgen_seed(EVP_PKEY_CTX *ctx,
                                              (void *)seed, seedlen);
     *p = OSSL_PARAM_construct_end();
 
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_set_dh_paramgen_type(EVP_PKEY_CTX *ctx, int typ)
 {
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *name;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, typ, NULL);
-
-    name = dh_gen_type_id2name(typ);
-    if (name == NULL)
-        return 0;
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE,
-                                            (char *) name, 0);
-    *p = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
+                             EVP_PKEY_CTRL_DH_PARAMGEN_TYPE, typ, NULL);
 }
 
 int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits)
@@ -110,14 +96,9 @@ int EVP_PKEY_CTX_set_dh_paramgen_prime_len(EVP_PKEY_CTX *ctx, int pbits)
     if ((ret = dh_paramgen_check(ctx)) <= 0)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN, pbits,
-                                 NULL);
     *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_FFC_PBITS, &bits);
     *p = OSSL_PARAM_construct_end();
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
 int EVP_PKEY_CTX_set_dh_paramgen_subprime_len(EVP_PKEY_CTX *ctx, int qbits)
@@ -129,15 +110,10 @@ int EVP_PKEY_CTX_set_dh_paramgen_subprime_len(EVP_PKEY_CTX *ctx, int qbits)
     if ((ret = dh_paramgen_check(ctx)) <= 0)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN, qbits,
-                                 NULL);
     *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_FFC_QBITS, &bits2);
     *p = OSSL_PARAM_construct_end();
 
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
 int EVP_PKEY_CTX_set_dh_paramgen_generator(EVP_PKEY_CTX *ctx, int gen)
@@ -148,37 +124,20 @@ int EVP_PKEY_CTX_set_dh_paramgen_generator(EVP_PKEY_CTX *ctx, int gen)
     if ((ret = dh_paramgen_check(ctx)) <= 0)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR, gen, NULL);
     *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_DH_GENERATOR, &gen);
     *p = OSSL_PARAM_construct_end();
 
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_set_dh_rfc5114(EVP_PKEY_CTX *ctx, int gen)
 {
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *name;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_PARAMGEN,
-                                 EVP_PKEY_CTRL_DH_RFC5114, gen, NULL);
-    name = ossl_ffc_named_group_get_name(ossl_ffc_uid_to_dh_named_group(gen));
-    if (name == NULL)
-        return 0;
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
-                                            (void *)name, 0);
-    *p = OSSL_PARAM_construct_end();
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_PARAMGEN,
+                             EVP_PKEY_CTRL_DH_RFC5114, gen, NULL);
 }
 
 int EVP_PKEY_CTX_set_dhx_rfc5114(EVP_PKEY_CTX *ctx, int gen)
@@ -186,28 +145,17 @@ int EVP_PKEY_CTX_set_dhx_rfc5114(EVP_PKEY_CTX *ctx, int gen)
     return EVP_PKEY_CTX_set_dh_rfc5114(ctx, gen);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name or an
+ * ASN1_OBJECT (which can be converted to text internally)?
+ */
 int EVP_PKEY_CTX_set_dh_nid(EVP_PKEY_CTX *ctx, int nid)
 {
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *name;
-
-    if ((ret = dh_paramgen_check(ctx)) <= 0)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH,
-                                 EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
-                                 EVP_PKEY_CTRL_DH_NID, nid, NULL);
-    name = ossl_ffc_named_group_get_name(ossl_ffc_uid_to_dh_named_group(nid));
-    if (name == NULL)
-        return 0;
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
-                                            (void *)name, 0);
-    *p = OSSL_PARAM_construct_end();
-    return EVP_PKEY_CTX_set_params(ctx, params);
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH,
+                             EVP_PKEY_OP_PARAMGEN | EVP_PKEY_OP_KEYGEN,
+                             EVP_PKEY_CTRL_DH_NID, nid, NULL);
 }
 
 int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad)
@@ -221,241 +169,91 @@ int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad)
         return -2;
     }
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_PAD, pad, NULL);
-
     dh_pad_params[0] = OSSL_PARAM_construct_uint(OSSL_EXCHANGE_PARAM_PAD, &upad);
     dh_pad_params[1] = OSSL_PARAM_construct_end();
 
-    return EVP_PKEY_CTX_set_params(ctx, dh_pad_params);
+    return evp_pkey_ctx_set_params_strict(ctx, dh_pad_params);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
 int EVP_PKEY_CTX_set_dh_kdf_type(EVP_PKEY_CTX *ctx, int kdf)
 {
-    int ret;
-    const char *kdf_type;
-    OSSL_PARAM params[2], *p = params;
-
-    ret = dh_param_derive_check(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_KDF_TYPE, kdf, NULL);
-    switch (kdf) {
-        case EVP_PKEY_DH_KDF_NONE:
-            kdf_type = "";
-            break;
-        case EVP_PKEY_DH_KDF_X9_42:
-            kdf_type = OSSL_KDF_NAME_X942KDF_ASN1;
-            break;
-        default:
-            return -2;
-    }
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
-                                            /*
-                                             * Cast away the const. This is read
-                                             * only so should be safe
-                                             */
-                                            (char *)kdf_type, 0);
-    *p = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    return ret;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_DH_KDF_TYPE, kdf, NULL);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of getting a name?
+ */
 int EVP_PKEY_CTX_get_dh_kdf_type(EVP_PKEY_CTX *ctx)
 {
-    int ret;
-    char kdf_type[80]; /* 80 should be big enough */
-    OSSL_PARAM params[2], *p = params;
-
-    ret = dh_param_derive_check(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_KDF_TYPE, -2, NULL);
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
-                                            kdf_type, sizeof(kdf_type));
-    *p = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
-
-    if (kdf_type[0] == '\0')
-        return EVP_PKEY_DH_KDF_NONE;
-    else if (strcmp(kdf_type, OSSL_KDF_NAME_X942KDF_ASN1) == 0)
-        return EVP_PKEY_DH_KDF_X9_42;
-
-    return -1;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_DH_KDF_TYPE, -2, NULL);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_set0_dh_kdf_oid(EVP_PKEY_CTX *ctx, ASN1_OBJECT *oid)
 {
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *oid_name;
-
-    ret = dh_param_derive_check(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_KDF_OID, 0, (void *)(oid));
-    oid_name = OBJ_nid2sn(OBJ_obj2nid(oid));
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CEK_ALG,
-                                            (char *)oid_name, 0);
-    *p = OSSL_PARAM_construct_end();
-    ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    return ret;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_DH_KDF_OID, 0, (void *)(oid));
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_get0_dh_kdf_oid(EVP_PKEY_CTX *ctx, ASN1_OBJECT **oid)
 {
-    int ret, nid;
-    OSSL_PARAM params[2], *p = params;
-    char oid_name[80]; /* 80 should be big enough */
-
-    ret = dh_param_derive_check(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_GET_DH_KDF_OID, 0, (void *)(oid));
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_CEK_ALG,
-                                            oid_name, sizeof(oid_name));
-    *p = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
-    nid = OBJ_sn2nid(oid_name);
-    if (nid == NID_undef)
-        nid = OBJ_ln2nid(oid_name);
-    *oid = (nid == NID_undef ? NULL : OBJ_nid2obj(nid));
-    return *oid != NULL;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_GET_DH_KDF_OID, 0, (void *)(oid));
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
 int EVP_PKEY_CTX_set_dh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 {
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *md_name = NULL;
-
-    ret = dh_param_derive_check(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_KDF_MD, 0, (void *)(md));
-    md_name = (md == NULL) ? "" : EVP_MD_name(md);
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
-                                            /*
-                                             * Cast away the const. This is read
-                                             * only so should be safe
-                                             */
-                                            (char *)md_name, 0);
-    *p = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-    return ret;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_DH_KDF_MD, 0, (void *)(md));
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of getting a name?
+ */
 int EVP_PKEY_CTX_get_dh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
 {
-    int ret;
-    char name[80] = "";  /* 80 should be big enough */
-    OSSL_PARAM params[2], *p = params;
-
-    ret = dh_param_derive_check(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
         return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
                                  EVP_PKEY_CTRL_GET_DH_KDF_MD, 0, (void *)(pmd));
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
-                                            name, sizeof(name));
-    *p = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
-
-    /* May be NULL meaning "unknown" */
-    *pmd = EVP_get_digestbyname(name);
-
-    return 1;
 }
 
-int EVP_PKEY_CTX_set_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int inlen)
+int EVP_PKEY_CTX_set_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int outlen)
 {
     int ret;
-    size_t len = inlen;
+    size_t len = outlen;
     OSSL_PARAM params[2], *p = params;
 
     ret = dh_param_derive_check(ctx);
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_KDF_OUTLEN, inlen, NULL);
-    if (inlen <= 0) {
+    if (outlen <= 0) {
         /*
          * This would ideally be -1 or 0, but we have to retain compatibility
          * with legacy behaviour of EVP_PKEY_CTX_ctrl() which returned -2 if
-         * in <= 0
+         * inlen <= 0
          */
         return -2;
     }
@@ -465,11 +263,8 @@ int EVP_PKEY_CTX_set_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int inlen)
     *p = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
+    if (ret == -2)
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
     return ret;
 }
 
@@ -483,25 +278,14 @@ int EVP_PKEY_CTX_get_dh_kdf_outlen(EVP_PKEY_CTX *ctx, int *plen)
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN, 0,
-                                 (void *)(plen));
     *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
                                        &len);
     *p = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
+    if (ret == -2)
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
-
-    if (len > INT_MAX)
+    if (ret != 1 || len > INT_MAX)
         return -1;
 
     *plen = (int)len;
@@ -521,11 +305,6 @@ int EVP_PKEY_CTX_set0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len)
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_DH_KDF_UKM, len, (void *)(ukm));
-
     *p++ = OSSL_PARAM_construct_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM,
                                             /*
                                              * Cast away the const. This is read
@@ -536,11 +315,8 @@ int EVP_PKEY_CTX_set0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len)
     *p = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
+    if (ret == -2)
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
     if (ret == 1)
         OPENSSL_free(ukm);
     return ret;
@@ -556,23 +332,15 @@ int EVP_PKEY_CTX_get0_dh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **pukm)
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DHX, EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_GET_DH_KDF_UKM, 0, (void *)(pukm));
-
     *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_EXCHANGE_PARAM_KDF_UKM,
                                           (void **)pukm, 0);
     *p = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
+    if (ret == -2)
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
+    if (ret != 1)
         return -1;
-    }
 
     ukmlen = params[0].return_size;
     if (ukmlen > INT_MAX)
diff --git a/crypto/evp/ec_ctrl.c b/crypto/evp/ec_ctrl.c
index 9d35e5f6ba..17f1a8f288 100644
--- a/crypto/evp/ec_ctrl.c
+++ b/crypto/evp/ec_ctrl.c
@@ -32,7 +32,8 @@ int evp_pkey_ctx_getset_ecdh_param_checks(const EVP_PKEY_CTX *ctx)
     }
 
     /* If key type not EC return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_EC)
+    if (evp_pkey_ctx_is_legacy(ctx)
+        && ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_EC)
         return -1;
 
     return 1;
@@ -58,24 +59,13 @@ int EVP_PKEY_CTX_set_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx, int cofactor_mode)
         return -2;
     }
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_ECDH_COFACTOR,
-                                 cofactor_mode, NULL);
-
     *p++ = OSSL_PARAM_construct_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
                                     &cofactor_mode);
     *p++ = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
+    if (ret == -2)
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
     return ret;
 }
 
@@ -88,207 +78,87 @@ int EVP_PKEY_CTX_get_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx)
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_ECDH_COFACTOR, -2, NULL);
-
     *p++ = OSSL_PARAM_construct_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
                                     &mode);
     *p++ = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
 
-    if (mode < 0 || mode > 1) {
-        /*
-         * The provider should return either 0 or 1, any other value is a
-         * provider error.
-         */
-        return -1;
+    switch (ret) {
+    case -2:
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        break;
+    case 1:
+        ret = mode;
+        if (mode < 0 || mode > 1) {
+            /*
+             * The provider should return either 0 or 1, any other value is a
+             * provider error.
+             */
+            ret = -1;
+        }
+        break;
+    default:
+        ret = -1;
+        break;
     }
 
-    return mode;
+    return ret;
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_set_ecdh_kdf_type(EVP_PKEY_CTX *ctx, int kdf)
 {
-    int ret;
-    const char *kdf_type;
-    OSSL_PARAM params[2], *p = params;
-
-    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
-    if (ret != 1)
-        return ret;
-
-    switch (kdf) {
-        case EVP_PKEY_ECDH_KDF_NONE:
-            kdf_type = "";
-            break;
-        case EVP_PKEY_ECDH_KDF_X9_63:
-            kdf_type = OSSL_KDF_NAME_X963KDF;
-            break;
-        default:
-            return -2;
-    }
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_KDF_TYPE, kdf, NULL);
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
-                                            /*
-                                             * Cast away the const. This is read
-                                             * only so should be safe
-                                             */
-                                            (char *)kdf_type, 0);
-    *p++ = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    return ret;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_EC_KDF_TYPE, kdf, NULL);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_get_ecdh_kdf_type(EVP_PKEY_CTX *ctx)
 {
-    int ret;
-    /* 80 should be big enough */
-    char kdf_type[80];
-    OSSL_PARAM params[2], *p = params;
-
-    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_KDF_TYPE, -2, NULL);
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
-                                            kdf_type, sizeof(kdf_type));
-    *p++ = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
-
-    if (kdf_type[0] == '\0')
-        return EVP_PKEY_ECDH_KDF_NONE;
-    else if (strcmp(kdf_type, OSSL_KDF_NAME_X963KDF) == 0)
-        return EVP_PKEY_ECDH_KDF_X9_63;
-
-    return -1;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_EC_KDF_TYPE, -2, NULL);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
 int EVP_PKEY_CTX_set_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 {
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-    const char *md_name = NULL;
-
-    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_KDF_MD, 0, (void *)(md));
-
-    md_name = (md == NULL) ? "" : EVP_MD_name(md);
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
-                                            /*
-                                             * Cast away the const. This is read
-                                             * only so should be safe
-                                             */
-                                            (char *)md_name, 0);
-    *p++ = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-    return ret;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_EC_KDF_MD, 0, (void *)(md));
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of getting a name?
+ */
 int EVP_PKEY_CTX_get_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
 {
-    /* 80 should be big enough */
-    char name[80] = "";
-    int ret;
-    OSSL_PARAM params[2], *p = params;
-
-    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
-    if (ret != 1)
-        return ret;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_GET_EC_KDF_MD, 0, (void *)(pmd));
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
-                                            name, sizeof(name));
-    *p++ = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
-
-    /* May be NULL meaning "unknown" */
-    *pmd = EVP_get_digestbyname(name);
-
-    return 1;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_DERIVE,
+                             EVP_PKEY_CTRL_GET_EC_KDF_MD, 0, (void *)(pmd));
 }
 
-int EVP_PKEY_CTX_set_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int in)
+int EVP_PKEY_CTX_set_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int outlen)
 {
     int ret;
-    size_t len = in;
+    size_t len = outlen;
     OSSL_PARAM params[2], *p = params;
 
     ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_KDF_OUTLEN, in, NULL);
-
-    if (in <= 0) {
+    if (outlen <= 0) {
         /*
          * This would ideally be -1 or 0, but we have to retain compatibility
          * with legacy behaviour of EVP_PKEY_CTX_ctrl() which returned -2 if
@@ -302,11 +172,8 @@ int EVP_PKEY_CTX_set_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int in)
     *p++ = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
+    if (ret == -2)
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
     return ret;
 }
 
@@ -320,32 +187,28 @@ int EVP_PKEY_CTX_get_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int *plen)
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN, 0,
-                                 (void *)(plen));
-
     *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
                                        &len);
     *p++ = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
+
+    switch (ret) {
+    case -2:
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
+        break;
+    case 1:
+        if (len <= INT_MAX)
+            *plen = (int)len;
+        else
+            ret = -1;
+        break;
+    default:
+        ret = -1;
+        break;
     }
 
-    if (len > INT_MAX)
-        return -1;
-
-    *plen = (int)len;
-
-    return 1;
+    return ret;
 }
 
 int EVP_PKEY_CTX_set0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len)
@@ -357,12 +220,6 @@ int EVP_PKEY_CTX_set0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int le
     if (ret != 1)
         return ret;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.kex.exchprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_DERIVE,
-                                 EVP_PKEY_CTRL_EC_KDF_UKM, len, (void *)(ukm));
-
     *p++ = OSSL_PARAM_construct_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM,
                                             /*
                                              * Cast away the const. This is read
@@ -373,13 +230,16 @@ int EVP_PKEY_CTX_set0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int le
     *p++ = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_set_params_strict(ctx, params);
-    if (ret == -2) {
+
+    switch (ret) {
+    case -2:
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-    if (ret == 1)
+        break;
+    case 1:
         OPENSSL_free(ukm);
+        break;
+    }
+
     return ret;
 }
 
@@ -405,81 +265,46 @@ int EVP_PKEY_CTX_get0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **pukm)
     *p++ = OSSL_PARAM_construct_end();
 
     ret = evp_pkey_ctx_get_params_strict(ctx, params);
-    if (ret == -2) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    } else if (ret != 1) {
-        return -1;
-    }
 
-    ukmlen = params[0].return_size;
-    if (ukmlen > INT_MAX)
-        return -1;
-
-    return (int)ukmlen;
-}
-
-#ifndef FIPS_MODULE
-int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid)
-{
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
+    switch (ret) {
+    case -2:
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* Legacy: if key type not EC return error */
-    if (ctx->pmeth != NULL
-        && EVP_PKEY_type(ctx->pmeth->pkey_id) != EVP_PKEY_EC)
-        return -1;
-
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                 EVP_PKEY_OP_PARAMGEN|EVP_PKEY_OP_KEYGEN,
-                                 EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
-                                 nid, NULL);
-
-    return EVP_PKEY_CTX_set_group_name(ctx, OBJ_nid2sn(nid));
-}
-
-int evp_pkey_ctx_set_ec_param_enc_prov(EVP_PKEY_CTX *ctx, int param_enc)
-{
-    const char *enc = NULL;
-    OSSL_PARAM params[2], *p = params;
-    int ret = -2;                /* Assume unsupported */
-
-    if (ctx == NULL
-        || !EVP_PKEY_CTX_IS_GEN_OP(ctx)
-        || ctx->op.keymgmt.genctx == NULL)
-        goto end;
-
-    switch (param_enc) {
-    case OPENSSL_EC_EXPLICIT_CURVE:
-        enc = OSSL_PKEY_EC_ENCODING_EXPLICIT;
         break;
-    case OPENSSL_EC_NAMED_CURVE:
-        enc = OSSL_PKEY_EC_ENCODING_GROUP;
+    case 1:
+        ret = -1;
+        ukmlen = params[0].return_size;
+        if (ukmlen <= INT_MAX)
+            ret = (int)ukmlen;
         break;
     default:
-        goto end;
+        ret = -1;
+        break;
     }
 
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING,
-                                            (char *)enc, 0);
-    *p = OSSL_PARAM_construct_end();
-
-    ret = evp_pkey_ctx_set_params_strict(ctx, params);
- end:
-    if (ret == -2)
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
     return ret;
 }
 
+#ifndef FIPS_MODULE
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of setting a name or an
+ * ASN1_OBJECT (which would be converted to text internally)?
+ */
+int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid)
+{
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_TYPE_GEN,
+                             EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
+                             nid, NULL);
+}
+
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int param_enc)
 {
-    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                             EVP_PKEY_OP_PARAMGEN|EVP_PKEY_OP_KEYGEN,
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_TYPE_GEN,
                              EVP_PKEY_CTRL_EC_PARAM_ENC, param_enc, NULL);
 }
 #endif
diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c
index 427ffc813a..f6598a8b3f 100644
--- a/crypto/evp/evp_lib.c
+++ b/crypto/evp/evp_lib.c
@@ -983,32 +983,8 @@ int EVP_CIPHER_CTX_test_flags(const EVP_CIPHER_CTX *ctx, int flags)
 int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name)
 {
     OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END };
-    OSSL_PARAM *p = params;
-
-    if (ctx == NULL) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
 
-    if (!EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
-#ifndef FIPS_MODULE
-        int nid;
-
-        /* Could be a legacy key, try and convert to a ctrl */
-        if (ctx->pmeth != NULL && (nid = OBJ_txt2nid(name)) != NID_undef) {
-            if (ctx->pmeth->pkey_id == EVP_PKEY_DH)
-                return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_DH,
-                                         EVP_PKEY_OP_PARAMGEN
-                                         | EVP_PKEY_OP_KEYGEN,
-                                         EVP_PKEY_CTRL_DH_NID, nid, NULL);
-            if (ctx->pmeth->pkey_id == EVP_PKEY_EC)
-                return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
-                                         EVP_PKEY_OP_PARAMGEN|EVP_PKEY_OP_KEYGEN,
-                                         EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID,
-                                         nid, NULL);
-        }
-#endif
+    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
         /* Uses the same return values as EVP_PKEY_CTX_ctrl */
         return -2;
@@ -1017,8 +993,8 @@ int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name)
     if (name == NULL)
         return -1;
 
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
-                                            (char *)name, 0);
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 (char *)name, 0);
     return EVP_PKEY_CTX_set_params(ctx, params);
 }
 
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index e655adde05..653a3b7743 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -1228,60 +1228,8 @@ int EVP_PKEY_get_default_digest_name(EVP_PKEY *pkey,
 int EVP_PKEY_get_group_name(const EVP_PKEY *pkey, char *gname, size_t gname_sz,
                             size_t *gname_len)
 {
-    if (evp_pkey_is_legacy(pkey)) {
-        const char *name = NULL;
-
-        switch (EVP_PKEY_base_id(pkey)) {
-#ifndef OPENSSL_NO_EC
-        case EVP_PKEY_EC:
-            {
-                const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey));
-                int nid = NID_undef;
-
-                if (grp != NULL)
-                    nid = EC_GROUP_get_curve_name(grp);
-                if (nid != NID_undef)
-                    name = ec_curve_nid2name(nid);
-            }
-            break;
-#endif
-#ifndef OPENSSL_NO_DH
-        case EVP_PKEY_DH:
-            {
-                DH *dh = EVP_PKEY_get0_DH(pkey);
-                int uid = DH_get_nid(dh);
-
-                if (uid != NID_undef) {
-                    const DH_NAMED_GROUP *dh_group =
-                        ossl_ffc_uid_to_dh_named_group(uid);
-
-                    name = ossl_ffc_named_group_get_name(dh_group);
-                }
-            }
-            break;
-#endif
-        default:
-            break;
-        }
-
-        if (gname_len != NULL)
-            *gname_len = (name == NULL ? 0 : strlen(name));
-        if (name != NULL) {
-            if (gname != NULL)
-                OPENSSL_strlcpy(gname, name, gname_sz);
-            return 1;
-        }
-    } else if (evp_pkey_is_provided(pkey)) {
-        if (EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME,
-                                           gname, gname_sz, gname_len))
-            return 1;
-    } else {
-        ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_KEY);
-        return 0;
-    }
-
-    ERR_raise(ERR_LIB_EVP, EVP_R_UNSUPPORTED_KEY_TYPE);
-    return 0;
+    return EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME,
+                                          gname, gname_sz, gname_len);
 }
 
 int EVP_PKEY_supports_digest_nid(EVP_PKEY *pkey, int nid)
@@ -2144,7 +2092,7 @@ int EVP_PKEY_set_bn_param(EVP_PKEY *pkey, const char *key_name,
     if (key_name == NULL
         || bn == NULL
         || pkey == NULL
-        || !evp_pkey_is_provided(pkey))
+        || !evp_pkey_is_assigned(pkey))
         return 0;
 
     bsize = BN_num_bytes(bn);
@@ -2194,12 +2142,28 @@ const OSSL_PARAM *EVP_PKEY_settable_params(const EVP_PKEY *pkey)
 
 int EVP_PKEY_set_params(EVP_PKEY *pkey, OSSL_PARAM params[])
 {
-    if (pkey == NULL)
-        return 0;
-
-    pkey->dirty_cnt++;
-    return evp_pkey_is_provided(pkey)
-        && evp_keymgmt_set_params(pkey->keymgmt, pkey->keydata, params);
+    if (pkey != NULL) {
+        if (evp_pkey_is_provided(pkey)) {
+            pkey->dirty_cnt++;
+            return evp_keymgmt_set_params(pkey->keymgmt, pkey->keydata, params);
+        }
+#ifndef FIPS_MODULE
+        /*
+         * TODO?
+         * We will hopefully never find the need to set individual data in
+         * EVP_PKEYs with a legacy internal key, but we can't be entirely
+         * sure.  This bit of code can be enabled if we find the need.  If
+         * not, it can safely be removed when #legacy support is removed.
+         */
+# if 0
+        else if (evp_pkey_is_legacy(pkey)) {
+            return evp_pkey_set_params_to_ctrl(pkey, params);
+        }
+# endif
+#endif
+    }
+    ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_KEY);
+    return 0;
 }
 
 const OSSL_PARAM *EVP_PKEY_gettable_params(const EVP_PKEY *pkey)
@@ -2211,9 +2175,16 @@ const OSSL_PARAM *EVP_PKEY_gettable_params(const EVP_PKEY *pkey)
 
 int EVP_PKEY_get_params(const EVP_PKEY *pkey, OSSL_PARAM params[])
 {
-    return pkey != NULL
-        && evp_pkey_is_provided(pkey)
-        && evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params);
+    if (pkey != NULL) {
+        if (evp_pkey_is_provided(pkey))
+            return evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params);
+#ifndef FIPS_MODULE
+        else if (evp_pkey_is_legacy(pkey))
+            return evp_pkey_get_params_to_ctrl(pkey, params);
+#endif
+    }
+    ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_KEY);
+    return 0;
 }
 
 #ifndef FIPS_MODULE
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index a933752071..500e056479 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -135,12 +135,26 @@ EVP_PKEY_METHOD *EVP_PKEY_meth_new(int id, int flags)
     return pmeth;
 }
 
-/* Three possible states: */
-# define EVP_PKEY_STATE_UNKNOWN         0
-# define EVP_PKEY_STATE_LEGACY          1
-# define EVP_PKEY_STATE_PROVIDER        2
+static void help_get_legacy_alg_type_from_keymgmt(const char *keytype,
+                                                  void *arg)
+{
+    int *type = arg;
+
+    if (*type == NID_undef)
+        *type = evp_pkey_name2type(keytype);
+}
+
+static int get_legacy_alg_type_from_keymgmt(const EVP_KEYMGMT *keymgmt)
+{
+    int type = NID_undef;
+
+    EVP_KEYMGMT_names_do_all(keymgmt, help_get_legacy_alg_type_from_keymgmt,
+                             &type);
+    return type;
+}
+#endif /* FIPS_MODULE */
 
-static int evp_pkey_ctx_state(EVP_PKEY_CTX *ctx)
+int evp_pkey_ctx_state(const EVP_PKEY_CTX *ctx)
 {
     if (ctx->operation == EVP_PKEY_OP_UNDEFINED)
         return EVP_PKEY_STATE_UNKNOWN;
@@ -160,25 +174,6 @@ static int evp_pkey_ctx_state(EVP_PKEY_CTX *ctx)
     return EVP_PKEY_STATE_LEGACY;
 }
 
-static void help_get_legacy_alg_type_from_keymgmt(const char *keytype,
-                                                  void *arg)
-{
-    int *type = arg;
-
-    if (*type == NID_undef)
-        *type = evp_pkey_name2type(keytype);
-}
-
-static int get_legacy_alg_type_from_keymgmt(const EVP_KEYMGMT *keymgmt)
-{
-    int type = NID_undef;
-
-    EVP_KEYMGMT_names_do_all(keymgmt, help_get_legacy_alg_type_from_keymgmt,
-                             &type);
-    return type;
-}
-#endif /* FIPS_MODULE */
-
 static EVP_PKEY_CTX *int_ctx_new(OSSL_LIB_CTX *libctx,
                                  EVP_PKEY *pkey, ENGINE *e,
                                  const char *keytype, const char *propquery,
@@ -649,67 +644,94 @@ const EVP_PKEY_METHOD *EVP_PKEY_meth_get0(size_t idx)
 }
 #endif
 
+int EVP_PKEY_CTX_is_a(EVP_PKEY_CTX *ctx, const char *keytype)
+{
+#ifndef FIPS_MODULE
+    if (evp_pkey_ctx_is_legacy(ctx))
+        return (ctx->pmeth->pkey_id == evp_pkey_name2type(keytype));
+#endif
+    return EVP_KEYMGMT_is_a(ctx->keymgmt, keytype);
+}
+
 int EVP_PKEY_CTX_set_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 {
-    if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
-            && ctx->op.kex.exchprovctx != NULL
+    switch (evp_pkey_ctx_state(ctx)) {
+    case EVP_PKEY_STATE_PROVIDER:
+        if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
             && ctx->op.kex.exchange != NULL
             && ctx->op.kex.exchange->set_ctx_params != NULL)
-        return ctx->op.kex.exchange->set_ctx_params(ctx->op.kex.exchprovctx,
-                                                    params);
-    if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-            && ctx->op.sig.sigprovctx != NULL
+            return
+                ctx->op.kex.exchange->set_ctx_params(ctx->op.kex.exchprovctx,
+                                                     params);
+        if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
             && ctx->op.sig.signature != NULL
             && ctx->op.sig.signature->set_ctx_params != NULL)
-        return ctx->op.sig.signature->set_ctx_params(ctx->op.sig.sigprovctx,
-                                                     params);
-    if (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-            && ctx->op.ciph.ciphprovctx != NULL
+            return
+                ctx->op.sig.signature->set_ctx_params(ctx->op.sig.sigprovctx,
+                                                      params);
+        if (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
             && ctx->op.ciph.cipher != NULL
             && ctx->op.ciph.cipher->set_ctx_params != NULL)
-        return ctx->op.ciph.cipher->set_ctx_params(ctx->op.ciph.ciphprovctx,
-                                                     params);
-    if (EVP_PKEY_CTX_IS_GEN_OP(ctx)
-        && ctx->op.keymgmt.genctx != NULL
-        && ctx->keymgmt != NULL
-        && ctx->keymgmt->gen_set_params != NULL)
-        return evp_keymgmt_gen_set_params(ctx->keymgmt, ctx->op.keymgmt.genctx,
-                                          params);
-    if (EVP_PKEY_CTX_IS_KEM_OP(ctx)
-        && ctx->op.encap.kemprovctx != NULL
-        && ctx->op.encap.kem != NULL
-        && ctx->op.encap.kem->set_ctx_params != NULL)
-        return ctx->op.encap.kem->set_ctx_params(ctx->op.encap.kemprovctx,
-                                                 params);
+            return
+                ctx->op.ciph.cipher->set_ctx_params(ctx->op.ciph.ciphprovctx,
+                                                    params);
+        if (EVP_PKEY_CTX_IS_GEN_OP(ctx)
+            && ctx->keymgmt != NULL
+            && ctx->keymgmt->gen_set_params != NULL)
+            return
+                evp_keymgmt_gen_set_params(ctx->keymgmt, ctx->op.keymgmt.genctx,
+                                           params);
+        if (EVP_PKEY_CTX_IS_KEM_OP(ctx)
+            && ctx->op.encap.kem != NULL
+            && ctx->op.encap.kem->set_ctx_params != NULL)
+            return
+                ctx->op.encap.kem->set_ctx_params(ctx->op.encap.kemprovctx,
+                                                  params);
+        break;
+#ifndef FIPS_MODULE
+    case EVP_PKEY_STATE_UNKNOWN:
+    case EVP_PKEY_STATE_LEGACY:
+        return evp_pkey_ctx_set_params_to_ctrl(ctx, params);
+#endif
+    }
     return 0;
 }
 
 int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 {
-    if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
-            && ctx->op.kex.exchprovctx != NULL
+    switch (evp_pkey_ctx_state(ctx)) {
+    case EVP_PKEY_STATE_PROVIDER:
+        if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
             && ctx->op.kex.exchange != NULL
             && ctx->op.kex.exchange->get_ctx_params != NULL)
-        return ctx->op.kex.exchange->get_ctx_params(ctx->op.kex.exchprovctx,
-                                                    params);
-    if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-            && ctx->op.sig.sigprovctx != NULL
+            return
+                ctx->op.kex.exchange->get_ctx_params(ctx->op.kex.exchprovctx,
+                                                     params);
+        if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
             && ctx->op.sig.signature != NULL
             && ctx->op.sig.signature->get_ctx_params != NULL)
-        return ctx->op.sig.signature->get_ctx_params(ctx->op.sig.sigprovctx,
-                                                     params);
-    if (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-            && ctx->op.ciph.ciphprovctx != NULL
+            return
+                ctx->op.sig.signature->get_ctx_params(ctx->op.sig.sigprovctx,
+                                                      params);
+        if (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
             && ctx->op.ciph.cipher != NULL
             && ctx->op.ciph.cipher->get_ctx_params != NULL)
-        return ctx->op.ciph.cipher->get_ctx_params(ctx->op.ciph.ciphprovctx,
-                                                   params);
-    if (EVP_PKEY_CTX_IS_KEM_OP(ctx)
-        && ctx->op.encap.kemprovctx != NULL
-        && ctx->op.encap.kem != NULL
-        && ctx->op.encap.kem->get_ctx_params != NULL)
-        return ctx->op.encap.kem->get_ctx_params(ctx->op.encap.kemprovctx,
-                                                 params);
+            return
+                ctx->op.ciph.cipher->get_ctx_params(ctx->op.ciph.ciphprovctx,
+                                                    params);
+        if (EVP_PKEY_CTX_IS_KEM_OP(ctx)
+            && ctx->op.encap.kem != NULL
+            && ctx->op.encap.kem->get_ctx_params != NULL)
+            return
+                ctx->op.encap.kem->get_ctx_params(ctx->op.encap.kemprovctx,
+                                                  params);
+        break;
+#ifndef FIPS_MODULE
+    case EVP_PKEY_STATE_UNKNOWN:
+    case EVP_PKEY_STATE_LEGACY:
+        return evp_pkey_ctx_get_params_to_ctrl(ctx, params);
+#endif
+    }
     return 0;
 }
 
@@ -797,16 +819,24 @@ const OSSL_PARAM *EVP_PKEY_CTX_settable_params(EVP_PKEY_CTX *ctx)
  */
 int evp_pkey_ctx_set_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 {
-    const OSSL_PARAM *p;
-
     if (ctx == NULL || params == NULL)
         return 0;
 
-    for (p = params; p->key != NULL; p++) {
-        /* Check the ctx actually understands this parameter */
-        if (OSSL_PARAM_locate_const(EVP_PKEY_CTX_settable_params(ctx),
-                                    p->key) == NULL )
-            return -2;
+    /*
+     * We only check for provider side EVP_PKEY_CTX.  For #legacy, we
+     * depend on the translation that happens in EVP_PKEY_CTX_set_params()
+     * call, and that the resulting ctrl call will return -2 if it doesn't
+     * known the ctrl command number.
+     */
+    if (evp_pkey_ctx_is_provided(ctx)) {
+        const OSSL_PARAM *settable = EVP_PKEY_CTX_settable_params(ctx);
+        const OSSL_PARAM *p;
+
+        for (p = params; p->key != NULL; p++) {
+            /* Check the ctx actually understands this parameter */
+            if (OSSL_PARAM_locate_const(settable, p->key) == NULL )
+                return -2;
+        }
     }
 
     return EVP_PKEY_CTX_set_params(ctx, params);
@@ -814,16 +844,24 @@ int evp_pkey_ctx_set_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 
 int evp_pkey_ctx_get_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 {
-    const OSSL_PARAM *p;
-
     if (ctx == NULL || params == NULL)
         return 0;
 
-    for (p = params; p->key != NULL; p++ ) {
-        /* Check the ctx actually understands this parameter */
-        if (OSSL_PARAM_locate_const(EVP_PKEY_CTX_gettable_params(ctx),
-                                    p->key) == NULL )
-            return -2;
+    /*
+     * We only check for provider side EVP_PKEY_CTX.  For #legacy, we
+     * depend on the translation that happens in EVP_PKEY_CTX_get_params()
+     * call, and that the resulting ctrl call will return -2 if it doesn't
+     * known the ctrl command number.
+     */
+    if (evp_pkey_ctx_is_provided(ctx)) {
+        const OSSL_PARAM *gettable = EVP_PKEY_CTX_gettable_params(ctx);
+        const OSSL_PARAM *p;
+
+        for (p = params; p->key != NULL; p++ ) {
+            /* Check the ctx actually understands this parameter */
+            if (OSSL_PARAM_locate_const(gettable, p->key) == NULL )
+                return -2;
+        }
     }
 
     return EVP_PKEY_CTX_get_params(ctx, params);
@@ -1211,235 +1249,6 @@ int EVP_PKEY_CTX_get1_id_len(EVP_PKEY_CTX *ctx, size_t *id_len)
                              EVP_PKEY_CTRL_GET1_ID_LEN, 0, (void*)id_len);
 }
 
-static int legacy_ctrl_to_param(EVP_PKEY_CTX *ctx, int keytype, int optype,
-                                int cmd, int p1, void *p2)
-{
-    switch (cmd) {
-    case EVP_PKEY_CTRL_SET1_ID:
-        return evp_pkey_ctx_set1_id_prov(ctx, p2, p1);
-    case EVP_PKEY_CTRL_GET1_ID:
-        return evp_pkey_ctx_get1_id_prov(ctx, p2);
-    case EVP_PKEY_CTRL_GET1_ID_LEN:
-        return evp_pkey_ctx_get1_id_len_prov(ctx, p2);
-    }
-
-    if (keytype == EVP_PKEY_DHX) {
-        switch (cmd) {
-        case EVP_PKEY_CTRL_DH_KDF_TYPE:
-            return EVP_PKEY_CTX_set_dh_kdf_type(ctx, p1);
-        case EVP_PKEY_CTRL_DH_KDF_MD:
-            return EVP_PKEY_CTX_set_dh_kdf_md(ctx, p2);
-        case EVP_PKEY_CTRL_DH_KDF_OUTLEN:
-            return EVP_PKEY_CTX_set_dh_kdf_outlen(ctx, p1);
-        case EVP_PKEY_CTRL_DH_KDF_UKM:
-            return EVP_PKEY_CTX_set0_dh_kdf_ukm(ctx, p2, p1);
-        case EVP_PKEY_CTRL_DH_KDF_OID:
-            return EVP_PKEY_CTX_set0_dh_kdf_oid(ctx, p2);
-        case EVP_PKEY_CTRL_GET_DH_KDF_MD:
-            return EVP_PKEY_CTX_get_dh_kdf_md(ctx, p2);
-        case EVP_PKEY_CTRL_GET_DH_KDF_OUTLEN:
-            return EVP_PKEY_CTX_get_dh_kdf_outlen(ctx, p2);
-        case EVP_PKEY_CTRL_GET_DH_KDF_UKM:
-            return EVP_PKEY_CTX_get0_dh_kdf_ukm(ctx, p2);
-        case EVP_PKEY_CTRL_GET_DH_KDF_OID:
-            return EVP_PKEY_CTX_get0_dh_kdf_oid(ctx, p2);
-        }
-    }
-    if (keytype == EVP_PKEY_DH) {
-        switch (cmd) {
-            case EVP_PKEY_CTRL_DH_PAD:
-                return EVP_PKEY_CTX_set_dh_pad(ctx, p1);
-            case EVP_PKEY_CTRL_DH_PARAMGEN_PRIME_LEN:
-                return EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, p1);
-            case EVP_PKEY_CTRL_DH_PARAMGEN_SUBPRIME_LEN:
-                return EVP_PKEY_CTX_set_dh_paramgen_subprime_len(ctx, p1);
-            case EVP_PKEY_CTRL_DH_PARAMGEN_GENERATOR:
-                return EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, p1);
-            case EVP_PKEY_CTRL_DH_PARAMGEN_TYPE:
-                return EVP_PKEY_CTX_set_dh_paramgen_type(ctx, p1);
-            case EVP_PKEY_CTRL_DH_RFC5114:
-                return EVP_PKEY_CTX_set_dh_rfc5114(ctx, p1);
-        }
-    }
-    if (keytype == EVP_PKEY_DSA) {
-        switch (cmd) {
-        case EVP_PKEY_CTRL_DSA_PARAMGEN_BITS:
-            return EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, p1);
-        case EVP_PKEY_CTRL_DSA_PARAMGEN_Q_BITS:
-            return EVP_PKEY_CTX_set_dsa_paramgen_q_bits(ctx, p1);
-        case EVP_PKEY_CTRL_DSA_PARAMGEN_MD:
-            return EVP_PKEY_CTX_set_dsa_paramgen_md(ctx, p2);
-        }
-    }
-    if (keytype == EVP_PKEY_EC) {
-        switch (cmd) {
-        case EVP_PKEY_CTRL_EC_PARAM_ENC:
-            return evp_pkey_ctx_set_ec_param_enc_prov(ctx, p1);
-        case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID:
-            return EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, p1);
-        case EVP_PKEY_CTRL_EC_ECDH_COFACTOR:
-            if (p1 == -2) {
-                return EVP_PKEY_CTX_get_ecdh_cofactor_mode(ctx);
-            } else if (p1 < -1 || p1 > 1) {
-                /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-                return -2;
-            } else {
-                return EVP_PKEY_CTX_set_ecdh_cofactor_mode(ctx, p1);
-            }
-        case EVP_PKEY_CTRL_EC_KDF_TYPE:
-            if (p1 == -2) {
-                return EVP_PKEY_CTX_get_ecdh_kdf_type(ctx);
-            } else {
-                return EVP_PKEY_CTX_set_ecdh_kdf_type(ctx, p1);
-            }
-        case EVP_PKEY_CTRL_GET_EC_KDF_MD:
-            return EVP_PKEY_CTX_get_ecdh_kdf_md(ctx, p2);
-        case EVP_PKEY_CTRL_EC_KDF_MD:
-            return EVP_PKEY_CTX_set_ecdh_kdf_md(ctx, p2);
-        case EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN:
-            return EVP_PKEY_CTX_get_ecdh_kdf_outlen(ctx, p2);
-        case EVP_PKEY_CTRL_EC_KDF_OUTLEN:
-            return EVP_PKEY_CTX_set_ecdh_kdf_outlen(ctx, p1);
-        case EVP_PKEY_CTRL_GET_EC_KDF_UKM:
-            return EVP_PKEY_CTX_get0_ecdh_kdf_ukm(ctx, p2);
-        case EVP_PKEY_CTRL_EC_KDF_UKM:
-            return EVP_PKEY_CTX_set0_ecdh_kdf_ukm(ctx, p2, p1);
-        }
-    }
-    if (keytype == EVP_PKEY_RSA) {
-        switch (cmd) {
-        case EVP_PKEY_CTRL_RSA_OAEP_MD:
-            return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, p2);
-        case EVP_PKEY_CTRL_GET_RSA_OAEP_MD:
-            return EVP_PKEY_CTX_get_rsa_oaep_md(ctx, p2);
-        case EVP_PKEY_CTRL_RSA_MGF1_MD:
-            return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, p2);
-        case EVP_PKEY_CTRL_RSA_OAEP_LABEL:
-            return EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, p2, p1);
-        case EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL:
-            return EVP_PKEY_CTX_get0_rsa_oaep_label(ctx, (unsigned char **)p2);
-        case EVP_PKEY_CTRL_RSA_KEYGEN_BITS:
-            return EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, p1);
-        case EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP:
-            return EVP_PKEY_CTX_set_rsa_keygen_pubexp(ctx, p2);
-        case EVP_PKEY_CTRL_RSA_KEYGEN_PRIMES:
-            return EVP_PKEY_CTX_set_rsa_keygen_primes(ctx, p1);
-        }
-    }
-
-    if (keytype == EVP_PKEY_RSA_PSS) {
-      switch(cmd) {
-        case EVP_PKEY_CTRL_MD:
-          return EVP_PKEY_CTX_set_rsa_pss_keygen_md(ctx, p2);
-      }
-    }
-
-    /*
-     * keytype == -1 is used when several key types share the same structure,
-     * or for generic controls that are the same across multiple key types.
-     */
-    if (keytype == -1) {
-        if (optype == EVP_PKEY_OP_DERIVE) {
-            switch (cmd) {
-            /* TLS1-PRF */
-            case EVP_PKEY_CTRL_TLS_MD:
-                return EVP_PKEY_CTX_set_tls1_prf_md(ctx, p2);
-            case EVP_PKEY_CTRL_TLS_SECRET:
-                return EVP_PKEY_CTX_set1_tls1_prf_secret(ctx, p2, p1);
-            case EVP_PKEY_CTRL_TLS_SEED:
-                return EVP_PKEY_CTX_add1_tls1_prf_seed(ctx, p2, p1);
-
-            /* HKDF */
-            case EVP_PKEY_CTRL_HKDF_MD:
-                return EVP_PKEY_CTX_set_hkdf_md(ctx, p2);
-            case EVP_PKEY_CTRL_HKDF_SALT :
-                return EVP_PKEY_CTX_set1_hkdf_salt(ctx, p2, p1);
-            case EVP_PKEY_CTRL_HKDF_KEY:
-                return EVP_PKEY_CTX_set1_hkdf_key(ctx, p2, p1);
-            case EVP_PKEY_CTRL_HKDF_INFO:
-                return EVP_PKEY_CTX_add1_hkdf_info(ctx, p2, p1);
-            case EVP_PKEY_CTRL_HKDF_MODE:
-                return EVP_PKEY_CTX_hkdf_mode(ctx, p1);
-
-            /* Scrypt */
-            case EVP_PKEY_CTRL_PASS:
-                return EVP_PKEY_CTX_set1_pbe_pass(ctx, p2, p1);
-            case EVP_PKEY_CTRL_SCRYPT_SALT:
-                return EVP_PKEY_CTX_set1_scrypt_salt(ctx, p2, p1);
-            case EVP_PKEY_CTRL_SCRYPT_N:
-                return EVP_PKEY_CTX_set_scrypt_N(ctx, p1);
-            case EVP_PKEY_CTRL_SCRYPT_R:
-                return EVP_PKEY_CTX_set_scrypt_r(ctx, p1);
-            case EVP_PKEY_CTRL_SCRYPT_P:
-                return EVP_PKEY_CTX_set_scrypt_p(ctx, p1);
-            case EVP_PKEY_CTRL_SCRYPT_MAXMEM_BYTES:
-                return EVP_PKEY_CTX_set_scrypt_maxmem_bytes(ctx, p1);
-            }
-        } else if (optype == EVP_PKEY_OP_KEYGEN) {
-            OSSL_PARAM params[2], *p = params;
-
-            switch (cmd) {
-            case EVP_PKEY_CTRL_CIPHER:
-                {
-                    char *ciphname = (char *)EVP_CIPHER_name(p2);
-
-                    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_CIPHER,
-                                                            ciphname, 0);
-                    *p = OSSL_PARAM_construct_end();
-
-                    return EVP_PKEY_CTX_set_params(ctx, params);
-                }
-            case EVP_PKEY_CTRL_SET_MAC_KEY:
-                {
-                    *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY,
-                                                             p2, p1);
-                    *p = OSSL_PARAM_construct_end();
-
-                    return EVP_PKEY_CTX_set_params(ctx, params);
-                }
-            }
-        }
-        switch (cmd) {
-        case EVP_PKEY_CTRL_MD:
-            return EVP_PKEY_CTX_set_signature_md(ctx, p2);
-        case EVP_PKEY_CTRL_GET_MD:
-            return EVP_PKEY_CTX_get_signature_md(ctx, p2);
-        case EVP_PKEY_CTRL_RSA_PADDING:
-            return EVP_PKEY_CTX_set_rsa_padding(ctx, p1);
-        case EVP_PKEY_CTRL_GET_RSA_PADDING:
-            return EVP_PKEY_CTX_get_rsa_padding(ctx, p2);
-        case EVP_PKEY_CTRL_GET_RSA_MGF1_MD:
-            return EVP_PKEY_CTX_get_rsa_oaep_md(ctx, p2);
-        case EVP_PKEY_CTRL_RSA_PSS_SALTLEN:
-            return EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, p1);
-        case EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN:
-            return EVP_PKEY_CTX_get_rsa_pss_saltlen(ctx, p2);
-        case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
-        case EVP_PKEY_CTRL_PKCS7_DECRYPT:
-# ifndef OPENSSL_NO_CMS
-        case EVP_PKEY_CTRL_CMS_DECRYPT:
-        case EVP_PKEY_CTRL_CMS_ENCRYPT:
-# endif
-            /* TODO (3.0) Temporary hack, this should probe */
-            if (!EVP_PKEY_is_a(EVP_PKEY_CTX_get0_pkey(ctx), "RSASSA-PSS"))
-                return 1;
-            ERR_raise(ERR_LIB_EVP,
-                      EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-            return -2;
-        }
-    }
-
-    /*
-     * GOST CMS format is different for different cipher algorithms.
-     * Most of other algorithms don't have such a difference
-     * so this ctrl is just ignored.
-     */
-    if (cmd == EVP_PKEY_CTRL_CIPHER)
-        return -2;
-
-    return 0;
-}
-
 static int evp_pkey_ctx_ctrl_int(EVP_PKEY_CTX *ctx, int keytype, int optype,
                                  int cmd, int p1, void *p2)
 {
@@ -1464,7 +1273,7 @@ static int evp_pkey_ctx_ctrl_int(EVP_PKEY_CTX *ctx, int keytype, int optype,
 
     switch (evp_pkey_ctx_state(ctx)) {
     case EVP_PKEY_STATE_PROVIDER:
-        return legacy_ctrl_to_param(ctx, keytype, optype, cmd, p1, p2);
+        return evp_pkey_ctx_ctrl_to_param(ctx, keytype, optype, cmd, p1, p2);
     case EVP_PKEY_STATE_UNKNOWN:
     case EVP_PKEY_STATE_LEGACY:
         if (ctx->pmeth == NULL || ctx->pmeth->ctrl == NULL) {
@@ -1517,96 +1326,6 @@ int EVP_PKEY_CTX_ctrl_uint64(EVP_PKEY_CTX *ctx, int keytype, int optype,
     return EVP_PKEY_CTX_ctrl(ctx, keytype, optype, cmd, 0, &value);
 }
 
-static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
-                                    const char *value)
-{
-    if (strcmp(name, "md") == 0)
-        name = OSSL_ALG_PARAM_DIGEST;
-    else if (strcmp(name, "rsa_padding_mode") == 0)
-        name = OSSL_ASYM_CIPHER_PARAM_PAD_MODE;
-    else if (strcmp(name, "rsa_mgf1_md") == 0)
-        name = OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST;
-    else if (strcmp(name, "rsa_oaep_md") == 0)
-        name = OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST;
-    else if (strcmp(name, "rsa_oaep_label") == 0)
-        name = OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL;
-    else if (strcmp(name, "rsa_pss_saltlen") == 0)
-        name = OSSL_SIGNATURE_PARAM_PSS_SALTLEN;
-    else if (strcmp(name, "rsa_keygen_bits") == 0)
-        name = OSSL_PKEY_PARAM_RSA_BITS;
-    else if (strcmp(name, "rsa_keygen_pubexp") == 0)
-        name = OSSL_PKEY_PARAM_RSA_E;
-    else if (strcmp(name, "rsa_keygen_primes") == 0)
-        name = OSSL_PKEY_PARAM_RSA_PRIMES;
-    else if (strcmp(name, "rsa_pss_keygen_md") == 0)
-        name = OSSL_PKEY_PARAM_RSA_DIGEST;
-    else if (strcmp(name, "rsa_pss_keygen_mgf1_md") == 0)
-        name = OSSL_PKEY_PARAM_RSA_MGF1_DIGEST;
-    else if (strcmp(name, "rsa_pss_keygen_saltlen") == 0)
-        name = OSSL_PKEY_PARAM_RSA_PSS_SALTLEN;
-    else if (strcmp(name, "dsa_paramgen_bits") == 0)
-        name = OSSL_PKEY_PARAM_FFC_PBITS;
-    else if (strcmp(name, "dsa_paramgen_q_bits") == 0)
-        name = OSSL_PKEY_PARAM_FFC_QBITS;
-    else if (strcmp(name, "dsa_paramgen_md") == 0)
-        name = OSSL_PKEY_PARAM_FFC_DIGEST;
-    else if (strcmp(name, "dh_paramgen_generator") == 0)
-        name = OSSL_PKEY_PARAM_DH_GENERATOR;
-    else if (strcmp(name, "dh_paramgen_prime_len") == 0)
-        name = OSSL_PKEY_PARAM_FFC_PBITS;
-    else if (strcmp(name, "dh_paramgen_subprime_len") == 0)
-        name = OSSL_PKEY_PARAM_FFC_QBITS;
-    else if (strcmp(name, "dh_paramgen_type") == 0) {
-        name = OSSL_PKEY_PARAM_FFC_TYPE;
-        value = dh_gen_type_id2name(atoi(value));
-    } else if (strcmp(name, "dh_param") == 0)
-        name = OSSL_PKEY_PARAM_GROUP_NAME;
-    else if (strcmp(name, "dh_rfc5114") == 0) {
-        int num = atoi(value);
-
-        name = OSSL_PKEY_PARAM_GROUP_NAME;
-        value =
-            ossl_ffc_named_group_get_name(ossl_ffc_uid_to_dh_named_group(num));
-    } else if (strcmp(name, "dh_pad") == 0)
-        name = OSSL_EXCHANGE_PARAM_PAD;
-    else if (strcmp(name, "ec_paramgen_curve") == 0)
-        name = OSSL_PKEY_PARAM_GROUP_NAME;
-    else if (strcmp(name, "ecdh_cofactor_mode") == 0)
-        name = OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE;
-    else if (strcmp(name, "ecdh_kdf_md") == 0)
-        name = OSSL_EXCHANGE_PARAM_KDF_DIGEST;
-    else if (strcmp(name, "ec_param_enc") == 0)
-        name = OSSL_PKEY_PARAM_EC_ENCODING;
-    else if (strcmp(name, "N") == 0)
-        name = OSSL_KDF_PARAM_SCRYPT_N;
-
-    {
-        /*
-         * TODO(3.0) reduce the code above to only translate known legacy
-         * string to the corresponding core name (see core_names.h), but
-         * otherwise leave it to this code block to do the actual work.
-         */
-        const OSSL_PARAM *settable = EVP_PKEY_CTX_settable_params(ctx);
-        OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
-        int rv = 0;
-        int exists = 0;
-
-        if (!OSSL_PARAM_allocate_from_text(&params[0], settable, name, value,
-                                           strlen(value), &exists)) {
-            if (!exists) {
-                ERR_raise_data(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED,
-                               "name=%s,value=%s", name, value);
-                return -2;
-            }
-            return 0;
-        }
-        if (EVP_PKEY_CTX_set_params(ctx, params))
-            rv = 1;
-        OPENSSL_free(params[0].data);
-        return rv;
-    }
-}
-
 static int evp_pkey_ctx_ctrl_str_int(EVP_PKEY_CTX *ctx,
                                      const char *name, const char *value)
 {
@@ -1619,7 +1338,7 @@ static int evp_pkey_ctx_ctrl_str_int(EVP_PKEY_CTX *ctx,
 
     switch (evp_pkey_ctx_state(ctx)) {
     case EVP_PKEY_STATE_PROVIDER:
-        return legacy_ctrl_str_to_param(ctx, name, value);
+        return evp_pkey_ctx_ctrl_str_to_param(ctx, name, value);
     case EVP_PKEY_STATE_UNKNOWN:
     case EVP_PKEY_STATE_LEGACY:
         if (ctx == NULL || ctx->pmeth == NULL || ctx->pmeth->ctrl_str == NULL) {
@@ -1684,6 +1403,19 @@ static int evp_pkey_ctx_store_cached_data(EVP_PKEY_CTX *ctx,
                                           int cmd, const char *name,
                                           const void *data, size_t data_len)
 {
+    /*
+     * Check that it's one of the supported commands.  The ctrl commands
+     * number cases here must correspond to the cases in the bottom switch
+     * in this function.
+     */
+    switch (cmd = decode_cmd(cmd, name)) {
+    case EVP_PKEY_CTRL_SET1_ID:
+        break;
+    default:
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        return -2;
+    }
+
     if (keytype != -1) {
         switch (evp_pkey_ctx_state(ctx)) {
         case EVP_PKEY_STATE_PROVIDER:
@@ -1703,7 +1435,7 @@ static int evp_pkey_ctx_store_cached_data(EVP_PKEY_CTX *ctx,
                 ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
                 return -2;
             }
-            if (ctx->pmeth->pkey_id != keytype) {
+            if (EVP_PKEY_type(ctx->pmeth->pkey_id) != EVP_PKEY_type(keytype)) {
                 ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_OPERATION);
                 return -1;
             }
@@ -1715,7 +1447,6 @@ static int evp_pkey_ctx_store_cached_data(EVP_PKEY_CTX *ctx,
         return -1;
     }
 
-    cmd = decode_cmd(cmd, name);
     switch (cmd) {
     case EVP_PKEY_CTRL_SET1_ID:
         evp_pkey_ctx_free_cached_data(ctx, cmd, name);
@@ -1735,11 +1466,9 @@ static int evp_pkey_ctx_store_cached_data(EVP_PKEY_CTX *ctx,
         }
         ctx->cached_parameters.dist_id_set = 1;
         ctx->cached_parameters.dist_id_len = data_len;
-        return 1;
+        break;
     }
-
-    ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-    return -2;
+    return 1;
 }
 
 static void evp_pkey_ctx_free_cached_data(EVP_PKEY_CTX *ctx,
diff --git a/crypto/rsa/rsa_lib.c b/crypto/rsa/rsa_lib.c
index 6ca4f3a541..817372cbb7 100644
--- a/crypto/rsa/rsa_lib.c
+++ b/crypto/rsa/rsa_lib.c
@@ -838,399 +838,219 @@ int ossl_rsa_get0_all_params(RSA *r, STACK_OF(BIGNUM_const) *primes,
 }
 
 #ifndef FIPS_MODULE
-int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad_mode)
+/* Helpers to set or get diverse hash algorithm names */
+static int int_set_rsa_md_name(EVP_PKEY_CTX *ctx,
+                               /* For checks */
+                               int keytype, int optype,
+                               /* For EVP_PKEY_CTX_set_params() */
+                               const char *mdkey, const char *mdname,
+                               const char *propkey, const char *mdprops)
 {
-    OSSL_PARAM pad_params[2], *p = pad_params;
+    OSSL_PARAM params[3], *p = params;
 
-    if (ctx == NULL) {
+    if (ctx == NULL || mdname == NULL || (ctx->operation & optype) == 0) {
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
         /* Uses the same return values as EVP_PKEY_CTX_ctrl */
         return -2;
     }
 
-    /* If key type not RSA or RSA-PSS return error */
-    if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if ((!EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-         || ctx->op.ciph.ciphprovctx == NULL)
-        && (!EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-            || ctx->op.sig.sigprovctx == NULL))
-        return EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_RSA_PADDING,
-                                 pad_mode, NULL);
+    /* If key type not RSA return error */
+    switch (keytype) {
+    case -1:
+        if (!EVP_PKEY_CTX_is_a(ctx, "RSA")
+            && !EVP_PKEY_CTX_is_a(ctx, "RSA-PSS"))
+            return -1;
+        break;
+    default:
+        if (!EVP_PKEY_CTX_is_a(ctx, evp_pkey_type2name(keytype)))
+            return -1;
+        break;
+    }
 
-    *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_PAD_MODE, &pad_mode);
+    /* Cast away the const. This is read only so should be safe */
+    *p++ = OSSL_PARAM_construct_utf8_string(mdkey, (char *)mdname, 0);
+    if (evp_pkey_ctx_is_provided(ctx) && mdprops != NULL) {
+        /* Cast away the const. This is read only so should be safe */
+        *p++ = OSSL_PARAM_construct_utf8_string(propkey, (char *)mdprops, 0);
+    }
     *p++ = OSSL_PARAM_construct_end();
 
-    return EVP_PKEY_CTX_set_params(ctx, pad_params);
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
-int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *pad_mode)
+/* Helpers to set or get diverse hash algorithm names */
+static int int_get_rsa_md_name(EVP_PKEY_CTX *ctx,
+                               /* For checks */
+                               int keytype, int optype,
+                               /* For EVP_PKEY_CTX_get_params() */
+                               const char *mdkey,
+                               char *mdname, size_t mdnamesize)
 {
-    OSSL_PARAM pad_params[2], *p = pad_params;
+    OSSL_PARAM params[2], *p = params;
 
-    if (ctx == NULL || pad_mode == NULL) {
+    if (ctx == NULL || mdname == NULL || (ctx->operation & optype) == 0) {
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
         /* Uses the same return values as EVP_PKEY_CTX_ctrl */
         return -2;
     }
 
-    /* If key type not RSA or RSA-PSS return error */
-    if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if ((!EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-         || ctx->op.ciph.ciphprovctx == NULL)
-        && (!EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-            || ctx->op.sig.sigprovctx == NULL))
-        return EVP_PKEY_CTX_ctrl(ctx, -1, -1, EVP_PKEY_CTRL_GET_RSA_PADDING, 0,
-                                 pad_mode);
+    /* If key type not RSA return error */
+    switch (keytype) {
+    case -1:
+        if (!EVP_PKEY_CTX_is_a(ctx, "RSA")
+            && !EVP_PKEY_CTX_is_a(ctx, "RSA-PSS"))
+            return -1;
+        break;
+    default:
+        if (!EVP_PKEY_CTX_is_a(ctx, evp_pkey_type2name(keytype)))
+            return -1;
+        break;
+    }
 
-    *p++ = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_PAD_MODE, pad_mode);
+    /* Cast away the const. This is read only so should be safe */
+    *p++ = OSSL_PARAM_construct_utf8_string(mdkey, (char *)mdname, mdnamesize);
     *p++ = OSSL_PARAM_construct_end();
 
-    if (!EVP_PKEY_CTX_get_params(ctx, pad_params))
-        return 0;
+    return evp_pkey_ctx_get_params_strict(ctx, params);
+}
 
-    return 1;
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated?
+ */
+int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad_mode)
+{
+    return RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_RSA_PADDING,
+                             pad_mode, NULL);
+}
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated?
+ */
+int EVP_PKEY_CTX_get_rsa_padding(EVP_PKEY_CTX *ctx, int *pad_mode)
+{
+    return RSA_pkey_ctx_ctrl(ctx, -1, EVP_PKEY_CTRL_GET_RSA_PADDING,
+                             0, pad_mode);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
 int EVP_PKEY_CTX_set_rsa_pss_keygen_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 {
-    const char *name;
-
-    if (ctx == NULL || md == NULL) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL
-        && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-        && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN,
-                                 EVP_PKEY_CTRL_MD, 0, (void *)md);
-
-    name = EVP_MD_name(md);
-
-    return EVP_PKEY_CTX_set_rsa_pss_keygen_md_name(ctx, name, NULL);
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN,
+                             EVP_PKEY_CTRL_MD, 0, (void *)(md));
 }
 
 int EVP_PKEY_CTX_set_rsa_pss_keygen_md_name(EVP_PKEY_CTX *ctx,
                                             const char *mdname,
                                             const char *mdprops)
 {
-    OSSL_PARAM rsa_params[3], *p = rsa_params;
-
-    if (ctx == NULL || mdname == NULL) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL
-        && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-        && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
-
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST,
-                                            /*
-                                             * Cast away the const. This is read
-                                             * only so should be safe
-                                             */
-                                            (char *)mdname, 0);
-    if (mdprops != NULL) {
-        *p++ = OSSL_PARAM_construct_utf8_string(
-                    OSSL_PKEY_PARAM_RSA_DIGEST_PROPS,
-                    /*
-                     * Cast away the const. This is read only so should be safe
-                     */
-                    (char *)mdprops, 0);
-    }
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, rsa_params);
+    return int_set_rsa_md_name(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN,
+                               OSSL_PKEY_PARAM_RSA_DIGEST, mdname,
+                               OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, mdprops);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
 int EVP_PKEY_CTX_set_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 {
-    const char *name;
-
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.ciph.ciphprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
-                                 EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void *)md);
-
-    name = (md == NULL) ? "" : EVP_MD_name(md);
-
-    return EVP_PKEY_CTX_set_rsa_oaep_md_name(ctx, name, NULL);
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                             EVP_PKEY_CTRL_RSA_OAEP_MD, 0, (void *)(md));
 }
 
 int EVP_PKEY_CTX_set_rsa_oaep_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
                                       const char *mdprops)
 {
-    OSSL_PARAM rsa_params[3], *p = rsa_params;
-
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
-        return -1;
-
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST,
-                                            /*
-                                             * Cast away the const. This is read
-                                             * only so should be safe
-                                             */
-                                            (char *)mdname, 0);
-    if (mdprops != NULL) {
-        *p++ = OSSL_PARAM_construct_utf8_string(
-                    OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS,
-                    /*
-                     * Cast away the const. This is read
-                     * only so should be safe
-                     */
-                    (char *)mdprops, 0);
-    }
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, rsa_params);
+    return
+        int_set_rsa_md_name(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                            OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, mdname,
+                            OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, mdprops);
 }
 
 int EVP_PKEY_CTX_get_rsa_oaep_md_name(EVP_PKEY_CTX *ctx, char *name,
-                                      size_t namelen)
+                                      size_t namesize)
 {
-    OSSL_PARAM rsa_params[2], *p = rsa_params;
-
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
-        return -1;
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST,
-                                            name, namelen);
-    *p++ = OSSL_PARAM_construct_end();
-
-    if (!EVP_PKEY_CTX_get_params(ctx, rsa_params))
-        return -1;
-
-    return 1;
+    return int_get_rsa_md_name(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                               OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST,
+                               name, namesize);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of getting a name?
+ */
 int EVP_PKEY_CTX_get_rsa_oaep_md(EVP_PKEY_CTX *ctx, const EVP_MD **md)
 {
-    /* 80 should be big enough */
-    char name[80] = "";
-
-    if (ctx == NULL || md == NULL || !EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.ciph.ciphprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
-                                 EVP_PKEY_CTRL_GET_RSA_OAEP_MD, 0, (void *)md);
-
-    if (EVP_PKEY_CTX_get_rsa_oaep_md_name(ctx, name, sizeof(name)) <= 0)
-        return -1;
-
-    /* May be NULL meaning "unknown" */
-    *md = evp_get_digestbyname_ex(ctx->libctx, name);
-
-    return 1;
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
+                             EVP_PKEY_CTRL_GET_RSA_OAEP_MD, 0, (void *)md);
 }
 
-static int int_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx,
-                               /* For EVP_PKEY_CTX_ctrl() */
-                               int keytype, int optype, int cmd,
-                               const EVP_MD *md,
-                               /* For EVP_PKEY_CTX_set_params() */
-                               const char *mdname, const char *mdprops)
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
+int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 {
-    OSSL_PARAM rsa_params[3], *p = rsa_params;
-
-    if (ctx == NULL || (ctx->operation & optype) == 0) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL
-        && (keytype == -1
-            ? (ctx->pmeth->pkey_id != EVP_PKEY_RSA
-               && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-            : ctx->pmeth->pkey_id != keytype))
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (cmd != -1) {
-        if ((EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-             && ctx->op.ciph.ciphprovctx == NULL)
-            || (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-                && ctx->op.sig.sigprovctx == NULL)
-            || (EVP_PKEY_CTX_IS_GEN_OP(ctx)
-                && ctx->op.keymgmt.genctx == NULL))
-            return EVP_PKEY_CTX_ctrl(ctx, keytype, optype, cmd, 0, (void *)md);
-
-        mdname = (md == NULL) ? "" : EVP_MD_name(md);
-    }
-
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_MGF1_DIGEST,
-                                            /*
-                                             * Cast away the const. This is
-                                             * read only so should be safe
-                                             */
-                                            (char *)mdname, 0);
-    if (mdprops != NULL) {
-        *p++ =
-            OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_MGF1_PROPERTIES,
-                                             /*
-                                              * Cast away the const. This is
-                                              * read only so should be safe
-                                              */
-                                             (char *)mdprops, 0);
-    }
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, rsa_params);
+    return RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                             EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void *)(md));
 }
 
-int EVP_PKEY_CTX_set_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
+int EVP_PKEY_CTX_set_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
+                                      const char *mdprops)
 {
-    return int_set_rsa_mgf1_md(ctx, -1,
+    return int_set_rsa_md_name(ctx, -1,
                                EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
-                               EVP_PKEY_CTRL_RSA_MGF1_MD, md, NULL, NULL);
+                               OSSL_PKEY_PARAM_MGF1_DIGEST, mdname,
+                               OSSL_PKEY_PARAM_MGF1_PROPERTIES, mdprops);
 }
 
-int EVP_PKEY_CTX_set_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, const char *mdname,
-                                      const char *mdprops)
+int EVP_PKEY_CTX_get_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, char *name,
+                                      size_t namesize)
 {
-    return int_set_rsa_mgf1_md(ctx, -1,
+    return int_get_rsa_md_name(ctx, -1,
                                EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
-                               -1, NULL, mdname, mdprops);
+                               OSSL_PKEY_PARAM_MGF1_DIGEST, name, namesize);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of passing a name?
+ */
 int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 {
-    return int_set_rsa_mgf1_md(ctx, EVP_PKEY_RSA_PSS,
-                               EVP_PKEY_OP_KEYGEN, EVP_PKEY_CTRL_RSA_MGF1_MD,
-                               md, NULL, NULL);
+    return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN,
+                             EVP_PKEY_CTRL_RSA_MGF1_MD, 0, (void *)(md));
 }
 
 int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md_name(EVP_PKEY_CTX *ctx,
                                                  const char *mdname)
 {
-    return int_set_rsa_mgf1_md(ctx, EVP_PKEY_RSA_PSS,
-                               EVP_PKEY_OP_TYPE_CRYPT | EVP_PKEY_OP_TYPE_SIG,
-                               -1, NULL, mdname, NULL);
-}
-
-int EVP_PKEY_CTX_get_rsa_mgf1_md_name(EVP_PKEY_CTX *ctx, char *name,
-                                      size_t namelen)
-{
-    OSSL_PARAM rsa_params[2], *p = rsa_params;
-
-    if (ctx == NULL
-            || (!EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-                && !EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx))) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA or RSA-PSS return error */
-    if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
-
-    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_MGF1_DIGEST,
-                                            name, namelen);
-    *p++ = OSSL_PARAM_construct_end();
-
-    if (!EVP_PKEY_CTX_get_params(ctx, rsa_params))
-        return -1;
-
-    return 1;
+    return int_set_rsa_md_name(ctx, EVP_PKEY_RSA_PSS, EVP_PKEY_OP_KEYGEN,
+                               OSSL_PKEY_PARAM_MGF1_DIGEST, mdname,
+                               NULL, NULL);
 }
 
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ * TODO(3.0) Should this be deprecated in favor of getting a name?
+ */
 int EVP_PKEY_CTX_get_rsa_mgf1_md(EVP_PKEY_CTX *ctx, const EVP_MD **md)
 {
-    /* 80 should be big enough */
-    char name[80] = "";
-
-    if (ctx == NULL
-            || (!EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-                && !EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx))) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA or RSA-PSS return error */
-    if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if ((EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)
-                && ctx->op.ciph.ciphprovctx == NULL)
-            || (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-                && ctx->op.sig.sigprovctx == NULL))
-        return EVP_PKEY_CTX_ctrl(ctx, -1,
-                                 EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
-                                 EVP_PKEY_CTRL_GET_RSA_MGF1_MD, 0, (void *)md);
-
-    if (EVP_PKEY_CTX_get_rsa_mgf1_md_name(ctx, name, sizeof(name)) <= 0)
-        return -1;
-
-    /* May be NULL meaning "unknown" */
-    *md = evp_get_digestbyname_ex(ctx->libctx, name);
-
-    return 1;
+    return RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_TYPE_SIG | EVP_PKEY_OP_TYPE_CRYPT,
+                             EVP_PKEY_CTRL_GET_RSA_MGF1_MD, 0, (void *)(md));
 }
 
 int EVP_PKEY_CTX_set0_rsa_oaep_label(EVP_PKEY_CTX *ctx, void *label, int llen)
@@ -1244,27 +1064,20 @@ int EVP_PKEY_CTX_set0_rsa_oaep_label(EVP_PKEY_CTX *ctx, void *label, int llen)
     }
 
     /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
+    if (!EVP_PKEY_CTX_is_a(ctx, "RSA"))
         return -1;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.ciph.ciphprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
-                                 EVP_PKEY_CTRL_RSA_OAEP_LABEL, llen,
-                                 (void *)label);
+    /* TODO(3.0) Shouldn't a set0 translate into setting an OCTET_PTR? */
 
+    /* Cast away the const. This is read only so should be safe */
     *p++ = OSSL_PARAM_construct_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL,
-                                             /*
-                                              * Cast away the const. This is
-                                              * read only so should be safe
-                                              */
-                                             (void *)label,
-                                             (size_t)llen);
+                                             (void *)label, (size_t)llen);
     *p++ = OSSL_PARAM_construct_end();
 
-    if (!EVP_PKEY_CTX_set_params(ctx, rsa_params))
+    if (!evp_pkey_ctx_set_params_strict(ctx, rsa_params))
         return 0;
 
+    /* TODO(3.0) ????? */
     OPENSSL_free(label);
     return 1;
 }
@@ -1281,15 +1094,9 @@ int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx, unsigned char **label)
     }
 
     /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
+    if (!EVP_PKEY_CTX_is_a(ctx, "RSA"))
         return -1;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.ciph.ciphprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_TYPE_CRYPT,
-                                 EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL, 0,
-                                 (void *)label);
-
     *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL,
                                           (void **)label, 0);
     *p++ = OSSL_PARAM_construct_end();
@@ -1304,84 +1111,63 @@ int EVP_PKEY_CTX_get0_rsa_oaep_label(EVP_PKEY_CTX *ctx, unsigned char **label)
     return (int)labellen;
 }
 
-static int int_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen,
-                                   int keytype, int optype)
-{
-    OSSL_PARAM pad_params[2], *p = pad_params;
-
-    if (ctx == NULL || (ctx->operation & optype) == 0) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA or RSA-PSS return error */
-    if (ctx->pmeth != NULL
-        && (keytype == -1
-            ? (ctx->pmeth->pkey_id != EVP_PKEY_RSA
-               && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-            : ctx->pmeth->pkey_id != keytype))
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if ((EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-         && ctx->op.sig.sigprovctx == NULL)
-        || (EVP_PKEY_CTX_IS_GEN_OP(ctx)
-            && ctx->op.keymgmt.genctx == NULL))
-        return EVP_PKEY_CTX_ctrl(ctx, keytype, optype,
-                                 EVP_PKEY_CTRL_RSA_PSS_SALTLEN,
-                                 saltlen, NULL);
-
-    *p++ =
-        OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, &saltlen);
-    *p++ = OSSL_PARAM_construct_end();
-
-    return EVP_PKEY_CTX_set_params(ctx, pad_params);
-}
-
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
 int EVP_PKEY_CTX_set_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
 {
-    return int_set_rsa_pss_saltlen(ctx, saltlen, -1, EVP_PKEY_OP_TYPE_SIG);
+    /*
+     * For some reason, the optype was set to this:
+     *
+     * EVP_PKEY_OP_SIGN|EVP_PKEY_OP_VERIFY
+     *
+     * However, we do use RSA-PSS with the whole gamut of diverse signature
+     * and verification operations, so the optype gets upgraded to this:
+     *
+     * EVP_PKEY_OP_TYPE_SIG
+     */
+    return RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_TYPE_SIG,
+                             EVP_PKEY_CTRL_RSA_PSS_SALTLEN, saltlen, NULL);
 }
 
-int EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
+/*
+ * This one is currently implemented as an EVP_PKEY_CTX_ctrl() wrapper,
+ * simply because that's easier.
+ */
+int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *saltlen)
 {
-    return int_set_rsa_pss_saltlen(ctx, saltlen, EVP_PKEY_RSA_PSS,
-                                   EVP_PKEY_OP_KEYGEN);
+    /*
+     * Because of circumstances, the optype is updated from:
+     *
+     * EVP_PKEY_OP_SIGN|EVP_PKEY_OP_VERIFY
+     *
+     * to:
+     *
+     * EVP_PKEY_OP_TYPE_SIG
+     */
+    return RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_TYPE_SIG,
+                             EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN, 0, saltlen);
 }
 
-int EVP_PKEY_CTX_get_rsa_pss_saltlen(EVP_PKEY_CTX *ctx, int *saltlen)
+int EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen(EVP_PKEY_CTX *ctx, int saltlen)
 {
     OSSL_PARAM pad_params[2], *p = pad_params;
 
-    if (ctx == NULL || saltlen == NULL) {
+    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
         ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
         /* Uses the same return values as EVP_PKEY_CTX_ctrl */
         return -2;
     }
 
-    /* If key type not RSA or RSA-PSS return error */
-    if (ctx->pmeth != NULL
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA
-            && ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+    if (!EVP_PKEY_CTX_is_a(ctx, "RSA-PSS"))
         return -1;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (!EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
-        || ctx->op.sig.sigprovctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, -1, -1,
-                                 EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN,
-                                 0, saltlen);
-
-    *p++ =
-        OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, saltlen);
+    *p++ = OSSL_PARAM_construct_int(OSSL_SIGNATURE_PARAM_PSS_SALTLEN,
+                                    &saltlen);
     *p++ = OSSL_PARAM_construct_end();
 
-    if (!EVP_PKEY_CTX_get_params(ctx, pad_params))
-        return 0;
-
-    return 1;
-
+    return evp_pkey_ctx_set_params_strict(ctx, pad_params);
 }
 
 int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits)
@@ -1396,84 +1182,49 @@ int EVP_PKEY_CTX_set_rsa_keygen_bits(EVP_PKEY_CTX *ctx, int bits)
     }
 
     /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA &&
-        ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+    if (!EVP_PKEY_CTX_is_a(ctx, "RSA")
+        && !EVP_PKEY_CTX_is_a(ctx, "RSA-PSS"))
         return -1;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
-                                 EVP_PKEY_CTRL_RSA_KEYGEN_BITS, bits, NULL);
-
     *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_RSA_BITS, &bits2);
     *p++ = OSSL_PARAM_construct_end();
 
-    if (!EVP_PKEY_CTX_set_params(ctx, params))
-        return 0;
-
-    return 1;
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 
-static int evp_pkey_ctx_set_rsa_keygen_pubexp_intern(EVP_PKEY_CTX *ctx,
-                                                     BIGNUM *pubexp,
-                                                     int copy)
+int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp)
 {
-    OSSL_PARAM_BLD *tmpl;
-    OSSL_PARAM *params;
-    int ret;
-
-    if (ctx == NULL || !EVP_PKEY_CTX_IS_GEN_OP(ctx)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
-        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
-        return -2;
-    }
-
-    /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
-        return -1;
-
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL) {
-        if (copy == 1)
-            pubexp = BN_dup(pubexp);
-        ret = EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
+    int ret = RSA_pkey_ctx_ctrl(ctx, EVP_PKEY_OP_KEYGEN,
                                 EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP, 0, pubexp);
-        if ((copy == 1) && (ret <= 0))
-            BN_free(pubexp);
-        return ret;
-    }
-
-    if ((tmpl = OSSL_PARAM_BLD_new()) == NULL)
-        return 0;
-    if (!OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_RSA_E, pubexp)
-        || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) {
-        OSSL_PARAM_BLD_free(tmpl);
-        return 0;
-    }
-    OSSL_PARAM_BLD_free(tmpl);
-
-    ret = EVP_PKEY_CTX_set_params(ctx, params);
-    OSSL_PARAM_BLD_free_params(params);
 
     /*
      * Satisfy memory semantics for pre-3.0 callers of
      * EVP_PKEY_CTX_set_rsa_keygen_pubexp(): their expectation is that input
      * pubexp BIGNUM becomes managed by the EVP_PKEY_CTX on success.
      */
-    if ((copy == 0) && (ret > 0))
+    if (ret > 0 && evp_pkey_ctx_is_provided(ctx)) {
+        BN_free(ctx->rsa_pubexp);
         ctx->rsa_pubexp = pubexp;
+    }
 
     return ret;
 }
 
-int EVP_PKEY_CTX_set_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp)
-{
-    return evp_pkey_ctx_set_rsa_keygen_pubexp_intern(ctx, pubexp, 0);
-}
-
 int EVP_PKEY_CTX_set1_rsa_keygen_pubexp(EVP_PKEY_CTX *ctx, BIGNUM *pubexp)
 {
-    return evp_pkey_ctx_set_rsa_keygen_pubexp_intern(ctx, pubexp, 1);
+    int ret = 0;
+
+    /*
+     * When we're dealing with a provider, there's no need to duplicate
+     * pubexp, as it gets copied when transforming to an OSSL_PARAM anyway.
+     */
+    if (evp_pkey_ctx_is_legacy(ctx))
+        pubexp = BN_dup(pubexp);
+    ret = EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
+                            EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP, 0, pubexp);
+    if (evp_pkey_ctx_is_legacy(ctx) && ret <= 0)
+        BN_free(pubexp);
+    return ret;
 }
 
 int EVP_PKEY_CTX_set_rsa_keygen_primes(EVP_PKEY_CTX *ctx, int primes)
@@ -1488,21 +1239,13 @@ int EVP_PKEY_CTX_set_rsa_keygen_primes(EVP_PKEY_CTX *ctx, int primes)
     }
 
     /* If key type not RSA return error */
-    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_RSA)
+    if (!EVP_PKEY_CTX_is_a(ctx, "RSA")
+        && !EVP_PKEY_CTX_is_a(ctx, "RSA-PSS"))
         return -1;
 
-    /* TODO(3.0): Remove this eventually when no more legacy */
-    if (ctx->op.keymgmt.genctx == NULL)
-        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_RSA, EVP_PKEY_OP_KEYGEN,
-                                 EVP_PKEY_CTRL_RSA_KEYGEN_PRIMES, primes,
-                                 NULL);
-
     *p++ = OSSL_PARAM_construct_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, &primes2);
     *p++ = OSSL_PARAM_construct_end();
 
-    if (!EVP_PKEY_CTX_set_params(ctx, params))
-        return 0;
-
-    return 1;
+    return evp_pkey_ctx_set_params_strict(ctx, params);
 }
 #endif
diff --git a/doc/man3/EVP_PKEY_CTX_new.pod b/doc/man3/EVP_PKEY_CTX_new.pod
index 3342386d94..cb203dbd71 100644
--- a/doc/man3/EVP_PKEY_CTX_new.pod
+++ b/doc/man3/EVP_PKEY_CTX_new.pod
@@ -3,7 +3,8 @@
 =head1 NAME
 
 EVP_PKEY_CTX_new, EVP_PKEY_CTX_new_id, EVP_PKEY_CTX_new_from_name,
-EVP_PKEY_CTX_new_from_pkey, EVP_PKEY_CTX_dup, EVP_PKEY_CTX_free
+EVP_PKEY_CTX_new_from_pkey, EVP_PKEY_CTX_dup, EVP_PKEY_CTX_free,
+EVP_PKEY_CTX_is_a
 - public key algorithm context functions
 
 =head1 SYNOPSIS
@@ -20,6 +21,7 @@ EVP_PKEY_CTX_new_from_pkey, EVP_PKEY_CTX_dup, EVP_PKEY_CTX_free
                                           const char *propquery);
  EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *ctx);
  void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
+ int EVP_PKEY_CTX_is_a(EVP_PKEY_CTX *ctx, const char *keytype);
 
 =head1 DESCRIPTION
 
@@ -53,6 +55,8 @@ keygen operation.
 EVP_PKEY_CTX_free() frees up the context I<ctx>.
 If I<ctx> is NULL, nothing is done.
 
+EVP_PKEY_is_a() checks if the key type associated with I<ctx> is I<keytype>.
+
 =head1 NOTES
 
 =head2 On B<EVP_PKEY_CTX>
@@ -102,6 +106,8 @@ the newly allocated B<EVP_PKEY_CTX> structure or B<NULL> if an error occurred.
 
 EVP_PKEY_CTX_free() does not return a value.
 
+EVP_PKEY_CTX_is_a() returns 1 for true and 0 for false.
+
 =head1 SEE ALSO
 
 L<EVP_PKEY_new(3)>
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 7f28edd6c2..0ed9a02396 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -23,7 +23,7 @@
 #define EVP_MD_CTX_FLAG_KEEP_PKEY_CTX   0x0400
 
 /*
- * An EVP_PKEY can have the following support states:
+ * An EVP_PKEY_CTX can have the following support states:
  *
  * Supports legacy implementations only:
  *
@@ -598,8 +598,13 @@ DEFINE_STACK_OF(OP_CACHE_ELEM)
     ((pk)->type == EVP_PKEY_NONE && (pk)->keymgmt == NULL)
 #define evp_pkey_is_typed(pk)                                   \
     ((pk)->type != EVP_PKEY_NONE || (pk)->keymgmt != NULL)
-#define evp_pkey_is_assigned(pk)                                \
+#ifndef FIPS_MODULE
+# define evp_pkey_is_assigned(pk)                               \
     ((pk)->pkey.ptr != NULL || (pk)->keydata != NULL)
+#else
+# define evp_pkey_is_assigned(pk)                               \
+    ((pk)->keydata != NULL)
+#endif
 #define evp_pkey_is_legacy(pk)                                  \
     ((pk)->type != EVP_PKEY_NONE && (pk)->keymgmt == NULL)
 #define evp_pkey_is_provided(pk)                                \
@@ -700,6 +705,9 @@ struct evp_pkey_st {
     ((ctx)->operation == EVP_PKEY_OP_PARAMGEN \
      || (ctx)->operation == EVP_PKEY_OP_KEYGEN)
 
+#define EVP_PKEY_CTX_IS_FROMDATA_OP(ctx) \
+    ((ctx)->operation == EVP_PKEY_OP_FROMDATA)
+
 #define EVP_PKEY_CTX_IS_KEM_OP(ctx) \
     ((ctx)->operation == EVP_PKEY_OP_ENCAPSULATE \
      || (ctx)->operation == EVP_PKEY_OP_DECAPSULATE)
@@ -858,4 +866,24 @@ EVP_PKEY *evp_privatekey_from_binary(int keytype, EVP_PKEY **a,
                                      const unsigned char **pp, long length,
                                      OSSL_LIB_CTX *libctx, const char *propq);
 
+/* Three possible states: */
+# define EVP_PKEY_STATE_UNKNOWN         0
+# define EVP_PKEY_STATE_LEGACY          1
+# define EVP_PKEY_STATE_PROVIDER        2
+int evp_pkey_ctx_state(const EVP_PKEY_CTX *ctx);
+
+/* These two must ONLY be called for provider side operations */
+int evp_pkey_ctx_ctrl_to_param(EVP_PKEY_CTX *ctx,
+                               int keytype, int optype,
+                               int cmd, int p1, void *p2);
+int evp_pkey_ctx_ctrl_str_to_param(EVP_PKEY_CTX *ctx,
+                                   const char *name, const char *value);
+
+/* These two must ONLY be called for legacy operations */
+int evp_pkey_ctx_set_params_to_ctrl(EVP_PKEY_CTX *ctx, OSSL_PARAM *params);
+int evp_pkey_ctx_get_params_to_ctrl(EVP_PKEY_CTX *ctx, OSSL_PARAM *params);
+
+/* This must ONLY be called for legacy EVP_PKEYs */
+int evp_pkey_get_params_to_ctrl(const EVP_PKEY *pkey, OSSL_PARAM *params);
+
 #endif /* OSSL_CRYPTO_EVP_H */
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index bdce18c5ee..1bf244322e 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1649,6 +1649,7 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_new_from_pkey(OSSL_LIB_CTX *libctx,
                                          EVP_PKEY *pkey, const char *propquery);
 EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *ctx);
 void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx);
+int EVP_PKEY_CTX_is_a(EVP_PKEY_CTX *ctx, const char *keytype);
 
 int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params);
 const OSSL_PARAM *EVP_PKEY_CTX_gettable_params(EVP_PKEY_CTX *ctx);
diff --git a/util/libcrypto.num b/util/libcrypto.num
index b602ee4978..a16b6e17eb 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5300,3 +5300,4 @@ EVP_PKEY_fromdata_init                  ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_fromdata_settable              ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_param_check_quick              ?	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_public_check_quick             ?	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_CTX_is_a                       ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list