[openssl] master update

Richard Levitte levitte at openssl.org
Wed Mar 25 16:03:57 UTC 2020


The branch master has been updated
       via  8158cf209792f7a92f0812ac89a9f54950e8453b (commit)
       via  0abae1636d7054266dd20724c0d5e06617d9f679 (commit)
       via  ff7262b4f4dfade7d2d6e05dcd3727ecc2bc7a5c (commit)
       via  acb90ba8ffe6a27f625607760e82842673eb9378 (commit)
       via  8243d8d1a17b700c9c48fc5660ff61245b1d14d2 (commit)
       via  adc9f7312665f14ec5c73b60090a4df933e6556d (commit)
      from  5036dc67d0f61a5c62ed3c45405648e7dc0d4d0a (commit)


- Log -----------------------------------------------------------------
commit 8158cf209792f7a92f0812ac89a9f54950e8453b
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Mar 23 06:03:16 2020 +0100

    EVP: Limit the diverse key parameter functions to domain params only
    
    Provider KEYMGMT functions can handle domain parameters as well as
    "other" parameters (the cofactor mode flag in ECC keys is one of
    those).  The public EVP functions EVP_PKEY_copy_parameters(),
    EVP_PKEY_missing_parameters(), EVP_PKEY_cmp_parameters() and
    EVP_PKEY_cmp() tried to handle all parameters, but looking back at
    EVP_PKEY_ASN1_METHOD code (especially crypto/ec/ec_ameth.c), it turns
    out that they only need to concern themselves with domain parameters.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11375)

commit 0abae1636d7054266dd20724c0d5e06617d9f679
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Mar 23 05:40:47 2020 +0100

    EVP: Implement support for key downgrading in backends
    
    Downgrading EVP_PKEYs from containing provider side internal keys to
    containing legacy keys demands support in the EVP_PKEY_ASN1_METHOD.
    
    This became a bit elaborate because the code would be almost exactly
    the same as the import functions int EVP_KEYMGMT.  Therefore, we end
    up moving most of the code to common backend support files that can be
    used both by legacy backend code and by our providers.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11375)

commit ff7262b4f4dfade7d2d6e05dcd3727ecc2bc7a5c
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Mar 21 06:26:41 2020 +0100

    test/evp_pkey_provided_test.c: Add test of EVP_PKEY_copy_parameters()
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11375)

commit acb90ba8ffe6a27f625607760e82842673eb9378
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Mar 21 06:21:26 2020 +0100

    EVP: Downgrade keys rather than upgrade
    
    Upgrading EVP_PKEYs from containing legacy keys to containing provider
    side keys proved to be risky, with a number of unpleasant corner
    cases, and with functions like EVP_PKEY_get0_DSA() failing
    unexpectedly.
    
    We therefore change course, and instead of upgrading legacy internal
    keys to provider side internal keys, we downgrade provider side
    internal keys to legacy ones.  To be able to do this, we add
    |import_from| and make it a callback function designed for
    evp_keymgmt_export().
    
    This means that evp_pkey_upgrade_to_provider() is replaced with
    evp_pkey_downgrade().
    
    EVP_PKEY_copy_parameters() is the most deeply affected function of
    this change.
    
    Fixes #11366
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11375)

commit 8243d8d1a17b700c9c48fc5660ff61245b1d14d2
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Mar 21 06:14:25 2020 +0100

    EVP: Add EVP_PKEY_set_type_by_keymgmt() and use it
    
    This function intialises an EVP_PKEY to contain a provider side internal
    key.
    
    We take the opportunity to also document the older EVP_PKEY_set_type()
    and EVP_PKEY_set_type_str().
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11375)

commit adc9f7312665f14ec5c73b60090a4df933e6556d
Author: Richard Levitte <levitte at openssl.org>
Date:   Sat Mar 21 06:03:39 2020 +0100

    EVP: Clarify the states of an EVP_PKEY
    
    EVP_PKEY is rather complex, even before provider side keys entered the
    stage.
    You could have untyped / unassigned keys (pk->type == EVP_PKEY_NONE),
    keys that had been assigned a type but no data (pk->pkey.ptr == NULL),
    and fully assigned keys (pk->type != EVP_PKEY_NONE && pk->pkey.ptr != NULL).
    
    For provider side keys, the corresponding states weren't well defined,
    and the code didn't quite account for all the possibilities.
    
    We also guard most of the legacy fields in EVP_PKEY with FIPS_MODE, so
    they don't exist at all in the FIPS module.
    
    Most of all, code needs to adapt to the case where an EVP_PKEY's
    |keymgmt| is non-NULL, but its |keydata| is NULL.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/11375)

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

Summary of changes:
 CHANGES.md                                    |  11 +
 crypto/dh/build.info                          |   2 +-
 crypto/dh/dh_ameth.c                          |  21 +
 crypto/dh/dh_backend.c                        |  56 +++
 crypto/dsa/build.info                         |   2 +-
 crypto/dsa/dsa_ameth.c                        |  24 +-
 crypto/dsa/dsa_backend.c                      |  57 +++
 crypto/ec/build.info                          |   2 +-
 crypto/ec/ec_ameth.c                          |  23 +-
 crypto/ec/ec_backend.c                        | 229 +++++++++++
 crypto/ec/ecx_backend.c                       |  62 +++
 crypto/ec/ecx_backend.h                       |  20 +
 crypto/ec/ecx_meth.c                          |  64 ++-
 crypto/err/openssl.txt                        |   5 +
 crypto/evp/evp_err.c                          |   7 +
 crypto/evp/keymgmt_lib.c                      | 144 ++++---
 crypto/evp/p_lib.c                            | 535 ++++++++++++++++----------
 crypto/evp/pmeth_check.c                      |  49 ++-
 crypto/evp/pmeth_gn.c                         |   8 +
 crypto/evp/pmeth_lib.c                        |   2 +-
 crypto/ffc/build.info                         |   2 +-
 crypto/ffc/ffc_backend.c                      |  44 +++
 crypto/rsa/build.info                         |   2 +-
 crypto/rsa/rsa_ameth.c                        |  24 +-
 crypto/rsa/rsa_backend.c                      |  95 +++++
 doc/man3/EVP_PKEY_set_type.pod                |  68 ++++
 include/crypto/asn1.h                         |   5 +-
 include/crypto/dh.h                           |   2 +
 include/crypto/dsa.h                          |   2 +
 include/crypto/ec.h                           |   8 +
 include/crypto/ecx.h                          |   5 +
 include/crypto/evp.h                          |  42 +-
 include/crypto/rsa.h                          |   2 +
 include/internal/ffc.h                        |   3 +
 include/openssl/evp.h                         |   1 +
 include/openssl/evperr.h                      |  11 +-
 providers/implementations/keymgmt/dh_kmgmt.c  |  89 +----
 providers/implementations/keymgmt/dsa_kmgmt.c |  91 +----
 providers/implementations/keymgmt/ec_kmgmt.c  | 248 +-----------
 providers/implementations/keymgmt/ecx_kmgmt.c |  57 +--
 providers/implementations/keymgmt/rsa_kmgmt.c |  95 +----
 test/evp_pkey_provided_test.c                 |  29 +-
 test/keymgmt_internal_test.c                  |  12 +-
 util/libcrypto.num                            |   1 +
 util/missingcrypto.txt                        |   2 -
 45 files changed, 1431 insertions(+), 832 deletions(-)
 create mode 100644 crypto/dh/dh_backend.c
 create mode 100644 crypto/dsa/dsa_backend.c
 create mode 100644 crypto/ec/ec_backend.c
 create mode 100644 crypto/ec/ecx_backend.c
 create mode 100644 crypto/ec/ecx_backend.h
 create mode 100644 crypto/ffc/ffc_backend.c
 create mode 100644 crypto/rsa/rsa_backend.c
 create mode 100644 doc/man3/EVP_PKEY_set_type.pod

diff --git a/CHANGES.md b/CHANGES.md
index d2aaec9fbe..82c186a6cd 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -24,6 +24,17 @@ OpenSSL 3.0
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx] ###
 
+ * EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(), EVP_PKEY_get0_DH(), and
+   EVP_PKEY_get0_EC_KEY() can now handle EVP_PKEYs with provider side
+   internal keys, if they correspond to one of those built in types.
+
+   *Richard Levitte*
+
+ * Added EVP_PKEY_set_type_by_keymgmt(), to initialise an EVP_PKEY to
+   contain a provider side internal key.
+
+   *Richard Levitte*
+
  * `ASN1_verify()`, `ASN1_digest()` and `ASN1_sign()` have been deprecated.
    They are old functions that we don't use, and that you could disable with
    the macro `NO_ASN1_OLD`.  This goes all the way back to OpenSSL 0.9.7.
diff --git a/crypto/dh/build.info b/crypto/dh/build.info
index 56c085bb1e..ce0918e7d3 100644
--- a/crypto/dh/build.info
+++ b/crypto/dh/build.info
@@ -1,6 +1,6 @@
 LIBS=../../libcrypto
 
-$COMMON=dh_lib.c dh_key.c dh_group_params.c dh_check.c
+$COMMON=dh_lib.c dh_key.c dh_group_params.c dh_check.c dh_backend.c
 
 SOURCE[../../libcrypto]=$COMMON\
         dh_asn1.c dh_gen.c dh_err.c dh_depr.c \
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index 877a66f9dc..d0eaceccb4 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -25,6 +25,7 @@
 #include <openssl/cms.h>
 #include <openssl/core_names.h>
 #include "internal/param_build.h"
+#include "internal/ffc.h"
 
 /*
  * i2d/d2i like DH parameter functions which use the appropriate routine for
@@ -543,6 +544,25 @@ static int dh_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     return rv;
 }
 
+static int dh_pkey_import_from(const OSSL_PARAM params[], void *key)
+{
+    EVP_PKEY *pkey = key;
+    DH *dh = DH_new();
+
+    if (dh == NULL) {
+        ERR_raise(ERR_LIB_DH, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!ffc_fromdata(dh_get0_params(dh), params)
+        || !dh_key_fromdata(dh, params)
+        || !EVP_PKEY_assign_DH(pkey, dh)) {
+        DH_free(dh);
+        return 0;
+    }
+    return 1;
+}
+
 const EVP_PKEY_ASN1_METHOD dh_asn1_meth = {
     EVP_PKEY_DH,
     EVP_PKEY_DH,
@@ -585,6 +605,7 @@ const EVP_PKEY_ASN1_METHOD dh_asn1_meth = {
 
     dh_pkey_dirty_cnt,
     dh_pkey_export_to,
+    dh_pkey_import_from,
 };
 
 const EVP_PKEY_ASN1_METHOD dhx_asn1_meth = {
diff --git a/crypto/dh/dh_backend.c b/crypto/dh/dh_backend.c
new file mode 100644
index 0000000000..bbeb096d55
--- /dev/null
+++ b/crypto/dh/dh_backend.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 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
+ */
+
+#include <openssl/core_names.h>
+#include "crypto/dh.h"
+
+/*
+ * The intention with the "backend" source file is to offer backend functions
+ * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
+ * implementations alike.
+ */
+
+int dh_key_fromdata(DH *dh, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *param_priv_key, *param_pub_key;
+    BIGNUM *priv_key = NULL, *pub_key = NULL;
+
+    if (dh == NULL)
+        return 0;
+
+    param_priv_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+    param_pub_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+
+    /*
+     * DH documentation says that a public key must be present if a
+     * private key is present.
+     * We want to have at least a public key either way, so we end up
+     * requiring it unconditionally.
+     */
+    if (param_priv_key != NULL && param_pub_key == NULL)
+        return 0;
+
+    if ((param_priv_key != NULL
+         && !OSSL_PARAM_get_BN(param_priv_key, &priv_key))
+        || (param_pub_key != NULL
+            && !OSSL_PARAM_get_BN(param_pub_key, &pub_key)))
+        goto err;
+
+    if (!DH_set0_key(dh, pub_key, priv_key))
+        goto err;
+
+    return 1;
+
+ err:
+    BN_clear_free(priv_key);
+    BN_free(pub_key);
+    return 0;
+}
diff --git a/crypto/dsa/build.info b/crypto/dsa/build.info
index 35a95a2be1..d8f035000d 100644
--- a/crypto/dsa/build.info
+++ b/crypto/dsa/build.info
@@ -1,7 +1,7 @@
 LIBS=../../libcrypto
 
 $COMMON=dsa_sign.c dsa_vrf.c dsa_lib.c dsa_ossl.c dsa_aid.c dsa_check.c \
-        dsa_key.c
+        dsa_key.c dsa_backend.c
 
 SOURCE[../../libcrypto]=$COMMON\
         dsa_gen.c dsa_asn1.c \
diff --git a/crypto/dsa/dsa_ameth.c b/crypto/dsa/dsa_ameth.c
index 92134f956b..53daf33d30 100644
--- a/crypto/dsa/dsa_ameth.c
+++ b/crypto/dsa/dsa_ameth.c
@@ -21,8 +21,10 @@
 #include <openssl/core_names.h>
 #include "internal/cryptlib.h"
 #include "crypto/asn1.h"
+#include "crypto/dsa.h"
 #include "crypto/evp.h"
 #include "internal/param_build.h"
+#include "internal/ffc.h"
 #include "dsa_local.h"
 
 static int dsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey)
@@ -569,6 +571,25 @@ static int dsa_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     return rv;
 }
 
+static int dsa_pkey_import_from(const OSSL_PARAM params[], void *key)
+{
+    EVP_PKEY *pkey = key;
+    DSA *dsa = DSA_new();
+
+    if (dsa == NULL) {
+        ERR_raise(ERR_LIB_DSA, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!ffc_fromdata(dsa_get0_params(dsa), params)
+        || !dsa_key_fromdata(dsa, params)
+        || !EVP_PKEY_assign_DSA(pkey, dsa)) {
+        DSA_free(dsa);
+        return 0;
+    }
+    return 1;
+}
+
 /* NB these are sorted in pkey_id order, lowest first */
 
 const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[5] = {
@@ -632,6 +653,7 @@ const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[5] = {
      NULL, NULL, NULL, NULL,
 
      dsa_pkey_dirty_cnt,
-     dsa_pkey_export_to
+     dsa_pkey_export_to,
+     dsa_pkey_import_from
     }
 };
diff --git a/crypto/dsa/dsa_backend.c b/crypto/dsa/dsa_backend.c
new file mode 100644
index 0000000000..b927465cfa
--- /dev/null
+++ b/crypto/dsa/dsa_backend.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2020 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
+ */
+
+#include <openssl/core_names.h>
+#include "crypto/dsa.h"
+
+/*
+ * The intention with the "backend" source file is to offer backend support
+ * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
+ * implementations alike.
+ */
+
+int dsa_key_fromdata(DSA *dsa, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *param_priv_key, *param_pub_key;
+    BIGNUM *priv_key = NULL, *pub_key = NULL;
+
+    if (dsa == NULL)
+        return 0;
+
+    param_priv_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+    param_pub_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+
+    /* It's ok if neither half is present */
+    if (param_priv_key == NULL && param_pub_key == NULL)
+        return 1;
+
+    /*
+     * DH documentation says that a public key must be present if a
+     * private key is present.
+     */
+    if (param_priv_key != NULL && param_pub_key == NULL)
+        return 0;
+
+    if (param_pub_key != NULL && !OSSL_PARAM_get_BN(param_pub_key, &pub_key))
+        goto err;
+    if (param_priv_key != NULL && !OSSL_PARAM_get_BN(param_priv_key, &priv_key))
+        goto err;
+
+    if (!DSA_set0_key(dsa, pub_key, priv_key))
+        goto err;
+
+    return 1;
+
+ err:
+    BN_clear_free(priv_key);
+    BN_free(pub_key);
+    return 0;
+}
diff --git a/crypto/ec/build.info b/crypto/ec/build.info
index f70543dd00..4494ce7a66 100644
--- a/crypto/ec/build.info
+++ b/crypto/ec/build.info
@@ -51,7 +51,7 @@ $COMMON=ec_lib.c ecp_smpl.c ecp_mont.c ecp_nist.c ec_cvt.c ec_mult.c \
         ecdsa_ossl.c ecdsa_sign.c ecdsa_vrf.c curve25519.c \
         curve448/arch_32/f_impl.c curve448/f_generic.c curve448/scalar.c \
         curve448/curve448_tables.c curve448/eddsa.c curve448/curve448.c \
-        $ECASM ecdsa_aid.c
+        $ECASM ecdsa_aid.c ec_backend.c ecx_backend.c
 SOURCE[../../libcrypto]=$COMMON ec_ameth.c ec_pmeth.c ecx_meth.c ecx_key.c \
                         ec_err.c ecdh_kdf.c eck_prn.c ec_evp_lib.c
 SOURCE[../../providers/libfips.a]=$COMMON
diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
index 944fc05835..f3812e46b5 100644
--- a/crypto/ec/ec_ameth.c
+++ b/crypto/ec/ec_ameth.c
@@ -744,6 +744,26 @@ int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     return rv;
 }
 
