]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
EVP: Implement data-driven translation between known ctrl and OSSL_PARAMs
authorRichard Levitte <levitte@openssl.org>
Wed, 20 Jan 2021 22:04:53 +0000 (23:04 +0100)
committerRichard Levitte <levitte@openssl.org>
Tue, 23 Feb 2021 12:41:48 +0000 (13:41 +0100)
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@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/13913)

crypto/evp/build.info
crypto/evp/ctrl_params_translate.c [new file with mode: 0644]
include/crypto/evp.h

index 4b3057873fbc57f76068628e16fb8eb2aeb1cf5c..34551df4a3d26f896874cae91461cbdb143a24a7 100644 (file)
@@ -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 (file)
index 0000000..84a55e4
--- /dev/null
@@ -0,0 +1,2685 @@
+/*
+ * 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);
+}
+
index b0e82f6c818f4dbf3a1349e3cb10ed36ca0abc79..0ed9a02396552129b14590a1e8a5b17d0a84e2c7 100644 (file)
@@ -705,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)
@@ -869,4 +872,18 @@ EVP_PKEY *evp_privatekey_from_binary(int keytype, EVP_PKEY **a,
 # 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 */