+static int ec_pkey_import_from(const OSSL_PARAM params[], void *key)
+{
+    EVP_PKEY *pkey = key;
+    EC_KEY *ec = EC_KEY_new();
+
+    if (ec == NULL) {
+        ERR_raise(ERR_LIB_DH, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!ec_key_domparams_fromdata(ec, params)
+        || !ec_key_otherparams_fromdata(ec, params)
+        || !ec_key_fromdata(ec, params, 1)
+        || !EVP_PKEY_assign_EC_KEY(pkey, ec)) {
+        EC_KEY_free(ec);
+        return 0;
+    }
+    return 1;
+}
+
 const EVP_PKEY_ASN1_METHOD eckey_asn1_meth = {
     EVP_PKEY_EC,
     EVP_PKEY_EC,
@@ -789,7 +809,8 @@ const EVP_PKEY_ASN1_METHOD eckey_asn1_meth = {
     0, /* get_pub_key */
 
     ec_pkey_dirty_cnt,
-    ec_pkey_export_to
+    ec_pkey_export_to,
+    ec_pkey_import_from
 };
 
 #if !defined(OPENSSL_NO_SM2)
diff --git a/crypto/ec/ec_backend.c b/crypto/ec/ec_backend.c
new file mode 100644
index 0000000000..b4520a7c60
--- /dev/null
+++ b/crypto/ec/ec_backend.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2020 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
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/objects.h>
+#include <openssl/params.h>
+#include "crypto/bn.h"
+#include "crypto/ec.h"
+
+/*
+ * The intention with the "backend" source file is to offer backend support
+ * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
+ * implementations alike.
+ */
+
+int ec_set_param_ecdh_cofactor_mode(EC_KEY *ec, const OSSL_PARAM *p)
+{
+    const EC_GROUP *ecg = EC_KEY_get0_group(ec);
+    const BIGNUM *cofactor;
+    int mode;
+
+    if (!OSSL_PARAM_get_int(p, &mode))
+        return 0;
+
+    /*
+     * mode can be only 0 for disable, or 1 for enable here.
+     *
+     * This is in contrast with the same parameter on an ECDH EVP_PKEY_CTX that
+     * also supports mode == -1 with the meaning of "reset to the default for
+     * the associated key".
+     */
+    if (mode < 0 || mode > 1)
+        return 0;
+
+    if ((cofactor = EC_GROUP_get0_cofactor(ecg)) == NULL )
+        return 0;
+
+    /* ECDH cofactor mode has no effect if cofactor is 1 */
+    if (BN_is_one(cofactor))
+        return 1;
+
+    if (mode == 1)
+        EC_KEY_set_flags(ec, EC_FLAG_COFACTOR_ECDH);
+    else if (mode == 0)
+        EC_KEY_clear_flags(ec, EC_FLAG_COFACTOR_ECDH);
+
+    return 1;
+}
+
+/*
+ * Callers of ec_key_fromdata MUST make sure that ec_key_params_fromdata has
+ * been called before!
+ *
+ * This function only gets the bare keypair, domain parameters and other
+ * parameters are treated separately, and domain parameters are required to
+ * define a keypair.
+ */
+int ec_key_fromdata(EC_KEY *ec, const OSSL_PARAM params[], int include_private)
+{
+    const OSSL_PARAM *param_priv_key, *param_pub_key;
+    BN_CTX *ctx = NULL;
+    BIGNUM *priv_key = NULL;
+    unsigned char *pub_key = NULL;
+    size_t pub_key_len;
+    const EC_GROUP *ecg = NULL;
+    EC_POINT *pub_point = NULL;
+    int ok = 0;
+
+    ecg = EC_KEY_get0_group(ec);
+    if (ecg == NULL)
+        return 0;
+
+    param_priv_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+    param_pub_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+
+    ctx = BN_CTX_new_ex(ec_key_get_libctx(ec));
+    if (ctx == NULL)
+        goto err;
+    /*
+     * We want to have at least a public key either way, so we end up
+     * requiring it unconditionally.
+     */
+    if (param_pub_key == NULL
+            || !OSSL_PARAM_get_octet_string(param_pub_key,
+                                            (void **)&pub_key, 0, &pub_key_len)
+            || (pub_point = EC_POINT_new(ecg)) == NULL
+            || !EC_POINT_oct2point(ecg, pub_point,
+                                   pub_key, pub_key_len, ctx))
+        goto err;
+
+    if (param_priv_key != NULL && include_private) {
+        int fixed_words;
+        const BIGNUM *order;
+
+        /*
+         * Key import/export should never leak the bit length of the secret
+         * scalar in the key.
+         *
+         * For this reason, on export we use padded BIGNUMs with fixed length.
+         *
+         * When importing we also should make sure that, even if short lived,
+         * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
+         * soon as possible, so that any processing of this BIGNUM might opt for
+         * constant time implementations in the backend.
+         *
+         * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
+         * to preallocate the BIGNUM internal buffer to a fixed public size big
+         * enough that operations performed during the processing never trigger
+         * a realloc which would leak the size of the scalar through memory
+         * accesses.
+         *
+         * Fixed Length
+         * ------------
+         *
+         * The order of the large prime subgroup of the curve is our choice for
+         * a fixed public size, as that is generally the upper bound for
+         * generating a private key in EC cryptosystems and should fit all valid
+         * secret scalars.
+         *
+         * For padding on export we just use the bit length of the order
+         * converted to bytes (rounding up).
+         *
+         * For preallocating the BIGNUM storage we look at the number of "words"
+         * required for the internal representation of the order, and we
+         * preallocate 2 extra "words" in case any of the subsequent processing
+         * might temporarily overflow the order length.
+         */
+        order = EC_GROUP_get0_order(ecg);
+        if (order == NULL || BN_is_zero(order))
+            goto err;
+
+        fixed_words = bn_get_top(order) + 2;
+
+        if ((priv_key = BN_secure_new()) == NULL)
+            goto err;
+        if (bn_wexpand(priv_key, fixed_words) == NULL)
+            goto err;
+        BN_set_flags(priv_key, BN_FLG_CONSTTIME);
+
+        if (!OSSL_PARAM_get_BN(param_priv_key, &priv_key))
+            goto err;
+    }
+
+    if (priv_key != NULL
+            && !EC_KEY_set_private_key(ec, priv_key))
+        goto err;
+
+    if (!EC_KEY_set_public_key(ec, pub_point))
+        goto err;
+
+    ok = 1;
+
+ err:
+    BN_CTX_free(ctx);
+    BN_clear_free(priv_key);
+    OPENSSL_free(pub_key);
+    EC_POINT_free(pub_point);
+    return ok;
+}
+
+int ec_key_domparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *param_ec_name;
+    EC_GROUP *ecg = NULL;
+    char *curve_name = NULL;
+    int ok = 0;
+
+    if (ec == NULL)
+        return 0;
+
+    param_ec_name = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME);
+    if (param_ec_name == NULL) {
+        /* explicit parameters */
+
+        /*
+         * TODO(3.0): should we support explicit parameters curves?
+         */
+        return 0;
+    } else {
+        /* named curve */
+        int curve_nid;
+
+        if (!OSSL_PARAM_get_utf8_string(param_ec_name, &curve_name, 0)
+                || curve_name == NULL
+                || (curve_nid = ec_curve_name2nid(curve_name)) == NID_undef)
+            goto err;
+
+        if ((ecg = EC_GROUP_new_by_curve_name_ex(ec_key_get_libctx(ec),
+                                                 curve_nid)) == NULL)
+            goto err;
+    }
+
+    if (!EC_KEY_set_group(ec, ecg))
+        goto err;
+
+    /*
+     * TODO(3.0): if the group has changed, should we invalidate the private and
+     * public key?
+     */
+
+    ok = 1;
+
+ err:
+    OPENSSL_free(curve_name);
+    EC_GROUP_free(ecg);
+    return ok;
+}
+
+int ec_key_otherparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *p;
+
+    if (ec == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH);
+    if (p != NULL && !ec_set_param_ecdh_cofactor_mode(ec, p))
+        return 0;
+
+    return 1;
+}
diff --git a/crypto/ec/ecx_backend.c b/crypto/ec/ecx_backend.c
new file mode 100644
index 0000000000..e613337029
--- /dev/null
+++ b/crypto/ec/ecx_backend.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2020 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
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include "crypto/ecx.h"
+#include "ecx_backend.h"
+
+/*
+ * The intention with the "backend" source file is to offer backend support
+ * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
+ * implementations alike.
+ */
+
+int ecx_key_fromdata(ECX_KEY *ecx, const OSSL_PARAM params[],
+                     int include_private)
+{
+    size_t privkeylen = 0, pubkeylen;
+    const OSSL_PARAM *param_priv_key = NULL, *param_pub_key;
+    unsigned char *pubkey;
+
+    if (ecx == NULL)
+        return 0;
+
+    param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+    if (include_private)
+        param_priv_key =
+            OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+    /*
+     * If a private key is present then a public key must also be present.
+     * Alternatively we've just got a public key.
+     */
+    if (param_pub_key == NULL)
+        return 0;
+
+    if (param_priv_key != NULL
+        && !OSSL_PARAM_get_octet_string(param_priv_key,
+                                        (void **)&ecx->privkey, ecx->keylen,
+                                        &privkeylen))
+        return 0;
+
+    pubkey = ecx->pubkey;
+    if (!OSSL_PARAM_get_octet_string(param_pub_key,
+                                     (void **)&pubkey,
+                                     sizeof(ecx->pubkey), &pubkeylen))
+        return 0;
+
+    if (pubkeylen != ecx->keylen
+        || (param_priv_key != NULL && privkeylen != ecx->keylen))
+        return 0;
+
+    ecx->haspubkey = 1;
+
+    return 1;
+}
+
diff --git a/crypto/ec/ecx_backend.h b/crypto/ec/ecx_backend.h
new file mode 100644
index 0000000000..50ece17abb
--- /dev/null
+++ b/crypto/ec/ecx_backend.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2020 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
+ */
+
+#define ISX448(id)      ((id) == EVP_PKEY_X448)
+#define IS25519(id)     ((id) == EVP_PKEY_X25519 || (id) == EVP_PKEY_ED25519)
+#define KEYLENID(id)    (IS25519(id) ? X25519_KEYLEN \
+                                     : ((id) == EVP_PKEY_X448 ? X448_KEYLEN \
+                                                              : ED448_KEYLEN))
+#define KEYNID2TYPE(id) \
+    (IS25519(id) ?  ECX_KEY_TYPE_X25519 \
+                 : ((id) == EVP_PKEY_X448 ? ECX_KEY_TYPE_X448 \
+                                          : ((id) == EVP_PKEY_ED25519 ? ECX_KEY_TYPE_ED25519 \
+                                                                      : ECX_KEY_TYPE_ED448)))
+#define KEYLEN(p)       KEYLENID((p)->ameth->pkey_id)
diff --git a/crypto/ec/ecx_meth.c b/crypto/ec/ecx_meth.c
index 9b9536f022..97d1b13f5a 100644
--- a/crypto/ec/ecx_meth.c
+++ b/crypto/ec/ecx_meth.c
@@ -25,19 +25,7 @@
 #include "crypto/ecx.h"
 #include "ec_local.h"
 #include "curve448/curve448_local.h"
-
-#define ISX448(id)      ((id) == EVP_PKEY_X448)
-#define IS25519(id)     ((id) == EVP_PKEY_X25519 || (id) == EVP_PKEY_ED25519)
-#define KEYLENID(id)    (IS25519(id) ? X25519_KEYLEN \
-                                     : ((id) == EVP_PKEY_X448 ? X448_KEYLEN \
-                                                              : ED448_KEYLEN))
-#define KEYNID2TYPE(id) \
-    (IS25519(id) ?  ECX_KEY_TYPE_X25519 \
-                 : ((id) == EVP_PKEY_X448 ? ECX_KEY_TYPE_X448 \
-                                          : ((id) == EVP_PKEY_ED25519 ? ECX_KEY_TYPE_ED25519 \
-                                                                      : ECX_KEY_TYPE_ED448)))
-#define KEYLEN(p)       KEYLENID((p)->ameth->pkey_id)
-
+#include "ecx_backend.h"
 
 typedef enum {
     KEY_OP_PUBLIC,
@@ -452,6 +440,30 @@ static int ecx_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     return rv;
 }
 
+static int ecx_generic_import_from(const OSSL_PARAM params[], void *key,
+                                   int keytype)
+{
+    EVP_PKEY *pkey = key;
+    ECX_KEY *ecx = ecx_key_new(KEYNID2TYPE(keytype), 0);
+
+    if (ecx == NULL) {
+        ERR_raise(ERR_LIB_DH, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!ecx_key_fromdata(ecx, params, 1)
+        || !EVP_PKEY_assign(pkey, keytype, ecx)) {
+        ecx_key_free(ecx);
+        return 0;
+    }
+    return 1;
+}
+
+static int x25519_import_from(const OSSL_PARAM params[], void *key)
+{
+    return ecx_generic_import_from(params, key, EVP_PKEY_X25519);
+}
+
 const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth = {
     EVP_PKEY_X25519,
     EVP_PKEY_X25519,
@@ -494,9 +506,15 @@ const EVP_PKEY_ASN1_METHOD ecx25519_asn1_meth = {
     ecx_get_priv_key,
     ecx_get_pub_key,
     ecx_pkey_dirty_cnt,
-    ecx_pkey_export_to
+    ecx_pkey_export_to,
+    x25519_import_from
 };
 
+static int x448_import_from(const OSSL_PARAM params[], void *key)
+{
+    return ecx_generic_import_from(params, key, EVP_PKEY_X448);
+}
+
 const EVP_PKEY_ASN1_METHOD ecx448_asn1_meth = {
     EVP_PKEY_X448,
     EVP_PKEY_X448,
@@ -539,7 +557,8 @@ const EVP_PKEY_ASN1_METHOD ecx448_asn1_meth = {
     ecx_get_priv_key,
     ecx_get_pub_key,
     ecx_pkey_dirty_cnt,
-    ecx_pkey_export_to
+    ecx_pkey_export_to,
+    x448_import_from
 };
 
 static int ecd_size25519(const EVP_PKEY *pkey)
@@ -614,6 +633,10 @@ static int ecd_sig_info_set448(X509_SIG_INFO *siginf, const X509_ALGOR *alg,
     return 1;
 }
 
+static int ed25519_import_from(const OSSL_PARAM params[], void *key)
+{
+    return ecx_generic_import_from(params, key, EVP_PKEY_ED25519);
+}
 
 const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
     EVP_PKEY_ED25519,
@@ -656,9 +679,15 @@ const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
     ecx_get_priv_key,
     ecx_get_pub_key,
     ecx_pkey_dirty_cnt,
-    ecx_pkey_export_to
+    ecx_pkey_export_to,
+    ed25519_import_from
 };
 
+static int ed448_import_from(const OSSL_PARAM params[], void *key)
+{
+    return ecx_generic_import_from(params, key, EVP_PKEY_ED448);
+}
+
 const EVP_PKEY_ASN1_METHOD ed448_asn1_meth = {
     EVP_PKEY_ED448,
     EVP_PKEY_ED448,
@@ -700,7 +729,8 @@ const EVP_PKEY_ASN1_METHOD ed448_asn1_meth = {
     ecx_get_priv_key,
     ecx_get_pub_key,
     ecx_pkey_dirty_cnt,
-    ecx_pkey_export_to
+    ecx_pkey_export_to,
+    ed448_import_from
 };
 
 static int pkey_ecx_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey)
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index cb106e23b1..1a21faa1e1 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -2525,6 +2525,8 @@ EVP_R_FINAL_ERROR:188:final error
 EVP_R_FIPS_MODE_NOT_SUPPORTED:167:fips mode not supported
 EVP_R_GET_RAW_KEY_FAILED:182:get raw key failed
 EVP_R_ILLEGAL_SCRYPT_PARAMETERS:171:illegal scrypt parameters
+EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS:204:inaccessible domain parameters
+EVP_R_INACCESSIBLE_KEY:203:inaccessible key
 EVP_R_INITIALIZATION_ERROR:134:initialization error
 EVP_R_INPUT_NOT_INITIALIZED:111:input not initialized
 EVP_R_INVALID_CUSTOM_LENGTH:185:invalid custom length
@@ -2537,6 +2539,7 @@ EVP_R_INVALID_OPERATION:148:invalid operation
 EVP_R_INVALID_PROVIDER_FUNCTIONS:193:invalid provider functions
 EVP_R_INVALID_SALT_LENGTH:186:invalid salt length
 EVP_R_KEYGEN_FAILURE:120:keygen failure
+EVP_R_KEYMGMT_EXPORT_FAILURE:205:keymgmt export failure
 EVP_R_KEY_SETUP_FAILED:180:key setup failed
 EVP_R_MEMORY_LIMIT_EXCEEDED:172:memory limit exceeded
 EVP_R_MESSAGE_DIGEST_IS_NULL:159:message digest is null
@@ -2547,6 +2550,7 @@ EVP_R_NOT_XOF_OR_INVALID_LENGTH:178:not XOF or invalid length
 EVP_R_NO_CIPHER_SET:131:no cipher set
 EVP_R_NO_DEFAULT_DIGEST:158:no default digest
 EVP_R_NO_DIGEST_SET:139:no digest set
+EVP_R_NO_IMPORT_FUNCTION:206:no import function
 EVP_R_NO_KEYMGMT_AVAILABLE:199:no keymgmt available
 EVP_R_NO_KEYMGMT_PRESENT:196:no keymgmt present
 EVP_R_NO_KEY_SET:154:no key set
@@ -2566,6 +2570,7 @@ EVP_R_PUBLIC_KEY_NOT_RSA:106:public key not rsa
 EVP_R_TOO_MANY_RECORDS:183:too many records
 EVP_R_UNKNOWN_CIPHER:160:unknown cipher
 EVP_R_UNKNOWN_DIGEST:161:unknown digest
+EVP_R_UNKNOWN_KEY_TYPE:207:unknown key type
 EVP_R_UNKNOWN_OPTION:169:unknown option
 EVP_R_UNKNOWN_PBE_ALGORITHM:121:unknown pbe algorithm
 EVP_R_UNSUPPORTED_ALGORITHM:156:unsupported algorithm
diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c
index 20921710ee..3f2b814f18 100644
--- a/crypto/evp/evp_err.c
+++ b/crypto/evp/evp_err.c
@@ -71,6 +71,9 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_GET_RAW_KEY_FAILED), "get raw key failed"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_ILLEGAL_SCRYPT_PARAMETERS),
     "illegal scrypt parameters"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS),
+    "inaccessible domain parameters"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INACCESSIBLE_KEY), "inaccessible key"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INITIALIZATION_ERROR),
     "initialization error"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INPUT_NOT_INITIALIZED),
@@ -88,6 +91,8 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_INVALID_SALT_LENGTH),
     "invalid salt length"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYGEN_FAILURE), "keygen failure"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEYMGMT_EXPORT_FAILURE),
+    "keymgmt export failure"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_KEY_SETUP_FAILED), "key setup failed"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_MEMORY_LIMIT_EXCEEDED),
     "memory limit exceeded"},
@@ -103,6 +108,7 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_CIPHER_SET), "no cipher set"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DEFAULT_DIGEST), "no default digest"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DIGEST_SET), "no digest set"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_IMPORT_FUNCTION), "no import function"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_AVAILABLE),
     "no keymgmt available"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_PRESENT), "no keymgmt present"},
@@ -129,6 +135,7 @@ static const ERR_STRING_DATA EVP_str_reasons[] = {
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_TOO_MANY_RECORDS), "too many records"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_CIPHER), "unknown cipher"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_DIGEST), "unknown digest"},
+    {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_KEY_TYPE), "unknown key type"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_OPTION), "unknown option"},
     {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_UNKNOWN_PBE_ALGORITHM),
     "unknown pbe algorithm"},
diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c
index 94be3c2a9c..6c66bfa72d 100644
--- a/crypto/evp/keymgmt_lib.c
+++ b/crypto/evp/keymgmt_lib.c
@@ -39,13 +39,26 @@ static int try_import(const OSSL_PARAM params[], void *arg)
 {
     struct import_data_st *data = arg;
 
+    /*
+     * It's fine if there was no data to transfer, we just end up with an
+     * empty destination key.
+     */
+    if (params[0].key == NULL)
+        return 1;
+
+    /* Just in time creation of keydata, if needed */
+    if (data->keydata == NULL
+        && (data->keydata = evp_keymgmt_newdata(data->keymgmt)) == NULL) {
+        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
     return evp_keymgmt_import(data->keymgmt, data->keydata, data->selection,
                               params);
 }
 
 void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
 {
-    void *keydata = NULL;
     struct import_data_st import_data;
     size_t i = 0;
 
@@ -54,7 +67,7 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
         return NULL;
 
     /* If we have an unassigned key, give up */
-    if (pk->keymgmt == NULL)
+    if (pk->keydata == NULL)
         return NULL;
 
     /* If |keymgmt| matches the "origin" |keymgmt|, no more to do */
@@ -91,10 +104,6 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
     if (!ossl_assert(match_type(pk->keymgmt, keymgmt)))
         return NULL;
 
-    /* Create space to import data into */
-    if ((keydata = evp_keymgmt_newdata(keymgmt)) == NULL)
-        return NULL;
-
     /*
      * We look at the already cached provider keys, and import from the
      * first that supports it (i.e. use its export function), and export
@@ -102,7 +111,7 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
      */
 
     /* Setup for the export callback */
-    import_data.keydata = keydata;
+    import_data.keydata = NULL;  /* try_import will create it */
     import_data.keymgmt = keymgmt;
     import_data.selection = OSSL_KEYMGMT_SELECT_ALL;
 
@@ -113,17 +122,17 @@ void *evp_keymgmt_util_export_to_provider(EVP_PKEY *pk, EVP_KEYMGMT *keymgmt)
     if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, OSSL_KEYMGMT_SELECT_ALL,
                             &try_import, &import_data)) {
         /* If there was an error, bail out */
-        evp_keymgmt_freedata(keymgmt, keydata);
+        evp_keymgmt_freedata(keymgmt, import_data.keydata);
         return NULL;
     }
 
     /* Add the new export to the operation cache */
-    if (!evp_keymgmt_util_cache_keydata(pk, i, keymgmt, keydata)) {
-        evp_keymgmt_freedata(keymgmt, keydata);
+    if (!evp_keymgmt_util_cache_keydata(pk, i, keymgmt, import_data.keydata)) {
+        evp_keymgmt_freedata(keymgmt, import_data.keydata);
         return NULL;
     }
 
-    return keydata;
+    return import_data.keydata;
 }
 
 void evp_keymgmt_util_clear_operation_cache(EVP_PKEY *pk)
@@ -175,7 +184,7 @@ void evp_keymgmt_util_cache_keyinfo(EVP_PKEY *pk)
      *
      * This services functions like EVP_PKEY_size, EVP_PKEY_bits, etc
      */
-    if (pk->keymgmt != NULL) {
+    if (pk->keydata != NULL) {
         int bits = 0;
         int security_bits = 0;
         int size = 0;
@@ -197,17 +206,15 @@ void evp_keymgmt_util_cache_keyinfo(EVP_PKEY *pk)
 void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
                                 int selection, const OSSL_PARAM params[])
 {
-    void *keydata = evp_keymgmt_newdata(keymgmt);
+    void *keydata = NULL;
 
+    if ((keydata = evp_keymgmt_newdata(keymgmt)) == NULL
+        || !evp_keymgmt_import(keymgmt, keydata, selection, params)
+        || !EVP_PKEY_set_type_by_keymgmt(target, keymgmt)) {
+        evp_keymgmt_freedata(keymgmt, keydata);
+        keydata = NULL;
+    }
     if (keydata != NULL) {
-        if (!evp_keymgmt_import(keymgmt, keydata, selection, params)
-            || !EVP_KEYMGMT_up_ref(keymgmt)) {
-            evp_keymgmt_freedata(keymgmt, keydata);
-            return NULL;
-        }
-
-        evp_keymgmt_util_clear_operation_cache(target);
-        target->keymgmt = keymgmt;
         target->keydata = keydata;
         evp_keymgmt_util_cache_keyinfo(target);
     }
@@ -254,7 +261,17 @@ int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection)
     keydata2 = pk2->keydata;
 
     if (keymgmt1 != keymgmt2) {
-        void *tmp_keydata = NULL;
+        /*
+         * The condition for a successful cross export is that the
+         * keydata to be exported is NULL (typed, but otherwise empty
+         * EVP_PKEY), or that it was possible to export it with
+         * evp_keymgmt_util_export_to_provider().
+         *
+         * We use |ok| to determine if it's ok to cross export one way,
+         * but also to determine if we should attempt a cross export
+         * the other way.  There's no point doing it both ways.
+         */
+        int ok = 1;
 
         /* Complex case, where the keymgmt differ */
         if (keymgmt1 != NULL
@@ -270,17 +287,35 @@ int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection)
          */
         if (keymgmt2 != NULL
             && keymgmt2->match != NULL) {
-            tmp_keydata = evp_keymgmt_util_export_to_provider(pk1, keymgmt2);
-            if (tmp_keydata != NULL) {
+            void *tmp_keydata = NULL;
+
+            ok = 1;
+            if (keydata1 != NULL) {
+                tmp_keydata =
+                    evp_keymgmt_util_export_to_provider(pk1, keymgmt2);
+                ok = (tmp_keydata != NULL);
+            }
+            if (ok) {
                 keymgmt1 = keymgmt2;
                 keydata1 = tmp_keydata;
             }
         }
-        if (tmp_keydata == NULL
+        /*
+         * If we've successfully cross exported one way, there's no point
+         * doing it the other way, hence the |!ok| check.
+         */
+        if (!ok
             && keymgmt1 != NULL
             && keymgmt1->match != NULL) {
-            tmp_keydata = evp_keymgmt_util_export_to_provider(pk2, keymgmt1);
-            if (tmp_keydata != NULL) {
+            void *tmp_keydata = NULL;
+
+            ok = 1;
+            if (keydata2 != NULL) {
+                tmp_keydata =
+                    evp_keymgmt_util_export_to_provider(pk2, keymgmt1);
+                ok = (tmp_keydata != NULL);
+            }
+            if (ok) {
                 keymgmt2 = keymgmt1;
                 keydata2 = tmp_keydata;
             }
@@ -291,6 +326,13 @@ int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection)
     if (keymgmt1 != keymgmt2)
         return -2;
 
+    /* If both keydata are NULL, then they're the same key */
+    if (keydata1 == NULL && keydata2 == NULL)
+        return 1;
+    /* If only one of the keydata is NULL, then they're different keys */
+    if (keydata1 == NULL || keydata2 == NULL)
+        return 0;
+    /* If both keydata are non-NULL, we let the backend decide */
     return evp_keymgmt_match(keymgmt1, keydata1, keydata2, selection);
 }
 
@@ -301,23 +343,21 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection)
     void *to_keydata = to->keydata, *alloc_keydata = NULL;
 
     /* An unassigned key can't be copied */
-    if (from == NULL || from->keymgmt == NULL)
+    if (from == NULL || from->keydata == NULL)
         return 0;
 
-    /* If |from| doesn't support copying, we fail */
-    if (from->keymgmt->copy == NULL)
-        return 0;
-
-    /* If |to| doesn't have a provider side "origin" yet, create one */
-    if (to_keymgmt == NULL) {
-        to_keydata = alloc_keydata = evp_keymgmt_newdata(from->keymgmt);
-        if (to_keydata == NULL)
+    if (to_keymgmt == from->keymgmt && to_keymgmt->copy != NULL) {
+        /* Make sure there's somewhere to copy to */
+        if (to_keydata == NULL
+            && (to_keydata = evp_keymgmt_newdata(to_keymgmt)) == NULL) {
+            ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
             return 0;
-        to_keymgmt = from->keymgmt;
-    }
+        }
 
-    if (to_keymgmt == from->keymgmt) {
-        /* |to| and |from| have the same keymgmt, just copy and be done */
+        /*
+         * |to| and |from| have the same keymgmt, and the copy function is
+         * implemented, so just copy and be done
+         */
         if (!evp_keymgmt_copy(to_keymgmt, to_keydata, from->keydata,
                               selection))
             return 0;
@@ -333,18 +373,22 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection)
             evp_keymgmt_freedata(to_keymgmt, alloc_keydata);
             return 0;
         }
+
+        /*
+         * In this case to_keydata was previously unallocated, try_import()
+         * may have created it for us.
+         */
+        to_keydata = import_data.keydata;
     } else {
         ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
         return 0;
     }
 
     if (to->keymgmt == NULL
-        && !EVP_KEYMGMT_up_ref(to_keymgmt)) {
+        && !EVP_PKEY_set_type_by_keymgmt(to, to_keymgmt)) {
         evp_keymgmt_freedata(to_keymgmt, alloc_keydata);
         return 0;
     }
-    evp_keymgmt_util_clear_operation_cache(to);
-    to->keymgmt = to_keymgmt;
     to->keydata = to_keydata;
     evp_keymgmt_util_cache_keyinfo(to);
 
@@ -354,16 +398,14 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection)
 void *evp_keymgmt_util_gen(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
                            void *genctx, OSSL_CALLBACK *cb, void *cbarg)
 {
-    void *keydata = evp_keymgmt_gen(keymgmt, genctx, cb, cbarg);
+    void *keydata = NULL;
 
+    if ((keydata = evp_keymgmt_gen(keymgmt, genctx, cb, cbarg)) == NULL
+        || !EVP_PKEY_set_type_by_keymgmt(target, keymgmt)) {
+        evp_keymgmt_freedata(keymgmt, keydata);
+        keydata = NULL;
+    }
     if (keydata != NULL) {
-        if (!EVP_KEYMGMT_up_ref(keymgmt)) {
-            evp_keymgmt_freedata(keymgmt, keydata);
-            return NULL;
-        }
-
-        evp_keymgmt_util_clear_operation_cache(target);
-        target->keymgmt = keymgmt;
         target->keydata = keydata;
         evp_keymgmt_util_cache_keyinfo(target);
     }
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index a2bb2b7190..9ed238e366 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -35,10 +35,15 @@
 #include "internal/provider.h"
 #include "evp_local.h"
 
+static int pkey_set_type(EVP_PKEY *pkey, ENGINE *e, int type, const char *str,
+                         int len, EVP_KEYMGMT *keymgmt);
 static void evp_pkey_free_it(EVP_PKEY *key);
 
 #ifndef FIPS_MODE
 
+/* The type of parameters selected in key parameter functions */
+# define SELECT_PARAMETERS OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
+
 int EVP_PKEY_bits(const EVP_PKEY *pkey)
 {
     if (pkey != NULL) {
@@ -92,16 +97,35 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
      */
 
     /*
-     * Only check that type match this early when both keys are legacy.
-     * If either of them is provided, we let evp_keymgmt_util_copy()
-     * do this check, after having exported either of them that isn't
-     * provided.
+     * If |to| is a legacy key and |from| isn't, we must downgrade |from|.
+     * If that fails, this function fails.
+     */
+    if (to->type != EVP_PKEY_NONE && from->keymgmt != NULL)
+        if (!evp_pkey_downgrade((EVP_PKEY *)from))
+            return 0;
+
+    /*
+     * Make sure |to| is typed.  Content is less important at this early
+     * stage.
+     *
+     * 1.  If |to| is untyped, assign |from|'s key type to it.
+     * 2.  If |to| contains a legacy key, compare its |type| to |from|'s.
+     *     (|from| was already downgraded above)
+     *
+     * If |to| is a provided key, there's nothing more to do here, functions
+     * like evp_keymgmt_util_copy() and evp_pkey_export_to_provider() called
+     * further down help us find out if they are the same or not.
      */
-    if (to->keymgmt == NULL && from->keymgmt == NULL) {
-        if (to->type == EVP_PKEY_NONE) {
+    if (to->type == EVP_PKEY_NONE && to->keymgmt == NULL) {
+        if (from->type != EVP_PKEY_NONE) {
             if (EVP_PKEY_set_type(to, from->type) == 0)
                 return 0;
-        } else if (to->type != from->type) {
+        } else {
+            if (EVP_PKEY_set_type_by_keymgmt(to, from->keymgmt) == 0)
+                return 0;
+        }
+    } else if (to->type != EVP_PKEY_NONE) {
+        if (to->type != from->type) {
             EVPerr(EVP_F_EVP_PKEY_COPY_PARAMETERS, EVP_R_DIFFERENT_KEY_TYPES);
             goto err;
         }
@@ -119,34 +143,9 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
         return 0;
     }
 
-    /*
-     * If |from| is provided, we upgrade |to| to be provided as well.
-     * This drops the legacy key from |to|.
-     * evp_pkey_upgrade_to_provider() checks if |to| is already provided,
-     * we don't need to do that here.
-     *
-     * TODO(3.0) We should investigate if that's too aggressive and make
-     * this scenario unsupported instead.
-     */
-    if (from->keymgmt != NULL) {
-        EVP_KEYMGMT *tmp_keymgmt = from->keymgmt;
-
-        /*
-         * The returned pointer is known to be cached, so we don't have to
-         * save it.  However, if it's NULL, something went wrong and we can't
-         * copy.
-         */
-        if (evp_pkey_upgrade_to_provider(to, NULL,
-                                         &tmp_keymgmt, NULL) == NULL) {
-            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
-            return 0;
-        }
-    }
-
     /* For purely provided keys, we just call the keymgmt utility */
     if (to->keymgmt != NULL && from->keymgmt != NULL)
-        return evp_keymgmt_util_copy(to, (EVP_PKEY *)from,
-                                     OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+        return evp_keymgmt_util_copy(to, (EVP_PKEY *)from, SELECT_PARAMETERS);
 
     /*
      * If |to| is provided, we know that |from| is legacy at this point.
@@ -159,12 +158,16 @@ int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
             evp_pkey_export_to_provider((EVP_PKEY *)from, NULL, &to_keymgmt,
                                         NULL);
 
+        /*
+         * If we get a NULL, it could be an internal error, or it could be
+         * that there's a key mismatch.  We're pretending the latter...
+         */
         if (from_keydata == NULL) {
-            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
             return 0;
         }
         return evp_keymgmt_copy(to->keymgmt, to->keydata, from_keydata,
-                                OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+                                SELECT_PARAMETERS);
     }
 
     /* Both keys are legacy */
@@ -178,8 +181,7 @@ int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey)
 {
     if (pkey != NULL) {
         if (pkey->keymgmt != NULL)
-            return !evp_keymgmt_util_has((EVP_PKEY *)pkey,
-                                         OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+            return !evp_keymgmt_util_has((EVP_PKEY *)pkey, SELECT_PARAMETERS);
         else if (pkey->ameth != NULL && pkey->ameth->param_missing != NULL)
             return pkey->ameth->param_missing(pkey);
     }
@@ -206,20 +208,25 @@ static int evp_pkey_cmp_any(const EVP_PKEY *a, const EVP_PKEY *b,
         return evp_keymgmt_util_match((EVP_PKEY *)a, (EVP_PKEY *)b, selection);
 
     /*
-     * Here, we know that we have a mixture of legacy and provided keys.
-     * Try cross export and compare the resulting key data.
+     * At this point, one of them is provided, the other not.  This allows
+     * us to compare types using legacy NIDs.
+     */
+    if ((a->type != EVP_PKEY_NONE
+         && !EVP_KEYMGMT_is_a(b->keymgmt, OBJ_nid2sn(a->type)))
+        || (b->type != EVP_PKEY_NONE
+            && !EVP_KEYMGMT_is_a(a->keymgmt, OBJ_nid2sn(b->type))))
+        return -1;               /* not the same key type */
+
+    /*
+     * We've determined that they both are the same keytype, so the next
+     * step is to do a bit of cross export to ensure we have keydata for
+     * both keys in the same keymgmt.
      */
     keymgmt1 = a->keymgmt;
     keydata1 = a->keydata;
     keymgmt2 = b->keymgmt;
     keydata2 = b->keydata;
 
-    if ((keymgmt1 == NULL
-         && !EVP_KEYMGMT_is_a(keymgmt2, OBJ_nid2sn(a->type)))
-        || (keymgmt2 == NULL
-            && !EVP_KEYMGMT_is_a(keymgmt1, OBJ_nid2sn(b->type))))
-        return -1;               /* not the same key type */
-
     if (keymgmt2 != NULL && keymgmt2->match != NULL) {
         tmp_keydata =
             evp_pkey_export_to_provider((EVP_PKEY *)a, NULL, &keymgmt2, NULL);
@@ -252,7 +259,7 @@ int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
      */
 
     if (a->keymgmt != NULL || b->keymgmt != NULL)
-        return evp_pkey_cmp_any(a, b, OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
+        return evp_pkey_cmp_any(a, b, SELECT_PARAMETERS);
 
     /* All legacy keys */
     if (a->type != b->type)
@@ -270,9 +277,8 @@ int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
      */
 
     if (a->keymgmt != NULL || b->keymgmt != NULL)
-        return evp_pkey_cmp_any(a, b,
-                                OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
-                                | OSSL_KEYMGMT_SELECT_PUBLIC_KEY);
+        return evp_pkey_cmp_any(a, b, (SELECT_PARAMETERS
+                                       | OSSL_KEYMGMT_SELECT_PUBLIC_KEY));
 
     /* All legacy keys */
     if (a->type != b->type)
@@ -294,57 +300,6 @@ int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
     return -2;
 }
 
-
-/*
- * Setup a public key ASN1 method and ENGINE from a NID or a string. If pkey
- * is NULL just return 1 or 0 if the algorithm exists.
- */
-
-static int pkey_set_type(EVP_PKEY *pkey, ENGINE *e, int type, const char *str,
-                         int len)
-{
-    const EVP_PKEY_ASN1_METHOD *ameth;
-    ENGINE **eptr = (e == NULL) ? &e :  NULL;
-
-    if (pkey) {
-        if (pkey->pkey.ptr)
-            evp_pkey_free_it(pkey);
-        /*
-         * If key type matches and a method exists then this lookup has
-         * succeeded once so just indicate success.
-         */
-        if ((type == pkey->save_type) && pkey->ameth)
-            return 1;
-# ifndef OPENSSL_NO_ENGINE
-        /* If we have ENGINEs release them */
-        ENGINE_finish(pkey->engine);
-        pkey->engine = NULL;
-        ENGINE_finish(pkey->pmeth_engine);
-        pkey->pmeth_engine = NULL;
-# endif
-    }
-    if (str)
-        ameth = EVP_PKEY_asn1_find_str(eptr, str, len);
-    else
-        ameth = EVP_PKEY_asn1_find(eptr, type);
-# ifndef OPENSSL_NO_ENGINE
-    if (pkey == NULL && eptr != NULL)
-        ENGINE_finish(e);
-# endif
-    if (ameth == NULL) {
-        EVPerr(EVP_F_PKEY_SET_TYPE, EVP_R_UNSUPPORTED_ALGORITHM);
-        return 0;
-    }
-    if (pkey) {
-        pkey->ameth = ameth;
-        pkey->engine = e;
-
-        pkey->type = pkey->ameth->pkey_id;
-        pkey->save_type = type;
-    }
-    return 1;
-}
-
 EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e,
                                        const unsigned char *priv,
                                        size_t len)
@@ -352,7 +307,7 @@ EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e,
     EVP_PKEY *ret = EVP_PKEY_new();
 
     if (ret == NULL
-            || !pkey_set_type(ret, e, type, NULL, -1)) {
+        || !pkey_set_type(ret, e, type, NULL, -1, NULL)) {
         /* EVPerr already called */
         goto err;
     }
@@ -382,7 +337,7 @@ EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e,
     EVP_PKEY *ret = EVP_PKEY_new();
 
     if (ret == NULL
-            || !pkey_set_type(ret, e, type, NULL, -1)) {
+        || !pkey_set_type(ret, e, type, NULL, -1, NULL)) {
         /* EVPerr already called */
         goto err;
     }
@@ -408,6 +363,7 @@ EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e,
 int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
                                  size_t *len)
 {
+    /* TODO(3.0) Do we need to do anything about provider side keys? */
      if (pkey->ameth->get_priv_key == NULL) {
         EVPerr(EVP_F_EVP_PKEY_GET_RAW_PRIVATE_KEY,
                EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -425,6 +381,7 @@ int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv,
 int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
                                 size_t *len)
 {
+    /* TODO(3.0) Do we need to do anything about provider side keys? */
      if (pkey->ameth->get_pub_key == NULL) {
         EVPerr(EVP_F_EVP_PKEY_GET_RAW_PUBLIC_KEY,
                EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -457,8 +414,8 @@ EVP_PKEY *EVP_PKEY_new_CMAC_key(ENGINE *e, const unsigned char *priv,
     size_t paramsn = 0;
 
     if (ret == NULL
-            || cmctx == NULL
-            || !pkey_set_type(ret, e, EVP_PKEY_CMAC, NULL, -1)) {
+        || cmctx == NULL
+        || !pkey_set_type(ret, e, EVP_PKEY_CMAC, NULL, -1, NULL)) {
         /* EVPerr already called */
         goto err;
     }
@@ -499,12 +456,12 @@ EVP_PKEY *EVP_PKEY_new_CMAC_key(ENGINE *e, const unsigned char *priv,
 
 int EVP_PKEY_set_type(EVP_PKEY *pkey, int type)
 {
-    return pkey_set_type(pkey, NULL, type, NULL, -1);
+    return pkey_set_type(pkey, NULL, type, NULL, -1, NULL);
 }
 
 int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len)
 {
-    return pkey_set_type(pkey, NULL, EVP_PKEY_NONE, str, len);
+    return pkey_set_type(pkey, NULL, EVP_PKEY_NONE, str, len, NULL);
 }
 
 int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type)
@@ -573,6 +530,10 @@ int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key)
 
 void *EVP_PKEY_get0(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     return pkey->pkey.ptr;
 }
 
@@ -628,6 +589,10 @@ int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key)
 
 RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_RSA_PSS) {
         EVPerr(EVP_F_EVP_PKEY_GET0_RSA, EVP_R_EXPECTING_AN_RSA_KEY);
         return NULL;
@@ -655,6 +620,10 @@ int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key)
 
 DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (pkey->type != EVP_PKEY_DSA) {
         EVPerr(EVP_F_EVP_PKEY_GET0_DSA, EVP_R_EXPECTING_A_DSA_KEY);
         return NULL;
@@ -683,6 +652,10 @@ int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key)
 
 EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
         EVPerr(EVP_F_EVP_PKEY_GET0_EC_KEY, EVP_R_EXPECTING_A_EC_KEY);
         return NULL;
@@ -713,6 +686,10 @@ int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key)
 
 DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey)
 {
+    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
+        return NULL;
+    }
     if (pkey->type != EVP_PKEY_DH && pkey->type != EVP_PKEY_DHX) {
         EVPerr(EVP_F_EVP_PKEY_GET0_DH, EVP_R_EXPECTING_A_DH_KEY);
         return NULL;
@@ -999,6 +976,178 @@ EVP_PKEY *EVP_PKEY_new(void)
     return ret;
 }
 
+/*
+ * Setup a public key management method.
+ *
+ * For legacy keys, either |type| or |str| is expected to have the type
+ * information.  In this case, the setup consists of finding an ASN1 method
+ * and potentially an ENGINE, and setting those fields in |pkey|.
+ *
+ * For provider side keys, |keymgmt| is expected to be non-NULL.  In this
+ * case, the setup consists of setting the |keymgmt| field in |pkey|.
+ *
+ * If pkey is NULL just return 1 or 0 if the key management method exists.
+ */
+
+static int pkey_set_type(EVP_PKEY *pkey, ENGINE *e, int type, const char *str,
+                         int len, EVP_KEYMGMT *keymgmt)
+{
+#ifndef FIPS_MODE
+    const EVP_PKEY_ASN1_METHOD *ameth = NULL;
+    ENGINE **eptr = (e == NULL) ? &e :  NULL;
+#endif
+
+    /*
+     * The setups can't set both legacy and provider side methods.
+     * It is forbidden
+     */
+    if (!ossl_assert(type == EVP_PKEY_NONE || keymgmt == NULL)
+        || !ossl_assert(e == NULL || keymgmt == NULL)) {
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+
+    if (pkey != NULL) {
+        int free_it = 0;
+
+#ifndef FIPS_MODE
+        free_it = free_it || pkey->pkey.ptr != NULL;
+#endif
+        free_it = free_it || pkey->keydata != NULL;
+        if (free_it)
+            evp_pkey_free_it(pkey);
+#ifndef FIPS_MODE
+        /*
+         * If key type matches and a method exists then this lookup has
+         * succeeded once so just indicate success.
+         */
+        if (pkey->type != EVP_PKEY_NONE
+            && type == pkey->save_type
+            && pkey->ameth != NULL)
+            return 1;
+# ifndef OPENSSL_NO_ENGINE
+        /* If we have ENGINEs release them */
+        ENGINE_finish(pkey->engine);
+        pkey->engine = NULL;
+        ENGINE_finish(pkey->pmeth_engine);
+        pkey->pmeth_engine = NULL;
+# endif
+#endif
+    }
+#ifndef FIPS_MODE
+    if (str != NULL)
+        ameth = EVP_PKEY_asn1_find_str(eptr, str, len);
+    else if (type != EVP_PKEY_NONE)
+        ameth = EVP_PKEY_asn1_find(eptr, type);
+# ifndef OPENSSL_NO_ENGINE
+    if (pkey == NULL && eptr != NULL)
+        ENGINE_finish(e);
+# endif
+#endif
+
+
+    {
+        int check = 1;
+
+#ifndef FIPS_MODE
+        check = check && ameth == NULL;
+#endif
+        check = check && keymgmt == NULL;
+        if (check) {
+            EVPerr(EVP_F_PKEY_SET_TYPE, EVP_R_UNSUPPORTED_ALGORITHM);
+            return 0;
+        }
+    }
+    if (pkey != NULL) {
+        if (keymgmt != NULL && !EVP_KEYMGMT_up_ref(keymgmt)) {
+            ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+            return 0;
+        }
+
+        pkey->keymgmt = keymgmt;
+
+        pkey->save_type = type;
+        pkey->type = type;
+
+#ifndef FIPS_MODE
+        /*
+         * If the internal "origin" key is provider side, don't save |ameth|.
+         * The main reason is that |ameth| is one factor to detect that the
+         * internal "origin" key is a legacy one.
+         */
+        if (keymgmt == NULL)
+            pkey->ameth = ameth;
+        pkey->engine = e;
+
+        /*
+         * The EVP_PKEY_ASN1_METHOD |pkey_id| serves different purposes,
+         * depending on if we're setting this key to contain a legacy or
+         * a provider side "origin" key.  For a legacy key, we assign it
+         * to the |type| field, but for a provider side key, we assign it
+         * to the |save_type| field, because |type| is supposed to be set
+         * to EVP_PKEY_NONE in that case.
+         */
+        if (keymgmt != NULL)
+            pkey->save_type = ameth->pkey_id;
+        else if (pkey->ameth != NULL)
+            pkey->type = ameth->pkey_id;
+#endif
+    }
+    return 1;
+}
+
+#ifndef FIPS_MODE
+static void find_ameth(const char *name, void *data)
+{
+    const char **str = data;
+
+    /*
+     * The error messages from pkey_set_type() are uninteresting here,
+     * and misleading.
+     */
+    ERR_set_mark();
+
+    if (pkey_set_type(NULL, NULL, EVP_PKEY_NONE, name, strlen(name),
+                      NULL)) {
+        if (str[0] == NULL)
+            str[0] = name;
+        else if (str[1] == NULL)
+            str[1] = name;
+    }
+
+    ERR_pop_to_mark();
+}
+#endif
+
+int EVP_PKEY_set_type_by_keymgmt(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt)
+{
+#ifndef FIPS_MODE
+# define EVP_PKEY_TYPE_STR str[0]
+# define EVP_PKEY_TYPE_STRLEN (str[0] == NULL ? -1 : (int)strlen(str[0]))
+    /*
+     * Find at most two strings that have an associated EVP_PKEY_ASN1_METHOD
+     * Ideally, only one should be found.  If two (or more) are found, the
+     * match is ambiguous.  This should never happen, but...
+     */
+    const char *str[2] = { NULL, NULL };
+
+    EVP_KEYMGMT_names_do_all(keymgmt, find_ameth, &str);
+    if (str[1] != NULL) {
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+#else
+# define EVP_PKEY_TYPE_STR NULL
+# define EVP_PKEY_TYPE_STRLEN -1
+#endif
+    return pkey_set_type(pkey, NULL, EVP_PKEY_NONE,
+                         EVP_PKEY_TYPE_STR, EVP_PKEY_TYPE_STRLEN,
+                         keymgmt);
+
+#undef EVP_PKEY_TYPE_STR
+#undef EVP_PKEY_TYPE_STRLEN
+}
+
 int EVP_PKEY_up_ref(EVP_PKEY *pkey)
 {
     int i;
@@ -1018,7 +1167,6 @@ void evp_pkey_free_legacy(EVP_PKEY *x)
         if (x->ameth->pkey_free != NULL)
             x->ameth->pkey_free(x);
         x->pkey.ptr = NULL;
-        x->ameth = NULL;
     }
 # ifndef OPENSSL_NO_ENGINE
     ENGINE_finish(x->engine);
@@ -1026,7 +1174,7 @@ void evp_pkey_free_legacy(EVP_PKEY *x)
     ENGINE_finish(x->pmeth_engine);
     x->pmeth_engine = NULL;
 # endif
-    x->type = x->save_type = EVP_PKEY_NONE;
+    x->type = EVP_PKEY_NONE;
 }
 #endif  /* FIPS_MODE */
 
@@ -1069,13 +1217,16 @@ void EVP_PKEY_free(EVP_PKEY *x)
 
 int EVP_PKEY_size(const EVP_PKEY *pkey)
 {
+    int size = 0;
+
     if (pkey != NULL) {
-        if (pkey->ameth == NULL)
-            return pkey->cache.size;
-        else if (pkey->ameth->pkey_size != NULL)
-            return pkey->ameth->pkey_size(pkey);
+        size = pkey->cache.size;
+#ifndef FIPS_MODE
+        if (pkey->ameth != NULL && pkey->ameth->pkey_size != NULL)
+            size = pkey->ameth->pkey_size(pkey);
+#endif
     }
-    return 0;
+    return size;
 }
 
 void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
@@ -1085,10 +1236,20 @@ void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
     EVP_KEYMGMT *allocated_keymgmt = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
     void *keydata = NULL;
+    int check;
 
     if (pk == NULL)
         return NULL;
 
+    /* No key data => nothing to export */
+    check = 1;
+#ifndef FIPS_MODE
+    check = check && pk->pkey.ptr == NULL;
+#endif
+    check = check && pk->keydata == NULL;
+    if (check)
+        return NULL;
+
 #ifndef FIPS_MODE
     if (pk->pkey.ptr != NULL) {
         /*
@@ -1214,101 +1375,87 @@ void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
 }
 
 #ifndef FIPS_MODE
-/*
- * This differs from exporting in that it releases the legacy key and assigns
- * the export keymgmt and keydata to the "origin" provider side key instead
- * of the operation cache.
- */
-void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
-                                   EVP_KEYMGMT **keymgmt,
-                                   const char *propquery)
+int evp_pkey_downgrade(EVP_PKEY *pk)
 {
-    EVP_KEYMGMT *allocated_keymgmt = NULL;
-    EVP_KEYMGMT *tmp_keymgmt = NULL;
-    void *keydata = NULL;
+    EVP_KEYMGMT *keymgmt = pk->keymgmt;
+    void *keydata = pk->keydata;
+    int type = pk->save_type;
+    const char *keytype = NULL;
 
-    if (pk == NULL)
-        return NULL;
+    /* If this isn't a provider side key, we're done */
+    if (keymgmt == NULL)
+        return 1;
+
+    /* Get the key type name for error reporting */
+    if (type != EVP_PKEY_NONE)
+        keytype = OBJ_nid2sn(type);
+    else
+        keytype =
+            evp_first_name(EVP_KEYMGMT_provider(keymgmt), keymgmt->name_id);
 
     /*
-     * If this key is already "upgraded", this function shouldn't have been
-     * called.
+     * |save_type| was set when any of the EVP_PKEY_set_type functions
+     * was called.  It was set to EVP_PKEY_NONE if the key type wasn't
+     * recognised to be any of the legacy key types, and the downgrade
+     * isn't possible.
      */
-    if (!ossl_assert(pk->keymgmt == NULL))
-        return NULL;
-
-    if (keymgmt != NULL) {
-        tmp_keymgmt = *keymgmt;
-        *keymgmt = NULL;
+    if (type == EVP_PKEY_NONE) {
+        ERR_raise_data(ERR_LIB_EVP, EVP_R_UNKNOWN_KEY_TYPE,
+                       "key type = %s, can't downgrade", keytype);
+        return 0;
     }
 
-    /* If the key isn't a legacy one, bail out, but with proper values */
-    if (pk->pkey.ptr == NULL) {
-        tmp_keymgmt = pk->keymgmt;
-        keydata = pk->keydata;
-    } else {
-        /* If the legacy key doesn't have an export function, give up */
-        if (pk->ameth->export_to == NULL)
-            return NULL;
-
-        /*
-         * If no keymgmt was given or found, get a default keymgmt.  We do
-         * so by letting EVP_PKEY_CTX_new_from_pkey() do it for us, then we
-         * steal it.
-         */
-        if (tmp_keymgmt == NULL) {
-            EVP_PKEY_CTX *ctx =
-                EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery);
-
-            tmp_keymgmt = ctx->keymgmt;
-            ctx->keymgmt = NULL;
-            EVP_PKEY_CTX_free(ctx);
-        }
-
-        /* If we still don't have a keymgmt, give up */
-        if (tmp_keymgmt == NULL)
-            goto end;
-
-        /* Make sure that the keymgmt key type matches the legacy NID */
-        if (!ossl_assert(EVP_KEYMGMT_is_a(tmp_keymgmt, OBJ_nid2sn(pk->type))))
-            goto end;
+    /*
+     * To be able to downgrade, we steal the provider side "origin" keymgmt
+     * and keydata.  We've already grabbed the pointers, so all we need to
+     * do is clear those pointers in |pk| and then call evp_pkey_free_it().
+     * That way, we can restore |pk| if we need to.
+     */
+    pk->keymgmt = NULL;
+    pk->keydata = NULL;
+    evp_pkey_free_it(pk);
+    if (EVP_PKEY_set_type(pk, type)) {
+        /* If the key is typed but empty, we're done */
+        if (keydata == NULL)
+            return 1;
 
-        if ((keydata = evp_keymgmt_newdata(tmp_keymgmt)) == NULL)
-            goto end;
+        if (pk->ameth->import_from == NULL) {
+            ERR_raise_data(ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION,
+                           "key type = %s", keytype);
+        } else if (evp_keymgmt_export(keymgmt, keydata,
+                                      OSSL_KEYMGMT_SELECT_ALL,
+                                      pk->ameth->import_from, pk)) {
+            /*
+             * Save the provider side data in the operation cache, so they'll
+             * find it again.  evp_pkey_free_it() cleared the cache, so it's
+             * safe to assume slot zero is free.
+             * Note that evp_keymgmt_util_cache_keydata() increments keymgmt's
+             * reference count.
+             */
+            evp_keymgmt_util_cache_keydata(pk, 0, keymgmt, keydata);
 
-        if (!pk->ameth->export_to(pk, keydata, tmp_keymgmt)
-            || !EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
-            evp_keymgmt_freedata(tmp_keymgmt, keydata);
-            keydata = NULL;
-            goto end;
+            /* Synchronize the dirty count */
+            pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+            return 1;
         }
 
-        /*
-         * Clear the operation cache, all the legacy data, as well as the
-         * dirty counters
-         */
-        evp_pkey_free_legacy(pk);
-        pk->dirty_cnt_copy = 0;
-
-        evp_keymgmt_util_clear_operation_cache(pk);
-        pk->keymgmt = tmp_keymgmt;
-        pk->keydata = keydata;
-        evp_keymgmt_util_cache_keyinfo(pk);
+        ERR_raise_data(ERR_LIB_EVP, EVP_R_KEYMGMT_EXPORT_FAILURE,
+                       "key type = %s", keytype);
     }
 
- end:
     /*
-     * If nothing was upgraded, |tmp_keymgmt| might point at a freed
-     * EVP_KEYMGMT, so we clear it to be safe.  It shouldn't be useful for
-     * the caller either way in that case.
+     * Something went wrong.  This could for example happen if the keymgmt
+     * turns out to be an HSM implementation that refuses to let go of some
+     * of the key data, typically the private bits.  In this case, we restore
+     * the provider side internal "origin" and leave it at that.
      */
-    if (keydata == NULL)
-        tmp_keymgmt = NULL;
-
-    if (keymgmt != NULL)
-        *keymgmt = tmp_keymgmt;
-
-    EVP_KEYMGMT_free(allocated_keymgmt);
-    return keydata;
+    if (!ossl_assert(EVP_PKEY_set_type_by_keymgmt(pk, keymgmt))) {
+        /* This should not be impossible */
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    pk->keydata = keydata;
+    evp_keymgmt_util_cache_keyinfo(pk);
+    return 0;     /* No downgrade, but at least the key is restored */
 }
 #endif  /* FIPS_MODE */
diff --git a/crypto/evp/pmeth_check.c b/crypto/evp/pmeth_check.c
index c02353d5ea..587e8ae12a 100644
--- a/crypto/evp/pmeth_check.c
+++ b/crypto/evp/pmeth_check.c
@@ -35,19 +35,24 @@ int EVP_PKEY_public_check(EVP_PKEY_CTX *ctx)
         return evp_keymgmt_validate(keymgmt, key,
                                     OSSL_KEYMGMT_SELECT_PUBLIC_KEY);
 
+    if (pkey->type == EVP_PKEY_NONE)
+        goto not_supported;
+
+#ifndef FIPS_MODE
     /* legacy */
     /* call customized public key check function first */
     if (ctx->pmeth->public_check != NULL)
         return ctx->pmeth->public_check(pkey);
 
     /* use default public key check function in ameth */
-    if (pkey->ameth == NULL || pkey->ameth->pkey_public_check == NULL) {
-        EVPerr(EVP_F_EVP_PKEY_PUBLIC_CHECK,
-               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-        return -2;
-    }
+    if (pkey->ameth == NULL || pkey->ameth->pkey_public_check == NULL)
+        goto not_supported;
 
     return pkey->ameth->pkey_public_check(pkey);
+#endif
+ not_supported:
+    EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
 }
 
 int EVP_PKEY_param_check(EVP_PKEY_CTX *ctx)
@@ -68,19 +73,24 @@ int EVP_PKEY_param_check(EVP_PKEY_CTX *ctx)
         return evp_keymgmt_validate(keymgmt, key,
                                     OSSL_KEYMGMT_SELECT_ALL_PARAMETERS);
 
+    if (pkey->type == EVP_PKEY_NONE)
+        goto not_supported;
+
+#ifndef FIPS_MODE
+    /* legacy */
     /* call customized param check function first */
     if (ctx->pmeth->param_check != NULL)
         return ctx->pmeth->param_check(pkey);
 
-    /* legacy */
     /* use default param check function in ameth */
-    if (pkey->ameth == NULL || pkey->ameth->pkey_param_check == NULL) {
-        EVPerr(EVP_F_EVP_PKEY_PARAM_CHECK,
-               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-        return -2;
-    }
+    if (pkey->ameth == NULL || pkey->ameth->pkey_param_check == NULL)
+        goto not_supported;
 
     return pkey->ameth->pkey_param_check(pkey);
+#endif
+ not_supported:
+    EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
 }
 
 int EVP_PKEY_private_check(EVP_PKEY_CTX *ctx)
@@ -101,6 +111,7 @@ int EVP_PKEY_private_check(EVP_PKEY_CTX *ctx)
         return evp_keymgmt_validate(keymgmt, key,
                                     OSSL_KEYMGMT_SELECT_PRIVATE_KEY);
     /* not supported for legacy keys */
+    EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
     return -2;
 }
 
@@ -121,6 +132,7 @@ int EVP_PKEY_pairwise_check(EVP_PKEY_CTX *ctx)
     if (key != NULL && keymgmt != NULL)
         return evp_keymgmt_validate(keymgmt, key, OSSL_KEYMGMT_SELECT_KEYPAIR);
     /* not supported for legacy keys */
+    EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
     return -2;
 }
 
@@ -141,18 +153,23 @@ int EVP_PKEY_check(EVP_PKEY_CTX *ctx)
     if (key != NULL && keymgmt != NULL)
         return evp_keymgmt_validate(keymgmt, key, OSSL_KEYMGMT_SELECT_ALL);
 
+    if (pkey->type == EVP_PKEY_NONE)
+        goto not_supported;
+
+#ifndef FIPS_MODE
     /* legacy */
     /* call customized check function first */
     if (ctx->pmeth->check != NULL)
         return ctx->pmeth->check(pkey);
 
     /* use default check function in ameth */
-    if (pkey->ameth == NULL || pkey->ameth->pkey_check == NULL) {
-        EVPerr(EVP_F_EVP_PKEY_CHECK,
-               EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-        return -2;
-    }
+    if (pkey->ameth == NULL || pkey->ameth->pkey_check == NULL)
+        goto not_supported;
 
     return pkey->ameth->pkey_check(pkey);
+#endif
+ not_supported:
+    EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return -2;
 }
 
diff --git a/crypto/evp/pmeth_gn.c b/crypto/evp/pmeth_gn.c
index 1bf95af2ac..67800282de 100644
--- a/crypto/evp/pmeth_gn.c
+++ b/crypto/evp/pmeth_gn.c
@@ -180,6 +180,8 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
 #ifdef FIPS_MODE
     goto not_supported;
 #else
+    if (ctx->pkey && !evp_pkey_downgrade(ctx->pkey))
+        goto not_accessible;
     switch (ctx->operation) {
     case EVP_PKEY_OP_PARAMGEN:
         ret = ctx->pmeth->paramgen(ctx, *ppkey);
@@ -208,6 +210,12 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
     ERR_raise(ERR_LIB_EVP, EVP_R_OPERATON_NOT_INITIALIZED);
     ret = -1;
     goto end;
+#ifndef FIPS_MODE
+ not_accessible:
+    ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS);
+    ret = -1;
+    goto end;
+#endif
 }
 
 int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index f5e1131f06..ecaaec41c7 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -157,7 +157,7 @@ static EVP_PKEY_CTX *int_ctx_new(OPENSSL_CTX *libctx,
      * If the key doesn't contain anything legacy, then it must be provided,
      * so we extract the necessary information and use that.
      */
-    if (pkey != NULL && pkey->ameth == NULL) {
+    if (pkey != NULL && pkey->type == EVP_PKEY_NONE) {
         /* If we have an engine, something went wrong somewhere... */
         if (!ossl_assert(e == NULL))
             return NULL;
diff --git a/crypto/ffc/build.info b/crypto/ffc/build.info
index c8bc7e9018..a04430d1d1 100644
--- a/crypto/ffc/build.info
+++ b/crypto/ffc/build.info
@@ -1,7 +1,7 @@
 LIBS=../../libcrypto
 
 $COMMON=ffc_params.c ffc_params_generate.c ffc_key_generate.c\
-        ffc_params_validate.c ffc_key_validate.c
+        ffc_params_validate.c ffc_key_validate.c ffc_backend.c
 
 SOURCE[../../libcrypto]=$COMMON
 SOURCE[../../providers/libfips.a]=$COMMON
diff --git a/crypto/ffc/ffc_backend.c b/crypto/ffc/ffc_backend.c
new file mode 100644
index 0000000000..cde9e43da3
--- /dev/null
+++ b/crypto/ffc/ffc_backend.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2020 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
+ */
+
+#include <openssl/core_names.h>
+#include "internal/ffc.h"
+
+/*
+ * The intention with the "backend" source file is to offer backend support
+ * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
+ * implementations alike.
+ */
+
+int ffc_fromdata(FFC_PARAMS *ffc, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *param_p, *param_q, *param_g;
+    BIGNUM *p = NULL, *q = NULL, *g = NULL;
+
+    if (ffc == NULL)
+        return 0;
+
+    param_p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_P);
+    param_q = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_Q);
+    param_g = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_G);
+
+    if ((param_p != NULL && !OSSL_PARAM_get_BN(param_p, &p))
+        || (param_q != NULL && !OSSL_PARAM_get_BN(param_q, &q))
+        || (param_g != NULL && !OSSL_PARAM_get_BN(param_g, &g)))
+        goto err;
+
+    ffc_params_set0_pqg(ffc, p, q, g);
+    return 1;
+
+ err:
+    BN_free(p);
+    BN_free(q);
+    BN_free(g);
+    return 0;
+}
diff --git a/crypto/rsa/build.info b/crypto/rsa/build.info
index ddb4e6fc5a..c1d1a3769b 100644
--- a/crypto/rsa/build.info
+++ b/crypto/rsa/build.info
@@ -2,7 +2,7 @@ LIBS=../../libcrypto
 
 $COMMON=rsa_ossl.c rsa_gen.c rsa_lib.c rsa_sign.c rsa_aid.c rsa_pk1.c \
         rsa_none.c rsa_oaep.c rsa_chk.c rsa_pss.c rsa_x931.c rsa_crpt.c \
-        rsa_x931g.c rsa_sp800_56b_gen.c rsa_sp800_56b_check.c
+        rsa_x931g.c rsa_sp800_56b_gen.c rsa_sp800_56b_check.c rsa_backend.c
 
 SOURCE[../../libcrypto]=$COMMON\
         rsa_saos.c rsa_err.c rsa_asn1.c rsa_depr.c rsa_ameth.c rsa_prn.c \
diff --git a/crypto/rsa/rsa_ameth.c b/crypto/rsa/rsa_ameth.c
index 06d7a8caf6..58341a928a 100644
--- a/crypto/rsa/rsa_ameth.c
+++ b/crypto/rsa/rsa_ameth.c
@@ -1187,6 +1187,24 @@ static int rsa_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
     return rv;
 }
 
+static int rsa_pkey_import_from(const OSSL_PARAM params[], void *key)
+{
+    EVP_PKEY *pkey = key;
+    RSA *rsa = RSA_new();
+
+    if (rsa == NULL) {
+        ERR_raise(ERR_LIB_DH, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    if (!rsa_fromdata(rsa, params)
+        || !EVP_PKEY_assign_RSA(pkey, rsa)) {
+        RSA_free(rsa);
+        return 0;
+    }
+    return 1;
+}
+
 const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[2] = {
     {
      EVP_PKEY_RSA,
@@ -1225,7 +1243,8 @@ const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[2] = {
      0, 0, 0, 0,
 
      rsa_pkey_dirty_cnt,
-     rsa_pkey_export_to
+     rsa_pkey_export_to,
+     rsa_pkey_import_from
     },
 
     {
@@ -1270,5 +1289,6 @@ const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth = {
      0, 0, 0, 0,
 
      rsa_pkey_dirty_cnt,
-     rsa_pkey_export_to
+     rsa_pkey_export_to,
+     rsa_pkey_import_from
 };
diff --git a/crypto/rsa/rsa_backend.c b/crypto/rsa/rsa_backend.c
new file mode 100644
index 0000000000..f68d38cc1a
--- /dev/null
+++ b/crypto/rsa/rsa_backend.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2020 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
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include "crypto/rsa.h"
+
+/*
+ * The intention with the "backend" source file is to offer backend support
+ * for legacy backends (EVP_PKEY_ASN1_METHOD and EVP_PKEY_METHOD) and provider
+ * implementations alike.
+ */
+
+DEFINE_STACK_OF(BIGNUM)
+
+static int collect_numbers(STACK_OF(BIGNUM) *numbers,
+                           const OSSL_PARAM params[], const char *key)
+{
+    const OSSL_PARAM *p = NULL;
+
+    if (numbers == NULL)
+        return 0;
+
+    for (p = params; (p = OSSL_PARAM_locate_const(p, key)) != NULL; p++) {
+        BIGNUM *tmp = NULL;
+
+        if (!OSSL_PARAM_get_BN(p, &tmp)
+            || sk_BIGNUM_push(numbers, tmp) == 0)
+            return 0;
+    }
+
+    return 1;
+}
+
+int rsa_fromdata(RSA *rsa, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *param_n, *param_e,  *param_d;
+    BIGNUM *n = NULL, *e = NULL, *d = NULL;
+    STACK_OF(BIGNUM) *factors = NULL, *exps = NULL, *coeffs = NULL;
+    int is_private = 0;
+
+    if (rsa == NULL)
+        return 0;
+
+    param_n = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N);
+    param_e = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E);
+    param_d = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D);
+
+    if ((param_n != NULL && !OSSL_PARAM_get_BN(param_n, &n))
+        || (param_e != NULL && !OSSL_PARAM_get_BN(param_e, &e))
+        || (param_d != NULL && !OSSL_PARAM_get_BN(param_d, &d)))
+        goto err;
+
+    is_private = (d != NULL);
+
+    if (!RSA_set0_key(rsa, n, e, d))
+        goto err;
+    n = e = d = NULL;
+
+    if (is_private) {
+        if (!collect_numbers(factors = sk_BIGNUM_new_null(), params,
+                             OSSL_PKEY_PARAM_RSA_FACTOR)
+            || !collect_numbers(exps = sk_BIGNUM_new_null(), params,
+                                OSSL_PKEY_PARAM_RSA_EXPONENT)
+            || !collect_numbers(coeffs = sk_BIGNUM_new_null(), params,
+                                OSSL_PKEY_PARAM_RSA_COEFFICIENT))
+            goto err;
+
+        /* It's ok if this private key just has n, e and d */
+        if (sk_BIGNUM_num(factors) != 0
+            && !rsa_set0_all_params(rsa, factors, exps, coeffs))
+            goto err;
+    }
+
+    sk_BIGNUM_free(factors);
+    sk_BIGNUM_free(exps);
+    sk_BIGNUM_free(coeffs);
+    return 1;
+
+ err:
+    BN_free(n);
+    BN_free(e);
+    BN_free(d);
+    sk_BIGNUM_pop_free(factors, BN_free);
+    sk_BIGNUM_pop_free(exps, BN_free);
+    sk_BIGNUM_pop_free(coeffs, BN_free);
+    return 0;
+}
+
diff --git a/doc/man3/EVP_PKEY_set_type.pod b/doc/man3/EVP_PKEY_set_type.pod
new file mode 100644
index 0000000000..e5111a555b
--- /dev/null
+++ b/doc/man3/EVP_PKEY_set_type.pod
@@ -0,0 +1,68 @@
+=pod
+
+=head1 NAME
+
+EVP_PKEY_set_type, EVP_PKEY_set_type_str, EVP_PKEY_set_type_by_keymgmt
+- functions to change the EVP_PKEY type
+
+=head1 SYNOPSIS
+
+ #include <openssl/evp.h>
+
+ int EVP_PKEY_set_type(EVP_PKEY *pkey, int type);
+ int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len);
+ int EVP_PKEY_set_type_by_keymgmt(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt);
+
+=head1 DESCRIPTION
+
+All the functions described here behave the same in so far that they
+clear all the previous key data and methods from I<pkey>, and reset it
+to be of the type of key given by the different arguments.  If
+I<pkey> is NULL, these functions will still return the same return
+values as if it wasn't.
+
+EVP_PKEY_set_type() initialises I<pkey> to contain an internal legacy
+key.  When doing this, it finds a L<EVP_PKEY_ASN1_METHOD(3)>
+corresponding to I<type>, and associates I<pkey> with the findings.
+It is an error if no L<EVP_PKEY_ASN1_METHOD(3)> could be found for
+I<type>.
+
+EVP_PKEY_set_type_str() initialises I<pkey> to contain an internal legacy
+key. When doing this, it finds a L<EVP_PKEY_ASN1_METHOD(3)>
+corresponding to I<str> that has then length I<len>, and associates
+I<pkey> with the findings.
+It is an error if no L<EVP_PKEY_ASN1_METHOD(3)> could be found for
+I<type>.
+
+For both EVP_PKEY_set_type() and EVP_PKEY_set_type_str(), I<pkey> gets
+a numeric type, which can be retrieved with L<EVP_PKEY_id(3)>.  This
+numeric type is taken from the L<EVP_PKEY_ASN1_METHOD(3)> that was
+found, and is equal to or closely related to I<type> in the case of
+EVP_PKEY_set_type(), or related to I<str> in the case of
+EVP_PKEY_set_type_str().
+
+EVP_PKEY_set_type_by_keymgmt() initialises I<pkey> to contain an
+internal provider side key.  When doing this, it associates I<pkey>
+with I<keymgmt>.  For keys initialised like this, the numeric type
+retrieved with L<EVP_PKEY_id(3)> will always be B<EVP_PKEY_NONE>.
+
+=head1 RETURN VALUES
+
+All functions described here return 1 if successful, or 0 on error.
+
+=head1 SEE ALSO
+
+L<EVP_PKEY_assign(3)>, L<EVP_PKEY_id(3)>, L<EVP_PKEY_get0_RSA(3)>,
+L<EVP_PKEY_copy_parameters(3)>, L<EVP_PKEY_ASN1_METHOD(3)>,
+L<EVP_KEYMGMT(3)>
+
+=head1 COPYRIGHT
+
+Copyright 2020 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
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/include/crypto/asn1.h b/include/crypto/asn1.h
index 0e7e9ba3d4..20732c2251 100644
--- a/include/crypto/asn1.h
+++ b/include/crypto/asn1.h
@@ -11,6 +11,8 @@
 
 /* ASN1 public key method structure */
 
+#include <openssl/core.h>
+
 struct evp_pkey_asn1_method_st {
     int pkey_id;
     int pkey_base_id;
@@ -68,10 +70,11 @@ struct evp_pkey_asn1_method_st {
      * TODO: Make sure these functions are defined for key types that are
      * implemented in providers.
      */
-    /* Exports to providers */
+    /* Exports and imports to / from providers */
     size_t (*dirty_cnt) (const EVP_PKEY *pk);
     int (*export_to) (const EVP_PKEY *pk, void *to_keydata,
                       EVP_KEYMGMT *to_keymgmt);
+    OSSL_CALLBACK *import_from;
 } /* EVP_PKEY_ASN1_METHOD */ ;
 
 DEFINE_STACK_OF_CONST(EVP_PKEY_ASN1_METHOD)
diff --git a/include/crypto/dh.h b/include/crypto/dh.h
index 7c7cebdc16..5d5470f165 100644
--- a/include/crypto/dh.h
+++ b/include/crypto/dh.h
@@ -7,6 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <openssl/core.h>
 #include <openssl/dh.h>
 #include "internal/ffc.h"
 
@@ -19,6 +20,7 @@ int dh_generate_public_key(BN_CTX *ctx, DH *dh, const BIGNUM *priv_key,
 
 FFC_PARAMS *dh_get0_params(DH *dh);
 int dh_get0_nid(const DH *dh);
+int dh_key_fromdata(DH *dh, const OSSL_PARAM params[]);
 
 int dh_check_pub_key_partial(const DH *dh, const BIGNUM *pub_key, int *ret);
 int dh_check_priv_key(const DH *dh, const BIGNUM *priv_key, int *ret);
diff --git a/include/crypto/dsa.h b/include/crypto/dsa.h
index eab5d44603..0afec99ae6 100644
--- a/include/crypto/dsa.h
+++ b/include/crypto/dsa.h
@@ -7,6 +7,7 @@
  * https://www.openssl.org/source/license.html
  */
 
+#include <openssl/core.h>
 #include <openssl/dsa.h>
 #include "internal/ffc.h"
 
@@ -24,6 +25,7 @@ int dsa_sign_int(int type, const unsigned char *dgst,
 const unsigned char *dsa_algorithmidentifier_encoding(int md_nid, size_t *len);
 
 FFC_PARAMS *dsa_get0_params(DSA *dsa);
+int dsa_key_fromdata(DSA *dsa, const OSSL_PARAM params[]);
 
 int dsa_generate_public_key(BN_CTX *ctx, const DSA *dsa, const BIGNUM *priv_key,
                             BIGNUM *pub_key);
diff --git a/include/crypto/ec.h b/include/crypto/ec.h
index 00b1b25aff..91fd9ebac9 100644
--- a/include/crypto/ec.h
+++ b/include/crypto/ec.h
@@ -14,6 +14,7 @@
 # include <openssl/opensslconf.h>
 
 # ifndef OPENSSL_NO_EC
+#  include <openssl/core.h>
 #  include <openssl/ec.h>
 
 /*-
@@ -56,5 +57,12 @@ OPENSSL_CTX *ec_key_get_libctx(const EC_KEY *eckey);
 const char *ec_curve_nid2name(int nid);
 int ec_curve_name2nid(const char *name);
 const unsigned char *ecdsa_algorithmidentifier_encoding(int md_nid, size_t *len);
+
+/* Backend support */
+int ec_key_fromdata(EC_KEY *ecx, const OSSL_PARAM params[], int include_private);
+int ec_key_domparams_fromdata(EC_KEY *ecx, const OSSL_PARAM params[]);
+int ec_key_otherparams_fromdata(EC_KEY *ec, const OSSL_PARAM params[]);
+int ec_set_param_ecdh_cofactor_mode(EC_KEY *ec, const OSSL_PARAM *p);
+
 # endif /* OPENSSL_NO_EC */
 #endif
diff --git a/include/crypto/ecx.h b/include/crypto/ecx.h
index 8afb104438..41020a22b3 100644
--- a/include/crypto/ecx.h
+++ b/include/crypto/ecx.h
@@ -15,6 +15,7 @@
 
 # ifndef OPENSSL_NO_EC
 
+#  include <openssl/core.h>
 #  include <openssl/e_os2.h>
 #  include <openssl/crypto.h>
 #  include "internal/refcount.h"
@@ -107,5 +108,9 @@ int s390x_x448_mul(unsigned char u_dst[56],
                    const unsigned char u_src[56],
                    const unsigned char d_src[56]);
 
+/* Backend support */
+int ecx_key_fromdata(ECX_KEY *ecx, const OSSL_PARAM params[],
+                     int include_private);
+
 # endif /* OPENSSL_NO_EC */
 #endif
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 2e0322fa98..63b6dad9c6 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -502,14 +502,31 @@ const EVP_CIPHER *EVP_##cname##_ecb(void) { return &cname##_ecb; }
                              cipher##_init_key, NULL, NULL, NULL, NULL)
 
 /*
- * Type needs to be a bit field Sub-type needs to be for variations on the
- * method, as in, can it do arbitrary encryption....
+ * An EVP_PKEY can have the following states:
+ *
+ * untyped & empty:
+ *
+ *     type == EVP_PKEY_NONE && keymgmt == NULL
+ *
+ * typed & empty:
+ *
+ *     (type != EVP_PKEY_NONE && pkey.ptr == NULL)      ## legacy (libcrypto only)
+ *     || (keymgmt != NULL && keydata == NULL)          ## provider side
+ *
+ * fully assigned:
+ *
+ *     (type != EVP_PKEY_NONE && pkey.ptr != NULL)      ## legacy (libcrypto only)
+ *     || (keymgmt != NULL && keydata != NULL)          ## provider side
+ *
+ * The easiest way to detect a legacy key is:           type != EVP_PKEY_NONE
+ * The easiest way to detect a provider side key is:    keymgmt != NULL
  */
 struct evp_pkey_st {
     /* == Legacy attributes == */
     int type;
     int save_type;
 
+# ifndef FIPS_MODE
     /*
      * Legacy key "origin" is composed of a pointer to an EVP_PKEY_ASN1_METHOD,
      * a pointer to a low level key and possibly a pointer to an engine.
@@ -519,20 +536,21 @@ struct evp_pkey_st {
     ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
     union {
         void *ptr;
-# ifndef OPENSSL_NO_RSA
+#  ifndef OPENSSL_NO_RSA
         struct rsa_st *rsa;     /* RSA */
-# endif
-# ifndef OPENSSL_NO_DSA
+#  endif
+#  ifndef OPENSSL_NO_DSA
         struct dsa_st *dsa;     /* DSA */
-# endif
-# ifndef OPENSSL_NO_DH
+#  endif
+#  ifndef OPENSSL_NO_DH
         struct dh_st *dh;       /* DH */
-# endif
-# ifndef OPENSSL_NO_EC
+#  endif
+#  ifndef OPENSSL_NO_EC
         struct ec_key_st *ec;   /* ECC */
         ECX_KEY *ecx;           /* X25519, X448, Ed25519, Ed448 */
-# endif
+#  endif
     } pkey;
+# endif
 
     /* == Common attributes == */
     CRYPTO_REF_COUNT references;
@@ -612,10 +630,8 @@ void evp_app_cleanup_int(void);
 void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
                                   EVP_KEYMGMT **keymgmt,
                                   const char *propquery);
-void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
-                                   EVP_KEYMGMT **keymgmt,
-                                   const char *propquery);
 #ifndef FIPS_MODE
+int evp_pkey_downgrade(EVP_PKEY *pk);
 void evp_pkey_free_legacy(EVP_PKEY *x);
 #endif
 
diff --git a/include/crypto/rsa.h b/include/crypto/rsa.h
index 09335fafe4..a92e666a3d 100644
--- a/include/crypto/rsa.h
+++ b/include/crypto/rsa.h
@@ -10,6 +10,7 @@
 #ifndef OSSL_INTERNAL_RSA_H
 # define OSSL_INTERNAL_RSA_H
 
+#include <openssl/core.h>
 #include <openssl/rsa.h>
 
 RSA *rsa_new_with_ctx(OPENSSL_CTX *libctx);
@@ -20,6 +21,7 @@ int rsa_set0_all_params(RSA *r, const STACK_OF(BIGNUM) *primes,
 int rsa_get0_all_params(RSA *r, STACK_OF(BIGNUM_const) *primes,
                         STACK_OF(BIGNUM_const) *exps,
                         STACK_OF(BIGNUM_const) *coeffs);
+int rsa_fromdata(RSA *rsa, const OSSL_PARAM params[]);
 
 int rsa_padding_check_PKCS1_type_2_TLS(OPENSSL_CTX *ctx, unsigned char *to,
                                        size_t tlen, const unsigned char *from,
diff --git a/include/internal/ffc.h b/include/internal/ffc.h
index 006be73d8c..fd1007631e 100644
--- a/include/internal/ffc.h
+++ b/include/internal/ffc.h
@@ -10,6 +10,7 @@
 #ifndef OSSL_INTERNAL_FFC_H
 # define OSSL_INTERNAL_FFC_H
 
+# include <openssl/core.h>
 # include <openssl/bn.h>
 # include <openssl/evp.h>
 # include <openssl/dh.h> /* Uses Error codes from DH */
@@ -154,4 +155,6 @@ int ffc_validate_public_key_partial(const FFC_PARAMS *params,
 int ffc_validate_private_key(const BIGNUM *upper, const BIGNUM *priv_key,
                              int *ret);
 
+int ffc_fromdata(FFC_PARAMS *ffc, const OSSL_PARAM params[]);
+
 #endif /* OSSL_INTERNAL_FFC_H */
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 3487b27e0a..d461f24999 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1112,6 +1112,7 @@ int EVP_PKEY_security_bits(const EVP_PKEY *pkey);
 int EVP_PKEY_size(const EVP_PKEY *pkey);
 int EVP_PKEY_set_type(EVP_PKEY *pkey, int type);
 int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len);
+int EVP_PKEY_set_type_by_keymgmt(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt);
 int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type);
 # ifndef OPENSSL_NO_ENGINE
 int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *e);
diff --git a/include/openssl/evperr.h b/include/openssl/evperr.h
index 994268af91..9290cfff94 100644
--- a/include/openssl/evperr.h
+++ b/include/openssl/evperr.h
@@ -10,12 +10,6 @@
 
 #ifndef OPENSSL_EVPERR_H
 # define OPENSSL_EVPERR_H
-# pragma once
-
-# include <openssl/macros.h>
-# ifndef OPENSSL_NO_DEPRECATED_3_0
-#  define HEADER_EVPERR_H
-# endif
 
 # include <openssl/opensslconf.h>
 # include <openssl/symhacks.h>
@@ -199,6 +193,8 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_FIPS_MODE_NOT_SUPPORTED                    167
 # define EVP_R_GET_RAW_KEY_FAILED                         182
 # define EVP_R_ILLEGAL_SCRYPT_PARAMETERS                  171
+# define EVP_R_INACCESSIBLE_DOMAIN_PARAMETERS             204
+# define EVP_R_INACCESSIBLE_KEY                           203
 # define EVP_R_INITIALIZATION_ERROR                       134
 # define EVP_R_INPUT_NOT_INITIALIZED                      111
 # define EVP_R_INVALID_CUSTOM_LENGTH                      185
@@ -211,6 +207,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_INVALID_PROVIDER_FUNCTIONS                 193
 # define EVP_R_INVALID_SALT_LENGTH                        186
 # define EVP_R_KEYGEN_FAILURE                             120
+# define EVP_R_KEYMGMT_EXPORT_FAILURE                     205
 # define EVP_R_KEY_SETUP_FAILED                           180
 # define EVP_R_MEMORY_LIMIT_EXCEEDED                      172
 # define EVP_R_MESSAGE_DIGEST_IS_NULL                     159
@@ -221,6 +218,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_NO_CIPHER_SET                              131
 # define EVP_R_NO_DEFAULT_DIGEST                          158
 # define EVP_R_NO_DIGEST_SET                              139
+# define EVP_R_NO_IMPORT_FUNCTION                         206
 # define EVP_R_NO_KEYMGMT_AVAILABLE                       199
 # define EVP_R_NO_KEYMGMT_PRESENT                         196
 # define EVP_R_NO_KEY_SET                                 154
@@ -238,6 +236,7 @@ int ERR_load_EVP_strings(void);
 # define EVP_R_TOO_MANY_RECORDS                           183
 # define EVP_R_UNKNOWN_CIPHER                             160
 # define EVP_R_UNKNOWN_DIGEST                             161
+# define EVP_R_UNKNOWN_KEY_TYPE                           207
 # define EVP_R_UNKNOWN_OPTION                             169
 # define EVP_R_UNKNOWN_PBE_ALGORITHM                      121
 # define EVP_R_UNSUPPORTED_ALGORITHM                      156
diff --git a/providers/implementations/keymgmt/dh_kmgmt.c b/providers/implementations/keymgmt/dh_kmgmt.c
index 6a6a06cc86..4ec48feee7 100644
--- a/providers/implementations/keymgmt/dh_kmgmt.c
+++ b/providers/implementations/keymgmt/dh_kmgmt.c
@@ -39,32 +39,6 @@ static OSSL_OP_keymgmt_export_types_fn dh_export_types;
 #define DH_POSSIBLE_SELECTIONS                 \
     (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)
 
-static int params_to_domparams(DH *dh, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *param_p, *param_g;
-    BIGNUM *p = NULL, *g = NULL;
-
-    if (dh == NULL)
-        return 0;
-
-    param_p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_P);
-    param_g = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_G);
-
-    if ((param_p != NULL && !OSSL_PARAM_get_BN(param_p, &p))
-        || (param_g != NULL && !OSSL_PARAM_get_BN(param_g, &g)))
-        goto err;
-
-    if (!DH_set0_pqg(dh, p, NULL, g))
-        goto err;
-
-    return 1;
-
- err:
-    BN_free(p);
-    BN_free(g);
-    return 0;
-}
-
 static int domparams_to_params(DH *dh, OSSL_PARAM_BLD *tmpl)
 {
     const BIGNUM *dh_p = NULL, *dh_g = NULL;
@@ -83,47 +57,6 @@ static int domparams_to_params(DH *dh, OSSL_PARAM_BLD *tmpl)
     return 1;
 }
 
-static int params_to_key(DH *dh, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *param_priv_key, *param_pub_key;
-    BIGNUM *priv_key = NULL, *pub_key = NULL;
-
-    if (dh == NULL)
-        return 0;
-
-    if (!params_to_domparams(dh, params))
-        return 0;
-
-    param_priv_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
-    param_pub_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
-
-    /*
-     * DH documentation says that a public key must be present if a
-     * private key is present.
-     * We want to have at least a public key either way, so we end up
-     * requiring it unconditionally.
-     */
-    if (param_pub_key == NULL)
-        return 0;
-
-    if ((param_priv_key != NULL
-         && !OSSL_PARAM_get_BN(param_priv_key, &priv_key))
-        || !OSSL_PARAM_get_BN(param_pub_key, &pub_key))
-        goto err;
-
-    if (!DH_set0_key(dh, pub_key, priv_key))
-        goto err;
-
-    return 1;
-
- err:
-    BN_clear_free(priv_key);
-    BN_free(pub_key);
-    return 0;
-}
-
 static int key_to_params(DH *dh, OSSL_PARAM_BLD *tmpl)
 {
     const BIGNUM *priv_key = NULL, *pub_key = NULL;
@@ -159,15 +92,17 @@ static int dh_has(void *keydata, int selection)
     DH *dh = keydata;
     int ok = 0;
 
-    if ((selection & DH_POSSIBLE_SELECTIONS) != 0)
-        ok = 1;
+    if (dh != NULL) {
+        if ((selection & DH_POSSIBLE_SELECTIONS) != 0)
+            ok = 1;
 
-    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
-        ok = ok && (DH_get0_pub_key(dh) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ok && (DH_get0_priv_key(dh) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
-        ok = ok && (DH_get0_p(dh) != NULL && DH_get0_g(dh) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+            ok = ok && (DH_get0_pub_key(dh) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+            ok = ok && (DH_get0_priv_key(dh) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+            ok = ok && (DH_get0_p(dh) != NULL && DH_get0_g(dh) != NULL);
+    }
     return ok;
 }
 
@@ -202,9 +137,9 @@ static int dh_import(void *keydata, int selection, const OSSL_PARAM params[])
         ok = 1;
 
     if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)
-        ok = ok && params_to_domparams(dh, params);
+        ok = ok && ffc_fromdata(dh_get0_params(dh), params);
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
-        ok = ok && params_to_key(dh, params);
+        ok = ok && dh_key_fromdata(dh, params);
 
     return ok;
 }
diff --git a/providers/implementations/keymgmt/dsa_kmgmt.c b/providers/implementations/keymgmt/dsa_kmgmt.c
index a4821f94c3..080ba743da 100644
--- a/providers/implementations/keymgmt/dsa_kmgmt.c
+++ b/providers/implementations/keymgmt/dsa_kmgmt.c
@@ -39,35 +39,6 @@ static OSSL_OP_keymgmt_export_types_fn dsa_export_types;
 #define DSA_POSSIBLE_SELECTIONS                 \
     (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)
 
-static int params_to_domparams(DSA *dsa, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *param_p, *param_q, *param_g;
-    BIGNUM *p = NULL, *q = NULL, *g = NULL;
-
-    if (dsa == NULL)
-        return 0;
-
-    param_p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_P);
-    param_q = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_Q);
-    param_g = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_G);
-
-    if ((param_p != NULL && !OSSL_PARAM_get_BN(param_p, &p))
-        || (param_q != NULL && !OSSL_PARAM_get_BN(param_q, &q))
-        || (param_g != NULL && !OSSL_PARAM_get_BN(param_g, &g)))
-        goto err;
-
-    if (!DSA_set0_pqg(dsa, p, q, g))
-        goto err;
-
-    return 1;
-
- err:
-    BN_free(p);
-    BN_free(q);
-    BN_free(g);
-    return 0;
-}
-
 static int domparams_to_params(DSA *dsa, OSSL_PARAM_BLD *tmpl)
 {
     const BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
@@ -89,46 +60,6 @@ static int domparams_to_params(DSA *dsa, OSSL_PARAM_BLD *tmpl)
     return 1;
 }
 
-static int params_to_key(DSA *dsa, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *param_priv_key, *param_pub_key;
-    BIGNUM *priv_key = NULL, *pub_key = NULL;
-
-    if (dsa == NULL)
-        return 0;
-
-    if (!params_to_domparams(dsa, params))
-        return 0;
-
-    param_priv_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
-    param_pub_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
-
-    /*
-     * DSA documentation says that a public key must be present if a private key
-     * is.
-     */
-    if (param_priv_key != NULL && param_pub_key == NULL)
-        return 0;
-
-    if ((param_priv_key != NULL
-         && !OSSL_PARAM_get_BN(param_priv_key, &priv_key))
-        || (param_pub_key != NULL
-            && !OSSL_PARAM_get_BN(param_pub_key, &pub_key)))
-        goto err;
-
-    if (pub_key != NULL && !DSA_set0_key(dsa, pub_key, priv_key))
-        goto err;
-
-    return 1;
-
- err:
-    BN_clear_free(priv_key);
-    BN_free(pub_key);
-    return 0;
-}
-
 static int key_to_params(DSA *dsa, OSSL_PARAM_BLD *tmpl)
 {
     const BIGNUM *priv_key = NULL, *pub_key = NULL;
@@ -164,15 +95,17 @@ static int dsa_has(void *keydata, int selection)
     DSA *dsa = keydata;
     int ok = 0;
 
-    if ((selection & DSA_POSSIBLE_SELECTIONS) != 0)
-        ok = 1;
+    if (dsa != NULL) {
+        if ((selection & DSA_POSSIBLE_SELECTIONS) != 0)
+            ok = 1;
 
-    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
-        ok = ok && (DSA_get0_pub_key(dsa) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ok && (DSA_get0_priv_key(dsa) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
-        ok = ok && (DSA_get0_p(dsa) != NULL && DSA_get0_g(dsa) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+            ok = ok && (DSA_get0_pub_key(dsa) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+            ok = ok && (DSA_get0_priv_key(dsa) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+            ok = ok && (DSA_get0_p(dsa) != NULL && DSA_get0_g(dsa) != NULL);
+    }
     return ok;
 }
 
@@ -209,9 +142,9 @@ static int dsa_import(void *keydata, int selection, const OSSL_PARAM params[])
         ok = 1;
 
     if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)
-        ok = ok && params_to_domparams(dsa, params);
+        ok = ok && ffc_fromdata(dsa_get0_params(dsa), params);
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
-        ok = ok && params_to_key(dsa, params);
+        ok = ok && dsa_key_fromdata(dsa, params);
 
     return ok;
 }
diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c
index 47872553b3..354ca0c5bb 100644
--- a/providers/implementations/keymgmt/ec_kmgmt.c
+++ b/providers/implementations/keymgmt/ec_kmgmt.c
@@ -55,55 +55,6 @@ const char *ec_query_operation_name(int operation_id)
     return NULL;
 }
 
-static ossl_inline
-int params_to_domparams(EC_KEY *ec, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *param_ec_name;
-    EC_GROUP *ecg = NULL;
-    char *curve_name = NULL;
-    int ok = 0;
-
-    if (ec == NULL)
-        return 0;
-
-    param_ec_name = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME);
-    if (param_ec_name == NULL) {
-        /* explicit parameters */
-
-        /*
-         * TODO(3.0): should we support explicit parameters curves?
-         */
-        return 0;
-    } else {
-        /* named curve */
-        int curve_nid;
-
-        if (!OSSL_PARAM_get_utf8_string(param_ec_name, &curve_name, 0)
-                || curve_name == NULL
-                || (curve_nid = ec_curve_name2nid(curve_name)) == NID_undef)
-            goto err;
-
-        if ((ecg = EC_GROUP_new_by_curve_name_ex(ec_key_get_libctx(ec),
-                                                 curve_nid)) == NULL)
-            goto err;
-    }
-
-    if (!EC_KEY_set_group(ec, ecg))
-        goto err;
-
-    /*
-     * TODO(3.0): if the group has changed, should we invalidate the private and
-     * public key?
-     */
-
-    ok = 1;
-
- err:
-    OPENSSL_free(curve_name);
-    EC_GROUP_free(ecg);
-    return ok;
-}
-
 static ossl_inline
 int domparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl)
 {
@@ -141,120 +92,6 @@ int domparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl)
     return 1;
 }
 
-/*
- * Callers of params_to_key MUST make sure that params_to_domparams has been
- * called before!
- *
- * This function only imports the bare keypair, domain parameters and other
- * parameters are imported separately, and domain parameters are required to
- * define a keypair.
- */
-static ossl_inline
-int params_to_key(EC_KEY *ec, const OSSL_PARAM params[], int include_private)
-{
-    const OSSL_PARAM *param_priv_key, *param_pub_key;
-    BN_CTX *ctx = NULL;
-    BIGNUM *priv_key = NULL;
-    unsigned char *pub_key = NULL;
-    size_t pub_key_len;
-    const EC_GROUP *ecg = NULL;
-    EC_POINT *pub_point = NULL;
-    int ok = 0;
-
-    ecg = EC_KEY_get0_group(ec);
-    if (ecg == NULL)
-        return 0;
-
-    param_priv_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
-    param_pub_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
-
-    ctx = BN_CTX_new_ex(ec_key_get_libctx(ec));
-    if (ctx == NULL)
-        goto err;
-    /*
-     * We want to have at least a public key either way, so we end up
-     * requiring it unconditionally.
-     */
-    if (param_pub_key == NULL
-            || !OSSL_PARAM_get_octet_string(param_pub_key,
-                                            (void **)&pub_key, 0, &pub_key_len)
-            || (pub_point = EC_POINT_new(ecg)) == NULL
-            || !EC_POINT_oct2point(ecg, pub_point,
-                                   pub_key, pub_key_len, ctx))
-        goto err;
-
-    if (param_priv_key != NULL && include_private) {
-        int fixed_top;
-        const BIGNUM *order;
-
-        /*
-         * Key import/export should never leak the bit length of the secret
-         * scalar in the key.
-         *
-         * For this reason, on export we use padded BIGNUMs with fixed length.
-         *
-         * When importing we also should make sure that, even if short lived,
-         * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
-         * soon as possible, so that any processing of this BIGNUM might opt for
-         * constant time implementations in the backend.
-         *
-         * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
-         * to preallocate the BIGNUM internal buffer to a fixed public size big
-         * enough that operations performed during the processing never trigger
-         * a realloc which would leak the size of the scalar through memory
-         * accesses.
-         *
-         * Fixed Length
-         * ------------
-         *
-         * The order of the large prime subgroup of the curve is our choice for
-         * a fixed public size, as that is generally the upper bound for
-         * generating a private key in EC cryptosystems and should fit all valid
-         * secret scalars.
-         *
-         * For padding on export we just use the bit length of the order
-         * converted to bytes (rounding up).
-         *
-         * For preallocating the BIGNUM storage we look at the number of "words"
-         * required for the internal representation of the order, and we
-         * preallocate 2 extra "words" in case any of the subsequent processing
-         * might temporarily overflow the order length.
-         */
-        order = EC_GROUP_get0_order(ecg);
-        if (order == NULL || BN_is_zero(order))
-            goto err;
-
-        fixed_top = bn_get_top(order) + 2;
-
-        if ((priv_key = BN_secure_new()) == NULL)
-            goto err;
-        if (bn_wexpand(priv_key, fixed_top) == NULL)
-            goto err;
-        BN_set_flags(priv_key, BN_FLG_CONSTTIME);
-
-        if (!OSSL_PARAM_get_BN(param_priv_key, &priv_key))
-            goto err;
-    }
-
-    if (priv_key != NULL
-            && !EC_KEY_set_private_key(ec, priv_key))
-        goto err;
-
-    if (!EC_KEY_set_public_key(ec, pub_point))
-        goto err;
-
-    ok = 1;
-
- err:
-    BN_CTX_free(ctx);
-    BN_clear_free(priv_key);
-    OPENSSL_free(pub_key);
-    EC_POINT_free(pub_point);
-    return ok;
-}
-
 /*
  * Callers of key_to_params MUST make sure that domparams_to_params is also
  * called!
@@ -346,56 +183,6 @@ int key_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl, int include_private
     return ret;
 }
 
-static ossl_inline
-int ec_set_param_ecdh_cofactor_mode(EC_KEY *ec, const OSSL_PARAM *p)
-{
-    const EC_GROUP *ecg = EC_KEY_get0_group(ec);
-    const BIGNUM *cofactor;
-    int mode;
-
-    if (!OSSL_PARAM_get_int(p, &mode))
-        return 0;
-
-    /*
-     * mode can be only 0 for disable, or 1 for enable here.
-     *
-     * This is in contrast with the same parameter on an ECDH EVP_PKEY_CTX that
-     * also supports mode == -1 with the meaning of "reset to the default for
-     * the associated key".
-     */
-    if (mode < 0 || mode > 1)
-        return 0;
-
-    if ((cofactor = EC_GROUP_get0_cofactor(ecg)) == NULL )
-        return 0;
-
-    /* ECDH cofactor mode has no effect if cofactor is 1 */
-    if (BN_is_one(cofactor))
-        return 1;
-
-    if (mode == 1)
-        EC_KEY_set_flags(ec, EC_FLAG_COFACTOR_ECDH);
-    else if (mode == 0)
-        EC_KEY_clear_flags(ec, EC_FLAG_COFACTOR_ECDH);
-
-    return 1;
-}
-
-static ossl_inline
-int params_to_otherparams(EC_KEY *ec, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *p;
-
-    if (ec == NULL)
-        return 0;
-
-    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH);
-    if (p != NULL && !ec_set_param_ecdh_cofactor_mode(ec, p))
-        return 0;
-
-    return 1;
-}
-
 static ossl_inline
 int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl)
 {
@@ -432,21 +219,22 @@ int ec_has(void *keydata, int selection)
     EC_KEY *ec = keydata;
     int ok = 0;
 
-    if ((selection & EC_POSSIBLE_SELECTIONS) != 0)
-        ok = 1;
-
-    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
-        ok = ok && (EC_KEY_get0_public_key(ec) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ok && (EC_KEY_get0_private_key(ec) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
-        ok = ok && (EC_KEY_get0_group(ec) != NULL);
-    /*
-     * We consider OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS to always be available,
-     * so no extra check is needed other than the previous one against
-     * EC_POSSIBLE_SELECTIONS.
-     */
+    if (ec != NULL) {
+        if ((selection & EC_POSSIBLE_SELECTIONS) != 0)
+            ok = 1;
 
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+            ok = ok && (EC_KEY_get0_public_key(ec) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+            ok = ok && (EC_KEY_get0_private_key(ec) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+            ok = ok && (EC_KEY_get0_group(ec) != NULL);
+        /*
+         * We consider OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS to always be
+         * available, so no extra check is needed other than the previous one
+         * against EC_POSSIBLE_SELECTIONS.
+         */
+    }
     return ok;
 }
 
@@ -508,15 +296,15 @@ int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
         return 0;
 
     if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
-        ok = ok && params_to_domparams(ec, params);
+        ok = ok && ec_key_domparams_fromdata(ec, params);
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
         int include_private =
             selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
 
-        ok = ok && params_to_key(ec, params, include_private);
+        ok = ok && ec_key_fromdata(ec, params, include_private);
     }
     if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
-        ok = ok && params_to_otherparams(ec, params);
+        ok = ok && ec_key_otherparams_fromdata(ec, params);
 
     return ok;
 }
diff --git a/providers/implementations/keymgmt/ecx_kmgmt.c b/providers/implementations/keymgmt/ecx_kmgmt.c
index 6450fbb22e..221287bff2 100644
--- a/providers/implementations/keymgmt/ecx_kmgmt.c
+++ b/providers/implementations/keymgmt/ecx_kmgmt.c
@@ -56,26 +56,26 @@ static void *ed448_new_key(void *provctx)
 static int ecx_has(void *keydata, int selection)
 {
     ECX_KEY *key = keydata;
-    int ok = 1;
-
-    if ((selection & ECX_POSSIBLE_SELECTIONS) == 0)
-        return 0;
+    int ok = 0;
 
-    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
-        ok = ok && key->haspubkey;
+    if (key != NULL) {
+        if ((selection & ECX_POSSIBLE_SELECTIONS) != 0)
+            ok = 1;
 
-    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ok && key->privkey != NULL;
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+            ok = ok && key->haspubkey;
 
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+            ok = ok && key->privkey != NULL;
+    }
     return ok;
 }
 
 static int ecx_import(void *keydata, int selection, const OSSL_PARAM params[])
 {
     ECX_KEY *key = keydata;
-    size_t privkeylen = 0, pubkeylen;
-    const OSSL_PARAM *param_priv_key = NULL, *param_pub_key;
-    unsigned char *pubkey;
+    int ok = 1;
+    int include_private = 0;
 
     if (key == NULL)
         return 0;
@@ -83,38 +83,11 @@ static int ecx_import(void *keydata, int selection, const OSSL_PARAM params[])
     if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
         return 0;
 
-    param_pub_key =
-        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
-
-    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        param_priv_key =
-            OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
-    /*
-     * If a private key is present then a public key must also be present.
-     * Alternatively we've just got a public key.
-     */
-    if (param_pub_key == NULL)
-        return 0;
-
-    if (param_priv_key != NULL
-             && !OSSL_PARAM_get_octet_string(param_priv_key,
-                                            (void **)&key->privkey, key->keylen,
-                                             &privkeylen))
-        return 0;
-
-    pubkey = key->pubkey;
-    if (!OSSL_PARAM_get_octet_string(param_pub_key,
-                                     (void **)&pubkey,
-                                     sizeof(key->pubkey), &pubkeylen))
-        return 0;
-
-    if (pubkeylen != key->keylen
-            || (param_priv_key != NULL && privkeylen != key->keylen))
-        return 0;
-
-    key->haspubkey = 1;
+    include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+        ok = ok && ecx_key_fromdata(key, params, include_private);
 
-    return 1;
+    return ok;
 }
 
 static int key_to_params(ECX_KEY *key, OSSL_PARAM_BLD *tmpl)
diff --git a/providers/implementations/keymgmt/rsa_kmgmt.c b/providers/implementations/keymgmt/rsa_kmgmt.c
index 4e77f5c4a7..176cf34e0a 100644
--- a/providers/implementations/keymgmt/rsa_kmgmt.c
+++ b/providers/implementations/keymgmt/rsa_kmgmt.c
@@ -51,80 +51,6 @@ static OSSL_OP_keymgmt_export_types_fn rsa_export_types;
 DEFINE_STACK_OF(BIGNUM)
 DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
 
-static int collect_numbers(STACK_OF(BIGNUM) *numbers,
-                           const OSSL_PARAM params[], const char *key)
-{
-    const OSSL_PARAM *p = NULL;
-
-    if (numbers == NULL)
-        return 0;
-
-    for (p = params; (p = OSSL_PARAM_locate_const(p, key)) != NULL; p++) {
-        BIGNUM *tmp = NULL;
-
-        if (!OSSL_PARAM_get_BN(p, &tmp))
-            return 0;
-        sk_BIGNUM_push(numbers, tmp);
-    }
-
-    return 1;
-}
-
-static int params_to_key(RSA *rsa, const OSSL_PARAM params[])
-{
-    const OSSL_PARAM *param_n, *param_e,  *param_d;
-    BIGNUM *n = NULL, *e = NULL, *d = NULL;
-    STACK_OF(BIGNUM) *factors = NULL, *exps = NULL, *coeffs = NULL;
-    int is_private = 0;
-
-    if (rsa == NULL)
-        return 0;
-
-    param_n = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_N);
-    param_e = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E);
-    param_d = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_D);
-
-    if ((param_n != NULL && !OSSL_PARAM_get_BN(param_n, &n))
-        || (param_e != NULL && !OSSL_PARAM_get_BN(param_e, &e))
-        || (param_d != NULL && !OSSL_PARAM_get_BN(param_d, &d)))
-        goto err;
-
-    is_private = (d != NULL);
-
-    if (!RSA_set0_key(rsa, n, e, d))
-        goto err;
-    n = e = d = NULL;
-
-    if (is_private) {
-        if (!collect_numbers(factors = sk_BIGNUM_new_null(), params,
-                             OSSL_PKEY_PARAM_RSA_FACTOR)
-            || !collect_numbers(exps = sk_BIGNUM_new_null(), params,
-                                OSSL_PKEY_PARAM_RSA_EXPONENT)
-            || !collect_numbers(coeffs = sk_BIGNUM_new_null(), params,
-                                OSSL_PKEY_PARAM_RSA_COEFFICIENT))
-            goto err;
-
-        /* It's ok if this private key just has n, e and d */
-        if (sk_BIGNUM_num(factors) != 0
-            && !rsa_set0_all_params(rsa, factors, exps, coeffs))
-            goto err;
-    }
-
-    sk_BIGNUM_free(factors);
-    sk_BIGNUM_free(exps);
-    sk_BIGNUM_free(coeffs);
-    return 1;
-
- err:
-    BN_free(n);
-    BN_free(e);
-    BN_free(d);
-    sk_BIGNUM_pop_free(factors, BN_free);
-    sk_BIGNUM_pop_free(exps, BN_free);
-    sk_BIGNUM_pop_free(coeffs, BN_free);
-    return 0;
-}
-
 static int export_numbers(OSSL_PARAM_BLD *tmpl, const char *key,
                           STACK_OF(BIGNUM_const) *numbers)
 {
@@ -198,14 +124,19 @@ static int rsa_has(void *keydata, int selection)
     RSA *rsa = keydata;
     int ok = 0;
 
-    if ((selection & RSA_POSSIBLE_SELECTIONS) != 0)
-        ok = 1;
+    if (rsa != NULL) {
+        if ((selection & RSA_POSSIBLE_SELECTIONS) != 0)
+            ok = 1;
 
-    ok = ok && (RSA_get0_e(rsa) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
-        ok = ok && (RSA_get0_n(rsa) != NULL);
-    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
-        ok = ok && (RSA_get0_d(rsa) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+            ok = ok && 0;     /* This will change with PSS and OAEP */
+        if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+            ok = ok && (RSA_get0_e(rsa) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+            ok = ok && (RSA_get0_n(rsa) != NULL);
+        if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+            ok = ok && (RSA_get0_d(rsa) != NULL);
+    }
     return ok;
 }
 
@@ -235,7 +166,7 @@ static int rsa_import(void *keydata, int selection, const OSSL_PARAM params[])
     /* TODO(3.0) PSS and OAEP should bring on parameters */
 
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
-        ok = ok && params_to_key(rsa, params);
+        ok = ok && rsa_fromdata(rsa, params);
 
     return ok;
 }
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
index d0a87b08d3..589261a060 100644
--- a/test/evp_pkey_provided_test.c
+++ b/test/evp_pkey_provided_test.c
@@ -262,7 +262,7 @@ static int test_fromdata_rsa(void)
 {
     int ret = 0;
     EVP_PKEY_CTX *ctx = NULL, *key_ctx = NULL;
-    EVP_PKEY *pk = NULL;
+    EVP_PKEY *pk = NULL, *copy_pk = NULL;
     /*
      * 32-bit RSA key, extracted from this command,
      * executed with OpenSSL 1.0.2:
@@ -310,11 +310,17 @@ static int test_fromdata_rsa(void)
         || !TEST_true(EVP_PKEY_pairwise_check(key_ctx)))
         goto err;
 
+    /* EVP_PKEY_copy_parameters() should fail for RSA */
+    if (!TEST_ptr(copy_pk = EVP_PKEY_new())
+        || !TEST_false(EVP_PKEY_copy_parameters(copy_pk, pk)))
+        goto err;
+
     ret = test_print_key_using_pem("RSA", pk)
           && test_print_key_using_serializer("RSA", pk);
 
  err:
     EVP_PKEY_free(pk);
+    EVP_PKEY_free(copy_pk);
     EVP_PKEY_CTX_free(key_ctx);
     EVP_PKEY_CTX_free(ctx);
 
@@ -332,7 +338,7 @@ static int test_fromdata_dh(void)
 {
     int ret = 0;
     EVP_PKEY_CTX *ctx = NULL, *key_ctx = NULL;
-    EVP_PKEY *pk = NULL;
+    EVP_PKEY *pk = NULL, *copy_pk = NULL;
     /*
      * 32-bit DH key, extracted from this command,
      * executed with OpenSSL 1.0.2:
@@ -364,6 +370,10 @@ static int test_fromdata_dh(void)
         || !TEST_int_eq(EVP_PKEY_size(pk), 4))
         goto err;
 
+    if (!TEST_ptr(copy_pk = EVP_PKEY_new())
+        || !TEST_true(EVP_PKEY_copy_parameters(copy_pk, pk)))
+        goto err;
+
     ret = test_print_key_using_pem("DH", pk)
           && test_print_key_using_serializer("DH", pk);
 
@@ -378,6 +388,7 @@ static int test_fromdata_dh(void)
 
  err:
     EVP_PKEY_free(pk);
+    EVP_PKEY_free(copy_pk);
     EVP_PKEY_CTX_free(ctx);
     EVP_PKEY_CTX_free(key_ctx);
 
@@ -399,7 +410,7 @@ static int test_fromdata_ecx(int tst)
 {
     int ret = 0;
     EVP_PKEY_CTX *ctx = NULL;
-    EVP_PKEY *pk = NULL;
+    EVP_PKEY *pk = NULL, *copy_pk = NULL;
     const char *alg = NULL;
 
     /* ED448_KEYLEN > X448_KEYLEN > X25519_KEYLEN == ED25519_KEYLEN */
@@ -565,11 +576,16 @@ static int test_fromdata_ecx(int tst)
         || !TEST_int_eq(EVP_PKEY_size(pk), size))
         goto err;
 
+    if (!TEST_ptr(copy_pk = EVP_PKEY_new())
+        || !TEST_false(EVP_PKEY_copy_parameters(copy_pk, pk)))
+        goto err;
+
     ret = test_print_key_using_pem(alg, pk)
           && test_print_key_using_serializer(alg, pk);
 
 err:
     EVP_PKEY_free(pk);
+    EVP_PKEY_free(copy_pk);
     EVP_PKEY_CTX_free(ctx);
 
     return ret;
@@ -579,7 +595,7 @@ static int test_fromdata_ec(void)
 {
     int ret = 0;
     EVP_PKEY_CTX *ctx = NULL;
-    EVP_PKEY *pk = NULL;
+    EVP_PKEY *pk = NULL, *copy_pk = NULL;
     OSSL_PARAM_BLD bld;
     BIGNUM *ec_priv_bn = NULL;
     OSSL_PARAM *fromdata_params = NULL;
@@ -630,12 +646,17 @@ static int test_fromdata_ec(void)
         || !TEST_int_eq(EVP_PKEY_size(pk), 2 + 35 * 2))
         goto err;
 
+    if (!TEST_ptr(copy_pk = EVP_PKEY_new())
+        || !TEST_true(EVP_PKEY_copy_parameters(copy_pk, pk)))
+        goto err;
+
     ret = test_print_key_using_pem(alg, pk)
           && test_print_key_using_serializer(alg, pk);
 err:
     BN_free(ec_priv_bn);
     ossl_param_bld_free(fromdata_params);
     EVP_PKEY_free(pk);
+    EVP_PKEY_free(copy_pk);
     EVP_PKEY_CTX_free(ctx);
     return ret;
 }
diff --git a/test/keymgmt_internal_test.c b/test/keymgmt_internal_test.c
index 5ef238ccf1..fd60893a45 100644
--- a/test/keymgmt_internal_test.c
+++ b/test/keymgmt_internal_test.c
@@ -207,14 +207,10 @@ static int test_pass_rsa(FIXTURE *fixture)
         || !TEST_ptr_ne(km1, km2))
         goto err;
 
-    if (!TEST_ptr(evp_pkey_export_to_provider(pk, NULL, &km1, NULL))
-        || !TEST_ptr(evp_pkey_upgrade_to_provider(pk, NULL, &km1, NULL))
-        || !TEST_ptr(provkey = evp_keymgmt_util_export_to_provider(pk, km2)))
-        goto err;
-
-    if (!TEST_true(evp_keymgmt_export(km2, provkey,
-                                      OSSL_KEYMGMT_SELECT_KEYPAIR,
-                                      &export_cb, keydata)))
+    if (!TEST_ptr(provkey = evp_pkey_export_to_provider(pk, NULL, &km1, NULL))
+        || !TEST_true(evp_keymgmt_export(km2, provkey,
+                                         OSSL_KEYMGMT_SELECT_KEYPAIR,
+                                         &export_cb, keydata)))
         goto err;
 
     /*
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 4d57cad19e..ecc735cb94 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5000,3 +5000,4 @@ EVP_PKEY_CTX_set_rsa_keygen_primes      ?	3_0_0	EXIST::FUNCTION:RSA
 NCONF_new_with_libctx                   ?	3_0_0	EXIST::FUNCTION:
 CONF_modules_load_file_with_libctx      ?	3_0_0	EXIST::FUNCTION:
 OPENSSL_CTX_load_config                 ?	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_set_type_by_keymgmt            ?	3_0_0	EXIST::FUNCTION:
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index cf56fae0a6..229b33b4da 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -699,8 +699,6 @@ EVP_PKEY_get_attr_by_OBJ(3)
 EVP_PKEY_get_attr_count(3)
 EVP_PKEY_save_parameters(3)
 EVP_PKEY_set1_tls_encodedpoint(3)
-EVP_PKEY_set_type(3)
-EVP_PKEY_set_type_str(3)
 EVP_add_alg_module(3)
 EVP_add_cipher(3)
 EVP_add_digest(3)


More information about the openssl-commits mailing list