[openssl] master update

Matt Caswell matt at openssl.org
Mon Mar 8 15:25:17 UTC 2021


The branch master has been updated
       via  7bc0fdd3fd4535e06c35b92d71afab9a6de94cc5 (commit)
       via  cc57dc962516410f6269023c8a93913617414b5e (commit)
       via  8e53d94d9971bb29a303dd2295f2f169b1c9a35e (commit)
       via  b574c6a9ac96825b4f19c5e835273bf176174af8 (commit)
       via  ec961f866ac048a2d3dfd6adcfa95042114bef52 (commit)
       via  e8afd78af69d2229a5c36f542b13a54927709901 (commit)
      from  a2c911c2d069b5c6f9e2a8f20764de83a82b1c99 (commit)


- Log -----------------------------------------------------------------
commit 7bc0fdd3fd4535e06c35b92d71afab9a6de94cc5
Author: Matt Caswell <matt at openssl.org>
Date:   Tue Mar 2 15:52:00 2021 +0000

    Make the EVP_PKEY_get0* functions have a const return type
    
    OTC have decided that the EVP_PKEY_get0* functions should have a const
    return type. This is a breaking change to emphasise that these values
    should be considered as immutable.
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14319)

commit cc57dc962516410f6269023c8a93913617414b5e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 25 17:00:38 2021 +0000

    Document the change in behaviour of the the low level key getters/setters
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14319)

commit 8e53d94d9971bb29a303dd2295f2f169b1c9a35e
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Feb 25 16:27:46 2021 +0000

    Ensure the various legacy key EVP_PKEY getters/setters are deprecated
    
    Most of these were already deprecated but a few have been missed. This
    commit corrects that.
    
    Fixes #14303
    Fixes #14317
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14319)

commit b574c6a9ac96825b4f19c5e835273bf176174af8
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Feb 24 16:38:28 2021 +0000

    Cache legacy keys instead of downgrading them
    
    If someone calls an EVP_PKEY_get0*() function then we create a legacy
    key and cache it in the EVP_PKEY - but it doesn't become an "origin" and
    it doesn't ever get updated. This will be documented as a restriction of
    the EVP_PKEY_get0*() function with provided keys.
    
    Fixes #14020
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14319)

commit ec961f866ac048a2d3dfd6adcfa95042114bef52
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Feb 24 15:04:41 2021 +0000

    Avoid a null pointer deref on a malloc failure
    
    Make sure we were sucessful in creating an EVP_PKEY
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14319)

commit e8afd78af69d2229a5c36f542b13a54927709901
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Jan 29 17:25:33 2021 +0000

    Add a multi thread test for downgrading keys
    
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    Reviewed-by: Paul Dale <pauli at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/14319)

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

Summary of changes:
 CHANGES.md                                        |  57 +++++-
 crypto/dh/dh_ameth.c                              |   5 +-
 crypto/ec/ec_ameth.c                              |   5 +-
 crypto/evp/ctrl_params_translate.c                |   8 +-
 crypto/evp/p_dec.c                                |   3 +-
 crypto/evp/p_enc.c                                |   4 +-
 crypto/evp/p_legacy.c                             |  32 +--
 crypto/evp/p_lib.c                                | 235 ++++++++++------------
 crypto/evp/pmeth_gn.c                             |  13 +-
 crypto/evp/pmeth_lib.c                            |   3 +-
 crypto/pem/pvkfmt.c                               |  16 +-
 doc/internal/man3/evp_pkey_export_to_provider.pod |  26 +--
 doc/internal/man7/EVP_PKEY.pod                    |  40 ++--
 doc/man3/EVP_PKEY_set1_RSA.pod                    | 134 ++++++++----
 doc/man7/evp.pod                                  |   4 +-
 include/crypto/evp.h                              |  45 +++--
 include/openssl/evp.h                             |  47 +++--
 test/endecoder_legacy_test.c                      |  12 +-
 test/sslapitest.c                                 |  14 +-
 test/threadstest.c                                |  39 +++-
 util/libcrypto.num                                |  20 +-
 21 files changed, 459 insertions(+), 303 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 33a335e689..c8f8e503ee 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,47 @@ OpenSSL 3.0
 -----------
 
 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx]
+
+ * The deprecated functions EVP_PKEY_get0(), EVP_PKEY_get0_RSA(),
+   EVP_PKEY_get0_DSA(), EVP_PKEY_get0_EC_KEY(), EVP_PKEY_get0_DH(),
+   EVP_PKEY_get0_hmac(), EVP_PKEY_get0_poly1305() and EVP_PKEY_get0_siphash() as
+   well as the similarly named "get1" functions behave slightly differently in
+   OpenSSL 3.0. Previously they returned a pointer to the low-level key used
+   internally by libcrypto. From OpenSSL 3.0 this key may now be held in a
+   provider. Calling these functions will only return a handle on the internal
+   key where the EVP_PKEY was constructed using this key in the first place, for
+   example using a function or macro such as EVP_PKEY_assign_RSA(),
+   EVP_PKEY_set1_RSA(), etc. Where the EVP_PKEY holds a provider managed key,
+   then these functions now return a cached copy of the key. Changes to
+   the internal provider key that take place after the first time the cached key
+   is accessed will not be reflected back in the cached copy. Similarly any
+   changes made to the cached copy by application code will not be reflected
+   back in the internal provider key.
+
+   For the above reasons the keys returned from these functions should typically
+   be treated as read-only. To emphasise this the value returned from
+   EVP_PKEY_get0(), EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(),
+   EVP_PKEY_get0_EC_KEY() and EVP_PKEY_get0_DH() has been made const. This may
+   break some existing code. Applications broken by this change should be
+   modified. The preferred solution is to refactor the code to avoid the use of
+   these deprecated functions. Failing this the code should be modified to use a
+   const pointer instead. The EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(),
+   EVP_PKEY_get1_EC_KEY() and EVP_PKEY_get1_DH() functions continue to return a
+   non-const pointer to enable them to be "freed". However they should also be
+   treated as read-only.
+
+   *Matt Caswell*
+
+ * A number of functions handling low level keys or engines were deprecated
+   including EVP_PKEY_set1_engine(), EVP_PKEY_get0_engine(), EVP_PKEY_assign(),
+   EVP_PKEY_get0(), EVP_PKEY_get0_hmac(), EVP_PKEY_get0_poly1305() and
+   EVP_PKEY_get0_siphash(). Applications using engines should instead use
+   providers. Applications getting or setting low level keys in an EVP_PKEY
+   should instead use the OSSL_ENCODER or OSSL_DECODER APIs, or alternatively
+   use EVP_PKEY_fromdata() or EVP_PKEY_get_params().
+
+   *Matt Caswell*
+
  * Deprecated obsolete EVP_PKEY_CTX_get0_dh_kdf_ukm() and
    EVP_PKEY_CTX_get0_ecdh_kdf_ukm() functions. They are not needed
    and require returning octet ptr parameters from providers that
@@ -35,6 +76,7 @@ OpenSSL 3.0
    be used instead via EVP_RAND(3).
 
    *Paul Dale*
+
  * The SRP APIs have been deprecated. The old APIs do not work via providers,
    and there is no EVP interface to them. Unfortunately there is no replacement
    for these APIs at this time.
@@ -492,12 +534,6 @@ OpenSSL 3.0
 
    *Kurt Roeckx*
 
- * 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.
 
@@ -667,7 +703,7 @@ OpenSSL 3.0
    `EVP_PKEY_set1_DH()` are also deprecated.
    Applications should instead either read or write an
    EVP_PKEY directly using the OSSL_DECODER and OSSL_ENCODER APIs.
-   Or load an    EVP_PKEY directly from DH data using `EVP_PKEY_fromdata()`.
+   Or load an EVP_PKEY directly from DH data using `EVP_PKEY_fromdata()`.
 
    *Paul Dale and Matt Caswell*
 
@@ -695,6 +731,13 @@ OpenSSL 3.0
    time.  Instead applications should use L<EVP_DigestSignInit_ex(3)>,
    L<EVP_DigestSignUpdate(3)> and L<EVP_DigestSignFinal(3)>.
 
+   Finaly functions that assign or obtain DSA objects from an EVP_PKEY such as
+   `EVP_PKEY_assign_DSA()`, `EVP_PKEY_get0_DSA()`, `EVP_PKEY_get1_DSA()`, and
+   `EVP_PKEY_set1_DSA()` are also deprecated.
+   Applications should instead either read or write an
+   EVP_PKEY directly using the OSSL_DECODER and OSSL_ENCODER APIs,
+   or load an EVP_PKEY directly from DSA data using `EVP_PKEY_fromdata()`.
+
    *Paul Dale*
 
  * Reworked the treatment of EC EVP_PKEYs with the SM2 curve to
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index 338f308934..18f4c9955e 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -433,7 +433,10 @@ static int dh_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
 {
     switch (op) {
     case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
-        return ossl_dh_buf2key(EVP_PKEY_get0_DH(pkey), arg2, arg1);
+        /* We should only be here if we have a legacy key */
+        if (!ossl_assert(evp_pkey_is_legacy(pkey)))
+            return 0;
+        return ossl_dh_buf2key(evp_pkey_get0_DH_int(pkey), arg2, arg1);
     case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
         return ossl_dh_key2buf(EVP_PKEY_get0_DH(pkey), arg2, 0, 1);
     default:
diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
index 89241b97c1..694fcb3789 100644
--- a/crypto/ec/ec_ameth.c
+++ b/crypto/ec/ec_ameth.c
@@ -482,7 +482,10 @@ static int ec_pkey_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
         return 1;
 
     case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
-        return EC_KEY_oct2key(EVP_PKEY_get0_EC_KEY(pkey), arg2, arg1, NULL);
+        /* We should only be here if we have a legacy key */
+        if (!ossl_assert(evp_pkey_is_legacy(pkey)))
+            return 0;
+        return EC_KEY_oct2key(evp_pkey_get0_EC_KEY_int(pkey), arg2, arg1, NULL);
 
     case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
         return EC_KEY_key2buf(EVP_PKEY_get0_EC_KEY(pkey),
diff --git a/crypto/evp/ctrl_params_translate.c b/crypto/evp/ctrl_params_translate.c
index ae3340395d..966278171c 100644
--- a/crypto/evp/ctrl_params_translate.c
+++ b/crypto/evp/ctrl_params_translate.c
@@ -1481,7 +1481,7 @@ static int get_payload_group_name(enum state state,
 #ifndef OPENSSL_NO_DH
     case EVP_PKEY_DH:
         {
-            DH *dh = EVP_PKEY_get0_DH(pkey);
+            const DH *dh = EVP_PKEY_get0_DH(pkey);
             int uid = DH_get_nid(dh);
 
             if (uid != NID_undef) {
@@ -1531,7 +1531,7 @@ static int get_payload_private_key(enum state state,
 #ifndef OPENSSL_NO_DH
     case EVP_PKEY_DH:
         {
-            DH *dh = EVP_PKEY_get0_DH(pkey);
+            const DH *dh = EVP_PKEY_get0_DH(pkey);
 
             ctx->p2 = (BIGNUM *)DH_get0_priv_key(dh);
         }
@@ -1540,7 +1540,7 @@ static int get_payload_private_key(enum state state,
 #ifndef OPENSSL_NO_EC
     case EVP_PKEY_EC:
         {
-            EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+            const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
 
             ctx->p2 = (BIGNUM *)EC_KEY_get0_private_key(ec);
         }
@@ -1590,7 +1590,7 @@ static int get_payload_public_key(enum state state,
 #ifndef OPENSSL_NO_EC
     case EVP_PKEY_EC:
         if (ctx->params->data_type == OSSL_PARAM_OCTET_STRING) {
-            EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey);
+            const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey);
             BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eckey));
             const EC_GROUP *ecg = EC_KEY_get0_group(eckey);
             const EC_POINT *point = EC_KEY_get0_public_key(eckey);
diff --git a/crypto/evp/p_dec.c b/crypto/evp/p_dec.c
index 6ac344e394..2e90705656 100644
--- a/crypto/evp/p_dec.c
+++ b/crypto/evp/p_dec.c
@@ -16,6 +16,7 @@
 #include <openssl/evp.h>
 #include <openssl/objects.h>
 #include <openssl/x509.h>
+#include "crypto/evp.h"
 
 int EVP_PKEY_decrypt_old(unsigned char *key, const unsigned char *ek, int ekl,
                          EVP_PKEY *priv)
@@ -28,7 +29,7 @@ int EVP_PKEY_decrypt_old(unsigned char *key, const unsigned char *ek, int ekl,
     }
 
     ret =
-        RSA_private_decrypt(ekl, ek, key, EVP_PKEY_get0_RSA(priv),
+        RSA_private_decrypt(ekl, ek, key, evp_pkey_get0_RSA_int(priv),
                             RSA_PKCS1_PADDING);
  err:
     return ret;
diff --git a/crypto/evp/p_enc.c b/crypto/evp/p_enc.c
index bdc490d884..5881153dbb 100644
--- a/crypto/evp/p_enc.c
+++ b/crypto/evp/p_enc.c
@@ -16,6 +16,7 @@
 #include <openssl/evp.h>
 #include <openssl/objects.h>
 #include <openssl/x509.h>
+#include "crypto/evp.h"
 
 int EVP_PKEY_encrypt_old(unsigned char *ek, const unsigned char *key,
                          int key_len, EVP_PKEY *pubk)
@@ -26,8 +27,9 @@ int EVP_PKEY_encrypt_old(unsigned char *ek, const unsigned char *key,
         ERR_raise(ERR_LIB_EVP, EVP_R_PUBLIC_KEY_NOT_RSA);
         goto err;
     }
+
     ret =
-        RSA_public_encrypt(key_len, key, ek, EVP_PKEY_get0_RSA(pubk),
+        RSA_public_encrypt(key_len, key, ek, evp_pkey_get0_RSA_int(pubk),
                            RSA_PKCS1_PADDING);
  err:
     return ret;
diff --git a/crypto/evp/p_legacy.c b/crypto/evp/p_legacy.c
index 5d8468f949..af93288dcb 100644
--- a/crypto/evp/p_legacy.c
+++ b/crypto/evp/p_legacy.c
@@ -31,22 +31,23 @@ int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key)
     return ret;
 }
 
-RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey)
+RSA *evp_pkey_get0_RSA_int(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) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_AN_RSA_KEY);
         return NULL;
     }
-    return pkey->pkey.rsa;
+    return evp_pkey_get_legacy((EVP_PKEY *)pkey);
+}
+
+const RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey)
+{
+    return evp_pkey_get0_RSA_int(pkey);
 }
 
 RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey)
 {
-    RSA *ret = EVP_PKEY_get0_RSA(pkey);
+    RSA *ret = evp_pkey_get0_RSA_int(pkey);
 
     if (ret != NULL)
         RSA_up_ref(ret);
@@ -63,22 +64,23 @@ int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key)
     return ret;
 }
 
-EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey)
+EC_KEY *evp_pkey_get0_EC_KEY_int(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);
+        ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_EC_KEY);
         return NULL;
     }
-    return pkey->pkey.ec;
+    return evp_pkey_get_legacy((EVP_PKEY *)pkey);
+}
+
+const EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey)
+{
+    return evp_pkey_get0_EC_KEY_int(pkey);
 }
 
 EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey)
 {
-    EC_KEY *ret = EVP_PKEY_get0_EC_KEY(pkey);
+    EC_KEY *ret = evp_pkey_get0_EC_KEY_int(pkey);
 
     if (ret != NULL)
         EC_KEY_up_ref(ret);
diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c
index 63f3f4cbc7..21fbc2ea4c 100644
--- a/crypto/evp/p_lib.c
+++ b/crypto/evp/p_lib.c
@@ -13,6 +13,7 @@
  */
 #include "internal/deprecated.h"
 
+#include <assert.h>
 #include <stdio.h>
 #include "internal/cryptlib.h"
 #include "internal/refcount.h"
@@ -660,7 +661,7 @@ 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, NULL);
 }
 
-#ifndef OPENSSL_NO_DEPRECATED_3_0
+# ifndef OPENSSL_NO_DEPRECATED_3_0
 int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type)
 {
     if (!evp_pkey_is_legacy(pkey)) {
@@ -689,7 +690,7 @@ int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type)
     pkey->type = type;
     return 1;
 }
-#endif
+# endif
 
 # ifndef OPENSSL_NO_ENGINE
 int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *e)
@@ -715,18 +716,20 @@ ENGINE *EVP_PKEY_get0_engine(const EVP_PKEY *pkey)
     return pkey->engine;
 }
 # endif
+
+# ifndef OPENSSL_NO_DEPRECATED_3_0
 int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key)
 {
     int alias = type;
 
-#ifndef OPENSSL_NO_EC
+#  ifndef OPENSSL_NO_EC
     if ((key != NULL) && (EVP_PKEY_type(type) == EVP_PKEY_EC)) {
         const EC_GROUP *group = EC_KEY_get0_group(key);
 
         if (group != NULL && EC_GROUP_get_curve_name(group) == NID_sm2)
             alias = EVP_PKEY_SM2;
     }
-#endif
+#  endif
 
     if (pkey == NULL || !EVP_PKEY_set_type(pkey, type))
         return 0;
@@ -735,21 +738,19 @@ int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key)
     pkey->pkey.ptr = key;
     return (key != NULL);
 }
+# endif
 
-void *EVP_PKEY_get0(const EVP_PKEY *pkey)
+const void *EVP_PKEY_get0(const EVP_PKEY *pkey)
 {
     if (pkey == NULL)
         return NULL;
-    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
-        return NULL;
-    }
-    return pkey->pkey.ptr;
+
+    return evp_pkey_get_legacy((EVP_PKEY *)pkey);
 }
 
 const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len)
 {
-    ASN1_OCTET_STRING *os = NULL;
+    const ASN1_OCTET_STRING *os = NULL;
     if (pkey->type != EVP_PKEY_HMAC) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_AN_HMAC_KEY);
         return NULL;
@@ -762,7 +763,7 @@ const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len)
 # ifndef OPENSSL_NO_POLY1305
 const unsigned char *EVP_PKEY_get0_poly1305(const EVP_PKEY *pkey, size_t *len)
 {
-    ASN1_OCTET_STRING *os = NULL;
+    const ASN1_OCTET_STRING *os = NULL;
     if (pkey->type != EVP_PKEY_POLY1305) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_POLY1305_KEY);
         return NULL;
@@ -776,7 +777,7 @@ const unsigned char *EVP_PKEY_get0_poly1305(const EVP_PKEY *pkey, size_t *len)
 # ifndef OPENSSL_NO_SIPHASH
 const unsigned char *EVP_PKEY_get0_siphash(const EVP_PKEY *pkey, size_t *len)
 {
-    ASN1_OCTET_STRING *os = NULL;
+    const ASN1_OCTET_STRING *os = NULL;
 
     if (pkey->type != EVP_PKEY_SIPHASH) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_SIPHASH_KEY);
@@ -789,17 +790,18 @@ const unsigned char *EVP_PKEY_get0_siphash(const EVP_PKEY *pkey, size_t *len)
 # endif
 
 # ifndef OPENSSL_NO_DSA
-DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey)
+static DSA *evp_pkey_get0_DSA_int(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) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_DSA_KEY);
         return NULL;
     }
-    return pkey->pkey.dsa;
+    return evp_pkey_get_legacy((EVP_PKEY *)pkey);
+}
+
+const DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey)
+{
+    return evp_pkey_get0_DSA_int(pkey);
 }
 
 int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key)
@@ -811,7 +813,8 @@ int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key)
 }
 DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey)
 {
-    DSA *ret = EVP_PKEY_get0_DSA(pkey);
+    DSA *ret = evp_pkey_get0_DSA_int(pkey);
+
     if (ret != NULL)
         DSA_up_ref(ret);
     return ret;
@@ -821,22 +824,18 @@ DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey)
 
 #ifndef FIPS_MODULE
 # ifndef OPENSSL_NO_EC
-static ECX_KEY *evp_pkey_get0_ECX_KEY(const EVP_PKEY *pkey, int type)
+static const ECX_KEY *evp_pkey_get0_ECX_KEY(const EVP_PKEY *pkey, int type)
 {
-    if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
-        return NULL;
-    }
     if (EVP_PKEY_base_id(pkey) != type) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_ECX_KEY);
         return NULL;
     }
-    return pkey->pkey.ecx;
+    return evp_pkey_get_legacy((EVP_PKEY *)pkey);
 }
 
 static ECX_KEY *evp_pkey_get1_ECX_KEY(EVP_PKEY *pkey, int type)
 {
-    ECX_KEY *ret = evp_pkey_get0_ECX_KEY(pkey, type);
+    ECX_KEY *ret = (ECX_KEY *)evp_pkey_get0_ECX_KEY(pkey, type);
     if (ret != NULL)
         ossl_ecx_key_up_ref(ret);
     return ret;
@@ -866,22 +865,24 @@ int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key)
     return ret;
 }
 
-DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey)
+DH *evp_pkey_get0_DH_int(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) {
         ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_DH_KEY);
         return NULL;
     }
-    return pkey->pkey.dh;
+    return evp_pkey_get_legacy((EVP_PKEY *)pkey);
+}
+
+const DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey)
+{
+    return evp_pkey_get0_DH_int(pkey);
 }
 
 DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey)
 {
-    DH *ret = EVP_PKEY_get0_DH(pkey);
+    DH *ret = evp_pkey_get0_DH_int(pkey);
+
     if (ret != NULL)
         DH_up_ref(ret);
     return ret;
@@ -1310,36 +1311,6 @@ size_t EVP_PKEY_get1_encoded_public_key(EVP_PKEY *pkey, unsigned char **ppub)
 
 /*- All methods below can also be used in FIPS_MODULE */
 
-/*
- * This reset function must be used very carefully, as it literally throws
- * away everything in an EVP_PKEY without freeing them, and may cause leaks
- * of memory, what have you.
- * The only reason we have this is to have the same code for EVP_PKEY_new()
- * and evp_pkey_downgrade().
- */
-static int evp_pkey_reset_unlocked(EVP_PKEY *pk)
-{
-    if (pk == NULL)
-        return 0;
-
-    if (pk->lock != NULL) {
-      const size_t offset = (unsigned char *)&pk->lock - (unsigned char *)pk;
-
-      memset(pk, 0, offset);
-      memset((unsigned char *)pk + offset + sizeof(pk->lock),
-             0,
-             sizeof(*pk) - offset - sizeof(pk->lock));
-    }
-    /* EVP_PKEY_new uses zalloc so no need to call memset if pk->lock is NULL */
-
-    pk->type = EVP_PKEY_NONE;
-    pk->save_type = EVP_PKEY_NONE;
-    pk->references = 1;
-    pk->save_parameters = 1;
-
-    return 1;
-}
-
 EVP_PKEY *EVP_PKEY_new(void)
 {
     EVP_PKEY *ret = OPENSSL_zalloc(sizeof(*ret));
@@ -1349,8 +1320,10 @@ EVP_PKEY *EVP_PKEY_new(void)
         return NULL;
     }
 
-    if (!evp_pkey_reset_unlocked(ret))
-        goto err;
+    ret->type = EVP_PKEY_NONE;
+    ret->save_type = EVP_PKEY_NONE;
+    ret->references = 1;
+    ret->save_parameters = 1;
 
     ret->lock = CRYPTO_THREAD_lock_new();
     if (ret->lock == NULL) {
@@ -1559,12 +1532,32 @@ int EVP_PKEY_up_ref(EVP_PKEY *pkey)
 #ifndef FIPS_MODULE
 void evp_pkey_free_legacy(EVP_PKEY *x)
 {
-    if (x->ameth != NULL) {
-        if (x->ameth->pkey_free != NULL)
-            x->ameth->pkey_free(x);
+    const EVP_PKEY_ASN1_METHOD *ameth = x->ameth;
+    ENGINE *tmpe = NULL;
+
+    if (ameth == NULL && x->legacy_cache_pkey.ptr != NULL)
+        ameth = EVP_PKEY_asn1_find(&tmpe, x->type);
+
+    if (ameth != NULL) {
+        if (x->legacy_cache_pkey.ptr != NULL) {
+            /*
+             * We should never have both a legacy origin key, and a key in the
+             * legacy cache.
+             */
+            assert(x->pkey.ptr == NULL);
+            /*
+             * For the purposes of freeing we make the legacy cache look like
+             * a legacy origin key.
+             */
+            x->pkey = x->legacy_cache_pkey;
+            x->legacy_cache_pkey.ptr = NULL;
+        }
+        if (ameth->pkey_free != NULL)
+            ameth->pkey_free(x);
         x->pkey.ptr = NULL;
     }
 # ifndef OPENSSL_NO_ENGINE
+    ENGINE_finish(tmpe);
     ENGINE_finish(x->engine);
     x->engine = NULL;
     ENGINE_finish(x->pmeth_engine);
@@ -1824,10 +1817,15 @@ int evp_pkey_copy_downgraded(EVP_PKEY **dest, const EVP_PKEY *src)
             keytype = OBJ_nid2sn(type);
 
         /* Make sure we have a clean slate to copy into */
-        if (*dest == NULL)
+        if (*dest == NULL) {
             *dest = EVP_PKEY_new();
-        else
+            if (*dest == NULL) {
+                ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+        } else {
             evp_pkey_free_it(*dest);
+        }
 
         if (EVP_PKEY_set_type(*dest, type)) {
             /* If the key is typed but empty, we're done */
@@ -1872,78 +1870,57 @@ int evp_pkey_copy_downgraded(EVP_PKEY **dest, const EVP_PKEY *src)
     return 0;
 }
 
-int evp_pkey_downgrade(EVP_PKEY *pk)
+void *evp_pkey_get_legacy(EVP_PKEY *pk)
 {
-    EVP_PKEY tmp_copy;              /* Stack allocated! */
-    int rv = 0;
+    EVP_PKEY *tmp_copy = NULL;
+    void *ret = NULL;
 
     if (!ossl_assert(pk != NULL))
-        return 0;
+        return NULL;
 
     /*
-     * Throughout this whole function, we must ensure that we lock / unlock
-     * the exact same lock.  Note that we do pass it around a bit.
+     * If this isn't an assigned provider side key, we just use any existing
+     * origin legacy key.
      */
-    if (!CRYPTO_THREAD_write_lock(pk->lock))
-        return 0;
+    if (!evp_pkey_is_assigned(pk))
+        return NULL;
+    if (!evp_pkey_is_provided(pk))
+        return pk->pkey.ptr;
 
-    /* If this isn't an assigned provider side key, we're done */
-    if (!evp_pkey_is_assigned(pk) || !evp_pkey_is_provided(pk)) {
-        rv = 1;
-        goto end;
-    }
+    if (!CRYPTO_THREAD_read_lock(pk->lock))
+        return NULL;
 
-    /*
-     * To be able to downgrade, we steal the contents of |pk|, then reset
-     * it, and finally try to make it a downgraded copy.  If any of that
-     * fails, we restore the copied contents into |pk|.
-     */
-    tmp_copy = *pk;              /* |tmp_copy| now owns THE lock */
+    ret = pk->legacy_cache_pkey.ptr;
 
-    if (evp_pkey_reset_unlocked(pk)
-        && evp_pkey_copy_downgraded(&pk, &tmp_copy)) {
+    if (!CRYPTO_THREAD_unlock(pk->lock))
+        return NULL;
 
-        /* Restore the common attributes, then empty |tmp_copy| */
-        pk->references = tmp_copy.references;
-        pk->attributes = tmp_copy.attributes;
-        pk->save_parameters = tmp_copy.save_parameters;
-        pk->ex_data = tmp_copy.ex_data;
+    if (ret != NULL)
+        return ret;
 
-        /* Ensure that stuff we've copied won't be freed */
-        tmp_copy.lock = NULL;
-        tmp_copy.attributes = NULL;
-        memset(&tmp_copy.ex_data, 0, sizeof(tmp_copy.ex_data));
+    if (!evp_pkey_copy_downgraded(&tmp_copy, pk))
+        return NULL;
 
-        /*
-         * Save the provider side data in the operation cache, so they'll
-         * find it again.  |pk| is new, so it's safe to assume slot zero
-         * is free.
-         * Note that evp_keymgmt_util_cache_keydata() increments keymgmt's
-         * reference count, so we need to decrement it, or there will be a
-         * leak.
-         */
-        evp_keymgmt_util_cache_keydata(pk, tmp_copy.keymgmt,
-                                       tmp_copy.keydata);
-        EVP_KEYMGMT_free(tmp_copy.keymgmt);
+    if (!CRYPTO_THREAD_write_lock(pk->lock))
+        goto err;
 
-        /*
-         * Clear keymgmt and keydata from |tmp_copy|, or they'll get
-         * inadvertently freed.
-         */
-        tmp_copy.keymgmt = NULL;
-        tmp_copy.keydata = NULL;
+    /* Check again in case some other thread has updated it in the meantime */
+    ret = pk->legacy_cache_pkey.ptr;
+    if (ret == NULL) {
+        /* Steal the legacy key reference from the temporary copy */
+        ret = pk->legacy_cache_pkey.ptr = tmp_copy->pkey.ptr;
+        tmp_copy->pkey.ptr = NULL;
+    }
 
-        evp_pkey_free_it(&tmp_copy);
-        rv = 1;
-    } else {
-        /* Restore the original key */
-        *pk = tmp_copy;
+    if (!CRYPTO_THREAD_unlock(pk->lock)) {
+        ret = NULL;
+        goto err;
     }
 
- end:
-    if (!CRYPTO_THREAD_unlock(pk->lock))
-        return 0;
-    return rv;
+ err:
+    EVP_PKEY_free(tmp_copy);
+
+    return ret;
 }
 #endif  /* FIPS_MODULE */
 
@@ -2201,7 +2178,7 @@ int EVP_PKEY_get_ec_point_conv_form(const EVP_PKEY *pkey)
             || pkey->keydata == NULL) {
 #ifndef OPENSSL_NO_EC
         /* Might work through the legacy route */
-        EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+        const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
 
         if (ec == NULL)
             return 0;
@@ -2241,7 +2218,7 @@ int EVP_PKEY_get_field_type(const EVP_PKEY *pkey)
             || pkey->keydata == NULL) {
 #ifndef OPENSSL_NO_EC
         /* Might work through the legacy route */
-        EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+        const EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
         const EC_GROUP *grp;
 
         if (ec == NULL)
diff --git a/crypto/evp/pmeth_gn.c b/crypto/evp/pmeth_gn.c
index 1e4078cfa7..1953e0f958 100644
--- a/crypto/evp/pmeth_gn.c
+++ b/crypto/evp/pmeth_gn.c
@@ -197,7 +197,7 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
 #endif
 
     /*
-     * Because we still have legacy keys, and evp_pkey_downgrade()
+     * Because we still have legacy keys
      * TODO remove this #legacy internal keys are gone
      */
     (*ppkey)->type = ctx->legacy_keytype;
@@ -208,8 +208,17 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
 #ifdef FIPS_MODULE
     goto not_supported;
 #else
-    if (ctx->pkey && !evp_pkey_downgrade(ctx->pkey))
+    /*
+     * If we get here then we're using legacy paramgen/keygen. In that case
+     * the pkey in ctx (if there is one) had better not be provided (because the
+     * legacy methods may not know how to handle it). However we can only get
+     * here if ctx->op.keymgmt.genctx == NULL, but that should never be the case
+     * if ctx->pkey is provided because we don't allow this when we initialise
+     * the ctx.
+     */
+    if (ctx->pkey != NULL && !ossl_assert(!evp_pkey_is_provided(ctx->pkey)))
         goto not_accessible;
+
     switch (ctx->operation) {
     case EVP_PKEY_OP_PARAMGEN:
         ret = ctx->pmeth->paramgen(ctx, *ppkey);
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index b08d0d2e3c..96d103544d 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -266,8 +266,7 @@ static EVP_PKEY_CTX *int_ctx_new(OSSL_LIB_CTX *libctx,
         /*
          * Chase down the legacy NID, as that might be needed for diverse
          * purposes, such as ensure that EVP_PKEY_type() can return sensible
-         * values, or that there's a better chance to "downgrade" a key when
-         * needed.  We go through all keymgmt names, because the keytype
+         * values. We go through all keymgmt names, because the keytype
          * that's passed to this function doesn't necessarily translate
          * directly.
          * TODO: Remove this when #legacy keys are gone.
diff --git a/crypto/pem/pvkfmt.c b/crypto/pem/pvkfmt.c
index de673be005..8006c64b3a 100644
--- a/crypto/pem/pvkfmt.c
+++ b/crypto/pem/pvkfmt.c
@@ -450,12 +450,12 @@ static void write_lebn(unsigned char **out, const BIGNUM *bn, int len)
     *out += len;
 }
 
-static int check_bitlen_rsa(RSA *rsa, int ispub, unsigned int *magic);
-static void write_rsa(unsigned char **out, RSA *rsa, int ispub);
+static int check_bitlen_rsa(const RSA *rsa, int ispub, unsigned int *magic);
+static void write_rsa(unsigned char **out, const RSA *rsa, int ispub);
 
 #ifndef OPENSSL_NO_DSA
-static int check_bitlen_dsa(DSA *dsa, int ispub, unsigned int *magic);
-static void write_dsa(unsigned char **out, DSA *dsa, int ispub);
+static int check_bitlen_dsa(const DSA *dsa, int ispub, unsigned int *magic);
+static void write_dsa(unsigned char **out, const DSA *dsa, int ispub);
 #endif
 
 static int do_i2b(unsigned char **out, const EVP_PKEY *pk, int ispub)
@@ -542,7 +542,7 @@ static int do_i2b_bio(BIO *out, const EVP_PKEY *pk, int ispub)
     return -1;
 }
 
-static int check_bitlen_rsa(RSA *rsa, int ispub, unsigned int *pmagic)
+static int check_bitlen_rsa(const RSA *rsa, int ispub, unsigned int *pmagic)
 {
     int nbyte, hnbyte, bitlen;
     const BIGNUM *e;
@@ -582,7 +582,7 @@ static int check_bitlen_rsa(RSA *rsa, int ispub, unsigned int *pmagic)
     return 0;
 }
 
-static void write_rsa(unsigned char **out, RSA *rsa, int ispub)
+static void write_rsa(unsigned char **out, const RSA *rsa, int ispub)
 {
     int nbyte, hnbyte;
     const BIGNUM *n, *d, *e, *p, *q, *iqmp, *dmp1, *dmq1;
@@ -605,7 +605,7 @@ static void write_rsa(unsigned char **out, RSA *rsa, int ispub)
 }
 
 #ifndef OPENSSL_NO_DSA
-static int check_bitlen_dsa(DSA *dsa, int ispub, unsigned int *pmagic)
+static int check_bitlen_dsa(const DSA *dsa, int ispub, unsigned int *pmagic)
 {
     int bitlen;
     const BIGNUM *p = NULL, *q = NULL, *g = NULL;
@@ -633,7 +633,7 @@ static int check_bitlen_dsa(DSA *dsa, int ispub, unsigned int *pmagic)
     return 0;
 }
 
-static void write_dsa(unsigned char **out, DSA *dsa, int ispub)
+static void write_dsa(unsigned char **out, const DSA *dsa, int ispub)
 {
     int nbyte;
     const BIGNUM *p = NULL, *q = NULL, *g = NULL;
diff --git a/doc/internal/man3/evp_pkey_export_to_provider.pod b/doc/internal/man3/evp_pkey_export_to_provider.pod
index 6cea8a9aab..833ff44d53 100644
--- a/doc/internal/man3/evp_pkey_export_to_provider.pod
+++ b/doc/internal/man3/evp_pkey_export_to_provider.pod
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-evp_pkey_export_to_provider, evp_pkey_copy_downgraded, evp_pkey_downgrade
+evp_pkey_export_to_provider, evp_pkey_copy_downgraded, evp_pkey_get_legacy
 - internal EVP_PKEY support functions for providers
 
 =head1 SYNOPSIS
@@ -14,7 +14,7 @@ evp_pkey_export_to_provider, evp_pkey_copy_downgraded, evp_pkey_downgrade
                                    EVP_KEYMGMT **keymgmt,
                                    const char *propquery);
  int evp_pkey_copy_downgraded(EVP_PKEY **dest, const EVP_PKEY *src);
- int evp_pkey_downgrade(EVP_PKEY *pk);
+ void *evp_pkey_get_legacy(EVP_PKEY *pk);
 
 =head1 DESCRIPTION
 
@@ -37,11 +37,14 @@ For example, L<PEM_write_bio_PrivateKey_traditional(3)> uses this to try its
 best to get "traditional" PEM output even if the input B<EVP_PKEY> has a
 provider-native internal key.
 
-evp_pkey_downgrade() converts an B<EVP_PKEY> with a provider side "origin" key
-to one with a legacy "origin", if there's a corresponding legacy implementation.
-This clears the operation cache, except for the provider side "origin" key.
-This function is used in spots where provider side keys aren't yet supported,
-in an attempt to keep operating with available implementations.
+evp_pkey_get_legacy() obtains and returns a legacy key structure. If the
+EVP_PKEY already contains a legacy key then it is simply returned. If it is a
+provider based key, then a new legacy key is constructed based on the provider
+key. The legacy key is cached inside the EVP_PKEY and its value returned from
+this function. Subsequent calls to evp_pkey_get_legacy() will return the cached
+key. Subsequent changes to the provider key are not reflected back in the
+legacy key. Similarly changes to the legacy key are not reflected back in the
+provider key.
 
 =head1 RETURN VALUES
 
@@ -49,14 +52,13 @@ evp_pkey_export_to_provider() returns the provider key data if there was any
 allocated.  It also either sets I<*keymgmt> to the B<EVP_KEYMGMT> associated
 with the returned key data, or NULL on error.
 
-evp_pkey_downgrade() returns 1 on success or 0 on error.
+evp_pkey_get_legacy() returns the legacy key or NULL on error.
 
 =head1 NOTES
 
-Some functions calling evp_pkey_export_to_provider() or evp_pkey_downgrade()
-may have received a const key, and may therefore have to cast the key to
-non-const form to call this function.  Since B<EVP_PKEY> is always dynamically
-allocated, this is OK.
+Some functions calling evp_pkey_export_to_provider() may have received a const
+key, and may therefore have to cast the key to non-const form to call this
+function.  Since B<EVP_PKEY> is always dynamically allocated, this is OK.
 
 =head1 SEE ALSO
 
diff --git a/doc/internal/man7/EVP_PKEY.pod b/doc/internal/man7/EVP_PKEY.pod
index 022f3f0e4e..cc738b9c28 100644
--- a/doc/internal/man7/EVP_PKEY.pod
+++ b/doc/internal/man7/EVP_PKEY.pod
@@ -65,7 +65,10 @@ The B<EVP_PKEY> internal keys are mutable.
 
 This is especially visible with internal legacy keys, since they can
 be extracted with functions like L<EVP_PKEY_get0_RSA(3)> and then
-modified at will with functions like L<RSA_set0_key(3)>.
+modified at will with functions like L<RSA_set0_key(3)>. Note that if the
+internal key is a provider key then the return value from functions such as
+L<EVP_PKEY_get0_RSA(3)> is a cached copy of the key. Changes to the cached
+copy are not reflected back in the provider key.
 
 Internal provider native keys are also possible to be modified, if the
 associated L<EVP_KEYMGMT(3)> implementation allows it.  This is done
@@ -178,27 +181,20 @@ OSSL_FUNC_keymgmt_import() function.
 
 =back
 
-=head2 Upgrading and downgrading a key
-
-An B<EVP_PKEY> with a legacy origin will I<never> be upgraded to
-become an B<EVP_PKEY> with a provider native origin.  Instead, we have
-the operation cache as described above, that takes care of the needs
-of the diverse operation the application may want to perform.
-
-An B<EVP_PKEY> with a provider native origin, I<may> be downgraded to
-be I<transformed> into an B<EVP_PKEY> with a legacy origin.  Because
-an B<EVP_PKEY> can't have two origins, it means that it stops having a
-provider native origin.  The previous provider native key data is
-moved to the operation cache.  Downgrading is performed with the
-internal function L<evp_pkey_downgrade(3)>.
-
-I<Downgrading a key is understandably fragile>, and possibly surprising,
-and should therefore be done I<as little as possible>, but is needed
-to be able to support functions like L<EVP_PKEY_get0_RSA(3)>.
-The general recommendation is to use L<evp_pkey_copy_downgraded(3)>
-whenever possible, which it should be if the need for a legacy origin
-is only internal, or better yet, to remove the need for downgrade at
-all.
+=head2 Changing a key origin
+
+It is never possible to change the origin of a key. An B<EVP_PKEY> with a legacy
+origin will I<never> be upgraded to become an B<EVP_PKEY> with a provider
+native origin. Instead, we have the operation cache as described above, that
+takes care of the needs of the diverse operation the application may want to
+perform.
+
+Similarly an B<EVP_PKEY> with a provider native origin, will I<never> be
+I<transformed> into an B<EVP_PKEY> with a legacy origin. Instead we may have a
+cached copy of the provider key in legacy form. Once the cached copy is created
+it is never updated. Changes made to the provider key are not reflected back in
+the cached legacy copy. Similarly changes made to the cached legacy copy are not
+reflected back in the provider key.
 
 =head1 SEE ALSO
 
diff --git a/doc/man3/EVP_PKEY_set1_RSA.pod b/doc/man3/EVP_PKEY_set1_RSA.pod
index d4ab126e0a..64760b2923 100644
--- a/doc/man3/EVP_PKEY_set1_RSA.pod
+++ b/doc/man3/EVP_PKEY_set1_RSA.pod
@@ -15,6 +15,16 @@ EVP_PKEY_set1_engine, EVP_PKEY_get0_engine - EVP_PKEY assignment functions
 
  #include <openssl/evp.h>
 
+ int EVP_PKEY_id(const EVP_PKEY *pkey);
+ int EVP_PKEY_base_id(const EVP_PKEY *pkey);
+ int EVP_PKEY_type(int type);
+
+Deprecated since OpenSSL 3.0, can be hidden entirely by defining
+B<OPENSSL_API_COMPAT> with a suitable version value, see
+L<openssl_user_macros(7)>:
+
+ int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type);
+
  int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key);
  int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key);
  int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key);
@@ -28,10 +38,10 @@ EVP_PKEY_set1_engine, EVP_PKEY_get0_engine - EVP_PKEY assignment functions
  const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len);
  const unsigned char *EVP_PKEY_get0_poly1305(const EVP_PKEY *pkey, size_t *len);
  const unsigned char *EVP_PKEY_get0_siphash(const EVP_PKEY *pkey, size_t *len);
- RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey);
- DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey);
- DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey);
- EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
+ const RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey);
+ const DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey);
+ const DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey);
+ const EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
 
  int EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *key);
  int EVP_PKEY_assign_DSA(EVP_PKEY *pkey, DSA *key);
@@ -40,40 +50,11 @@ EVP_PKEY_set1_engine, EVP_PKEY_get0_engine - EVP_PKEY assignment functions
  int EVP_PKEY_assign_POLY1305(EVP_PKEY *pkey, ASN1_OCTET_STRING *key);
  int EVP_PKEY_assign_SIPHASH(EVP_PKEY *pkey, ASN1_OCTET_STRING *key);
 
- int EVP_PKEY_id(const EVP_PKEY *pkey);
- int EVP_PKEY_base_id(const EVP_PKEY *pkey);
- int EVP_PKEY_type(int type);
-
  ENGINE *EVP_PKEY_get0_engine(const EVP_PKEY *pkey);
  int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *engine);
 
-Deprecated since OpenSSL 3.0, can be hidden entirely by defining
-B<OPENSSL_API_COMPAT> with a suitable version value, see
-L<openssl_user_macros(7)>:
-
- int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type);
-
 =head1 DESCRIPTION
 
-EVP_PKEY_set1_RSA(), EVP_PKEY_set1_DSA(), EVP_PKEY_set1_DH() and
-EVP_PKEY_set1_EC_KEY() set the key referenced by I<pkey> to I<key>.
-
-EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and
-EVP_PKEY_get1_EC_KEY() return the referenced key in I<pkey> or NULL if the
-key is not of the correct type.  The returned key must be freed after use.
-
-EVP_PKEY_get0_hmac(), EVP_PKEY_get0_poly1305(), EVP_PKEY_get0_siphash(),
-EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(), EVP_PKEY_get0_DH() and
-EVP_PKEY_get0_EC_KEY() return the referenced key in I<pkey> or NULL if the
-key is not of the correct type but the reference count of the returned key
-is B<not> incremented and so must not be freed after use.
-
-EVP_PKEY_assign_RSA(), EVP_PKEY_assign_DSA(), EVP_PKEY_assign_DH(),
-EVP_PKEY_assign_EC_KEY(), EVP_PKEY_assign_POLY1305() and
-EVP_PKEY_assign_SIPHASH() set the referenced key to I<key> however these use
-the supplied I<key> internally and so I<key> will be freed when the parent
-I<pkey> is freed.
-
 EVP_PKEY_base_id() returns the type of I<pkey>. For example
 an RSA key will return B<EVP_PKEY_RSA>.
 
@@ -87,15 +68,71 @@ often seen in practice.
 EVP_PKEY_type() returns the underlying type of the NID I<type>. For example
 EVP_PKEY_type(EVP_PKEY_RSA2) will return B<EVP_PKEY_RSA>.
 
-EVP_PKEY_get0_engine() returns a reference to the ENGINE handling I<pkey>.
+EVP_PKEY_set1_RSA(), EVP_PKEY_set1_DSA(), EVP_PKEY_set1_DH() and
+EVP_PKEY_set1_EC_KEY() set the key referenced by I<pkey> to I<key>. These
+functions are deprecated. Applications should instead use
+L<EVP_PKEY_fromdata(3)>.
+
+EVP_PKEY_assign_RSA(), EVP_PKEY_assign_DSA(), EVP_PKEY_assign_DH(),
+EVP_PKEY_assign_EC_KEY(), EVP_PKEY_assign_POLY1305() and
+EVP_PKEY_assign_SIPHASH() set the referenced key to I<key> however these use
+the supplied I<key> internally and so I<key> will be freed when the parent
+I<pkey> is freed. These macros are deprecated. Applications should instead read
+an EVP_PKEY directly using the OSSL_DECODER APIs (see
+L<OSSL_DECODER_CTX_new_for_pkey(3)>), or construct an EVP_PKEY from data using
+L<EVP_PKEY_fromdata(3)>.
+
+EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and
+EVP_PKEY_get1_EC_KEY() return the referenced key in I<pkey> or NULL if the
+key is not of the correct type. The returned key must be freed after use.
+These functions are deprecated. Applications should instead use the EVP_PKEY
+directly where possible. If access to the low level key parameters is required
+then applications should use L<EVP_PKEY_get_params(3)> and other similar
+functions. To write an EVP_PKEY out use the OSSL_ENCODER APIs (see
+L<OSSL_ENCODER_CTX_new_for_pkey(3)>).
+
+EVP_PKEY_get0_hmac(), EVP_PKEY_get0_poly1305(), EVP_PKEY_get0_siphash(),
+EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(), EVP_PKEY_get0_DH() and
+EVP_PKEY_get0_EC_KEY() return the referenced key in I<pkey> or NULL if the
+key is not of the correct type. The reference count of the returned key is
+B<not> incremented and so the key must not be freed after use. These functions
+are deprecated. Applications should instead use the EVP_PKEY directly where
+possible. If access to the low level key parameters is required then
+applications should use L<EVP_PKEY_get_params(3)> and other similar functions.
+To write an EVP_PKEY out use the OSSL_ENCODER APIs (see
+L<OSSL_ENCODER_CTX_new_for_pkey(3)>).
+
+Note that if an EVP_PKEY was not constructed using one of the deprecated
+functions such as EVP_PKEY_set1_RSA(), EVP_PKEY_set1_DSA(), EVP_PKEY_set1_DH()
+or EVP_PKEY_set1_EC_KEY(), or via the similarly named B<EVP_PKEY_assign> macros
+described above then the internal key will be managed by a provider (see
+L<provider(7)>). In that case the key returned by EVP_PKEY_get1_RSA(),
+EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH(), EVP_PKEY_get1_EC_KEY(),
+EVP_PKEY_get0_hmac(), EVP_PKEY_get0_poly1305(), EVP_PKEY_get0_siphash(),
+EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(), EVP_PKEY_get0_DH() or
+EVP_PKEY_get0_EC_KEY() will be a cached copy of the provider's key. Subsequent
+updates to the provider's key will not be reflected back in the cached copy, and
+updates made by an application to the returned key will not be reflected back in
+the provider's key. Subsequent calls to EVP_PKEY_get1_RSA(),
+EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and EVP_PKEY_get1_EC_KEY() will always
+return the cached copy returned by the first call.
+
+EVP_PKEY_get0_engine() returns a reference to the ENGINE handling I<pkey>. This
+function is deprecated. Applications should use providers instead of engines
+(see L<provider(7)> for details).
 
 EVP_PKEY_set1_engine() sets the ENGINE handling I<pkey> to I<engine>. It
 must be called after the key algorithm and components are set up.
 If I<engine> does not include an B<EVP_PKEY_METHOD> for I<pkey> an
-error occurs.
+error occurs. This function is deprecated. Applications should use providers
+instead of engines (see L<provider(7)> for details).
 
-EVP_PKEY_set_alias_type() allows modifying a EVP_PKEY to use a
-different set of algorithms than the default.
+EVP_PKEY_set_alias_type() allows modifying an EVP_PKEY to use a
+different set of algorithms than the default. This function is deprecated and
+was previously needed as a workaround to recognise SM2 keys. From OpenSSL 3.0,
+this key type is internally recognised so the workaround is no longer needed.
+Functionality is still retained as it is, but will only work with EVP_PKEYs
+with a legacy internal key.
 
 =head1 WARNINGS
 
@@ -106,6 +143,17 @@ EVP_PKEY_id(), EVP_PKEY_base_id(), EVP_PKEY_type(), EVP_PKEY_set_alias_type()
 
 For EVP_PKEY key type checking purposes, L<EVP_PKEY_is_a(3)> is more generic.
 
+The keys returned from the functions EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(),
+EVP_PKEY_get0_DH() and EVP_PKEY_get0_EC_KEY() were changed to have a "const"
+return type in OpenSSL 3.0. As described above the keys returned may be cached
+copies of the key held in a provider. Due to this, and unlike in earlier
+versions of OpenSSL, they should be considered read-only copies of the key.
+Updates to these keys will not be reflected back in the provider side key. The
+EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and
+EVP_PKEY_get1_EC_KEY() functions were not changed to have a "const" return type
+in order that applications can "free" the return value. However applications
+should still consider them as read-only copies.
+
 =head1 NOTES
 
 In accordance with the OpenSSL naming convention the key obtained
@@ -170,7 +218,17 @@ L<EVP_PKEY_new(3)>, L<SM2(7)>
 
 =head1 HISTORY
 
-EVP_PKEY_set_alias_type() was deprecated in OpenSSL 3.0.
+EVP_PKEY_set1_RSA, EVP_PKEY_set1_DSA, EVP_PKEY_set1_DH, EVP_PKEY_set1_EC_KEY,
+EVP_PKEY_get1_RSA, EVP_PKEY_get1_DSA, EVP_PKEY_get1_DH, EVP_PKEY_get1_EC_KEY,
+EVP_PKEY_get0_RSA, EVP_PKEY_get0_DSA, EVP_PKEY_get0_DH, EVP_PKEY_get0_EC_KEY,
+EVP_PKEY_assign_RSA, EVP_PKEY_assign_DSA, EVP_PKEY_assign_DH,
+EVP_PKEY_assign_EC_KEY, EVP_PKEY_assign_POLY1305, EVP_PKEY_assign_SIPHASH,
+EVP_PKEY_get0_hmac, EVP_PKEY_get0_poly1305, EVP_PKEY_get0_siphash,
+EVP_PKEY_set_alias_type, EVP_PKEY_set1_engine and EVP_PKEY_get0_engine were
+deprecated in OpenSSL 3.0.
+
+The return value from EVP_PKEY_get0_RSA, EVP_PKEY_get0_DSA, EVP_PKEY_get0_DH,
+EVP_PKEY_get0_EC_KEY were made const in OpenSSL 3.0.
 
 =head1 COPYRIGHT
 
diff --git a/doc/man7/evp.pod b/doc/man7/evp.pod
index d8f5a2c1d3..c97abba3dd 100644
--- a/doc/man7/evp.pod
+++ b/doc/man7/evp.pod
@@ -29,7 +29,7 @@ The B<EVP_PKEY>I<XXX> functions provide a high-level interface to
 asymmetric algorithms. To create a new EVP_PKEY see
 L<EVP_PKEY_new(3)>. EVP_PKEYs can be associated
 with a private key of a particular algorithm by using the functions
-described on the L<EVP_PKEY_set1_RSA(3)> page, or
+described on the L<EVP_PKEY_fromdata(3)> page, or
 new keys can be generated using L<EVP_PKEY_keygen(3)>.
 EVP_PKEYs can be compared using L<EVP_PKEY_cmp(3)>, or printed using
 L<EVP_PKEY_print_private(3)>.
@@ -90,7 +90,7 @@ L<EVP_SignInit(3)>,
 L<EVP_VerifyInit(3)>,
 L<EVP_EncodeInit(3)>,
 L<EVP_PKEY_new(3)>,
-L<EVP_PKEY_set1_RSA(3)>,
+L<EVP_PKEY_fromdata(3)>,
 L<EVP_PKEY_keygen(3)>,
 L<EVP_PKEY_print_private(3)>,
 L<EVP_PKEY_decrypt(3)>,
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 41487d2af2..70cc6fff1c 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -608,6 +608,21 @@ DEFINE_STACK_OF(OP_CACHE_ELEM)
 #define evp_pkey_is_provided(pk)                                \
     ((pk)->keymgmt != NULL)
 
+union legacy_pkey_st {
+    void *ptr;
+    struct rsa_st *rsa;     /* RSA */
+#  ifndef OPENSSL_NO_DSA
+    struct dsa_st *dsa;     /* DSA */
+#  endif
+#  ifndef OPENSSL_NO_DH
+    struct dh_st *dh;       /* DH */
+#  endif
+#  ifndef OPENSSL_NO_EC
+    struct ec_key_st *ec;   /* ECC */
+    ECX_KEY *ecx;           /* X25519, X448, Ed25519, Ed448 */
+#  endif
+};
+
 struct evp_pkey_st {
     /* == Legacy attributes == */
     int type;
@@ -621,24 +636,15 @@ struct evp_pkey_st {
     const EVP_PKEY_ASN1_METHOD *ameth;
     ENGINE *engine;
     ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */
-    union {
-        void *ptr;
-        struct rsa_st *rsa;     /* RSA */
-#  ifndef OPENSSL_NO_DSA
-        struct dsa_st *dsa;     /* DSA */
-#  endif
-#  ifndef OPENSSL_NO_DH
-        struct dh_st *dh;       /* DH */
-#  endif
-#  ifndef OPENSSL_NO_EC
-        struct ec_key_st *ec;   /* ECC */
-        ECX_KEY *ecx;           /* X25519, X448, Ed25519, Ed448 */
-#  endif
-    } pkey;
+
+    /* Union to store the reference to an origin legacy key */
+    union legacy_pkey_st pkey;
+
+    /* Union to store the reference to a non-origin legacy key */
+    union legacy_pkey_st legacy_cache_pkey;
 # endif
 
     /* == Common attributes == */
-    /* If these are modified, so must evp_pkey_downgrade() */
     CRYPTO_REF_COUNT references;
     CRYPTO_RWLOCK *lock;
     STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */
@@ -719,7 +725,7 @@ void *evp_pkey_export_to_provider(EVP_PKEY *pk, OSSL_LIB_CTX *libctx,
                                   const char *propquery);
 #ifndef FIPS_MODULE
 int evp_pkey_copy_downgraded(EVP_PKEY **dest, const EVP_PKEY *src);
-int evp_pkey_downgrade(EVP_PKEY *pk);
+void *evp_pkey_get_legacy(EVP_PKEY *pk);
 void evp_pkey_free_legacy(EVP_PKEY *x);
 #endif
 
@@ -884,4 +890,11 @@ int evp_pkey_ctx_get_params_to_ctrl(EVP_PKEY_CTX *ctx, OSSL_PARAM *params);
 /* This must ONLY be called for legacy EVP_PKEYs */
 int evp_pkey_get_params_to_ctrl(const EVP_PKEY *pkey, OSSL_PARAM *params);
 
+/* Same as the public get0 functions but are not const */
+# ifndef OPENSSL_NO_DEPRECATED_3_0
+DH *evp_pkey_get0_DH_int(const EVP_PKEY *pkey);
+EC_KEY *evp_pkey_get0_EC_KEY_int(const EVP_PKEY *pkey);
+RSA *evp_pkey_get0_RSA_int(const EVP_PKEY *pkey);
+# endif
+
 #endif /* OSSL_CRYPTO_EVP_H */
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 96a82827fc..ec8503e7d8 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1240,55 +1240,62 @@ 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);
 # ifndef OPENSSL_NO_DEPRECATED_3_0
 OSSL_DEPRECATEDIN_3_0 int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type);
-# endif
-# ifndef OPENSSL_NO_ENGINE
+#  ifndef OPENSSL_NO_ENGINE
+OSSL_DEPRECATEDIN_3_0
 int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *e);
+OSSL_DEPRECATEDIN_3_0
 ENGINE *EVP_PKEY_get0_engine(const EVP_PKEY *pkey);
-# endif
+#  endif
+OSSL_DEPRECATEDIN_3_0
 int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
-void *EVP_PKEY_get0(const EVP_PKEY *pkey);
+OSSL_DEPRECATEDIN_3_0
+const void *EVP_PKEY_get0(const EVP_PKEY *pkey);
+OSSL_DEPRECATEDIN_3_0
 const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len);
-# ifndef OPENSSL_NO_POLY1305
+#  ifndef OPENSSL_NO_POLY1305
+OSSL_DEPRECATEDIN_3_0
 const unsigned char *EVP_PKEY_get0_poly1305(const EVP_PKEY *pkey, size_t *len);
-# endif
-# ifndef OPENSSL_NO_SIPHASH
+#  endif
+#  ifndef OPENSSL_NO_SIPHASH
+OSSL_DEPRECATEDIN_3_0
 const unsigned char *EVP_PKEY_get0_siphash(const EVP_PKEY *pkey, size_t *len);
-# endif
+#  endif
 
-# ifndef OPENSSL_NO_DEPRECATED_3_0
 struct rsa_st;
 OSSL_DEPRECATEDIN_3_0
 int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, struct rsa_st *key);
 OSSL_DEPRECATEDIN_3_0
-struct rsa_st *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey);
+const struct rsa_st *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey);
 OSSL_DEPRECATEDIN_3_0
 struct rsa_st *EVP_PKEY_get1_RSA(EVP_PKEY *pkey);
-# endif
-# ifndef OPENSSL_NO_DSA
+
+#  ifndef OPENSSL_NO_DSA
 struct dsa_st;
+OSSL_DEPRECATEDIN_3_0
 int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, struct dsa_st *key);
-struct dsa_st *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey);
+OSSL_DEPRECATEDIN_3_0
+const struct dsa_st *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey);
+OSSL_DEPRECATEDIN_3_0
 struct dsa_st *EVP_PKEY_get1_DSA(EVP_PKEY *pkey);
-# endif
-# ifndef OPENSSL_NO_DEPRECATED_3_0
+#  endif
+
 #  ifndef OPENSSL_NO_DH
 struct dh_st;
 OSSL_DEPRECATEDIN_3_0 int EVP_PKEY_set1_DH(EVP_PKEY *pkey, struct dh_st *key);
-OSSL_DEPRECATEDIN_3_0 struct dh_st *EVP_PKEY_get0_DH(const EVP_PKEY *pkey);
+OSSL_DEPRECATEDIN_3_0 const struct dh_st *EVP_PKEY_get0_DH(const EVP_PKEY *pkey);
 OSSL_DEPRECATEDIN_3_0 struct dh_st *EVP_PKEY_get1_DH(EVP_PKEY *pkey);
 #  endif
-# endif
-# ifndef OPENSSL_NO_DEPRECATED_3_0
+
 #  ifndef OPENSSL_NO_EC
 struct ec_key_st;
 OSSL_DEPRECATEDIN_3_0
 int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, struct ec_key_st *key);
 OSSL_DEPRECATEDIN_3_0
-struct ec_key_st *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
+const struct ec_key_st *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
 OSSL_DEPRECATEDIN_3_0
 struct ec_key_st *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey);
 #  endif
-# endif
+# endif /* OPENSSL_NO_DEPRECATED_3_0 */
 
 EVP_PKEY *EVP_PKEY_new(void);
 int EVP_PKEY_up_ref(EVP_PKEY *pkey);
diff --git a/test/endecoder_legacy_test.c b/test/endecoder_legacy_test.c
index cdf86530ce..c72d15bdaa 100644
--- a/test/endecoder_legacy_test.c
+++ b/test/endecoder_legacy_test.c
@@ -58,11 +58,11 @@
 
 #include "testutil.h"
 
-typedef int PEM_write_bio_of_void_protected(BIO *out, void *obj,
+typedef int PEM_write_bio_of_void_protected(BIO *out, const void *obj,
                                             const EVP_CIPHER *enc,
                                             unsigned char *kstr, int klen,
                                             pem_password_cb *cb, void *u);
-typedef int PEM_write_bio_of_void_unprotected(BIO *out, void *obj);
+typedef int PEM_write_bio_of_void_unprotected(BIO *out, const void *obj);
 typedef void *PEM_read_bio_of_void(BIO *out, void **obj,
                                    pem_password_cb *cb, void *u);
 typedef int EVP_PKEY_print_fn(BIO *out, const EVP_PKEY *pkey,
@@ -294,7 +294,7 @@ static int test_membio_str_eq(BIO *bio_provided, BIO *bio_legacy)
 }
 
 static int test_protected_PEM(const char *keytype, int evp_type,
-                              void *legacy_key,
+                              const void *legacy_key,
                               PEM_write_bio_of_void_protected *pem_write_bio,
                               PEM_read_bio_of_void *pem_read_bio,
                               EVP_PKEY_eq_fn *evp_pkey_eq,
@@ -362,7 +362,7 @@ static int test_protected_PEM(const char *keytype, int evp_type,
 }
 
 static int test_unprotected_PEM(const char *keytype, int evp_type,
-                                void *legacy_key,
+                                const void *legacy_key,
                                 PEM_write_bio_of_void_unprotected *pem_write_bio,
                                 PEM_read_bio_of_void *pem_read_bio,
                                 EVP_PKEY_eq_fn *evp_pkey_eq,
@@ -429,7 +429,7 @@ static int test_unprotected_PEM(const char *keytype, int evp_type,
 }
 
 static int test_DER(const char *keytype, int evp_type,
-                    void *legacy_key, i2d_of_void *i2d, d2i_of_void *d2i,
+                    const void *legacy_key, i2d_of_void *i2d, d2i_of_void *d2i,
                     EVP_PKEY_eq_fn *evp_pkey_eq,
                     EVP_PKEY_print_fn *evp_pkey_print,
                     EVP_PKEY *provided_pkey, int selection,
@@ -506,7 +506,7 @@ static int test_key(int idx)
     int ok = 0;
     size_t i;
     EVP_PKEY *pkey = NULL, *downgraded_pkey = NULL;
-    void *legacy_obj = NULL;
+    const void *legacy_obj = NULL;
 
     /* Get the test data */
     if (!TEST_ptr(test_stanza = &test_stanzas[idx])
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 06d8e80200..b469d80a17 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -8313,7 +8313,14 @@ static DH *tmp_dh_callback(SSL *s, int is_export, int keylen)
     if (!TEST_ptr(dhpkey))
         return NULL;
 
-    ret = EVP_PKEY_get0_DH(dhpkey);
+    /*
+     * libssl does not free the returned DH, so we free it now knowing that even
+     * after we free dhpkey, there will still be a reference to the owning
+     * EVP_PKEY in tmp_dh_params, and so the DH object will live for the length
+     * of time we need it for.
+     */
+    ret = EVP_PKEY_get1_DH(dhpkey);
+    DH_free(ret);
 
     EVP_PKEY_free(dhpkey);
 
@@ -8361,7 +8368,7 @@ static int test_set_tmp_dh(int idx)
     }
 #  ifndef OPENSSL_NO_DEPRECATED_3_0
     if (idx == 7 || idx == 8) {
-        dh = EVP_PKEY_get0_DH(dhpkey);
+        dh = EVP_PKEY_get1_DH(dhpkey);
         if (!TEST_ptr(dh))
             goto end;
     }
@@ -8431,6 +8438,9 @@ static int test_set_tmp_dh(int idx)
     testresult = 1;
 
  end:
+#  ifndef OPENSSL_NO_DEPRECATED_3_0
+    DH_free(dh);
+#  endif
     SSL_free(serverssl);
     SSL_free(clientssl);
     SSL_CTX_free(sctx);
diff --git a/test/threadstest.c b/test/threadstest.c
index 26807e294c..1967ec6dad 100644
--- a/test/threadstest.c
+++ b/test/threadstest.c
@@ -7,6 +7,9 @@
  * https://www.openssl.org/source/license.html
  */
 
+/* test_multi below tests the thread safety of a deprecated function */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
 #if defined(_WIN32)
 # include <windows.h>
 #endif
@@ -401,23 +404,46 @@ static void thread_shared_evp_pkey(void)
         multi_success = 0;
 }
 
+static void thread_downgrade_shared_evp_pkey(void)
+{
+#ifndef OPENSSL_NO_DEPRECATED_3_0
+    /*
+     * This test is only relevant for deprecated functions that perform
+     * downgrading
+     */
+    if (EVP_PKEY_get0(shared_evp_pkey) == NULL)
+        multi_success = 0;
+#else
+    /* Shouldn't ever get here */
+    multi_success = 0;
+#endif
+}
+
+
 /*
  * Do work in multiple worker threads at the same time.
  * Test 0: General worker, using the default provider
  * Test 1: General worker, using the fips provider
  * Test 2: Simple fetch worker
- * Test 3: Worker using a shared EVP_PKEY
+ * Test 3: Worker downgrading a shared EVP_PKEY
+ * Test 4: Worker using a shared EVP_PKEY
  */
 static int test_multi(int idx)
 {
     thread_t thread1, thread2;
     int testresult = 0;
     OSSL_PROVIDER *prov = NULL, *prov2 = NULL;
-    void (*worker)(void);
+    void (*worker)(void) = NULL;
+    void (*worker2)(void) = NULL;
 
     if (idx == 1 && !do_fips)
         return TEST_skip("FIPS not supported");
 
+#ifdef OPENSSL_NO_DEPRECATED_3_0
+    if (idx == 3)
+        return TEST_skip("Skipping tests for deprected functions");
+#endif
+
     multi_success = 1;
     multi_libctx = OSSL_LIB_CTX_new();
     if (!TEST_ptr(multi_libctx))
@@ -435,6 +461,9 @@ static int test_multi(int idx)
         worker = thread_multi_simple_fetch;
         break;
     case 3:
+        worker2 = thread_downgrade_shared_evp_pkey;
+        /* fall through */
+    case 4:
         /*
          * If available we have both the default and fips providers for this
          * test
@@ -450,9 +479,11 @@ static int test_multi(int idx)
         TEST_error("Invalid test index");
         goto err;
     }
+    if (worker2 == NULL)
+        worker2 = worker;
 
     if (!TEST_true(run_thread(&thread1, worker))
-            || !TEST_true(run_thread(&thread2, worker)))
+            || !TEST_true(run_thread(&thread2, worker2)))
         goto err;
 
     worker();
@@ -547,7 +578,7 @@ int setup_tests(void)
     ADD_TEST(test_thread_local);
     ADD_TEST(test_atomic);
     ADD_TEST(test_multi_load);
-    ADD_ALL_TESTS(test_multi, 4);
+    ADD_ALL_TESTS(test_multi, 5);
     return 1;
 }
 
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 88b5648a74..309676f39b 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -1818,7 +1818,7 @@ PEM_write_PKCS8                         1860	3_0_0	EXIST::FUNCTION:STDIO
 PKCS7_digest_from_attributes            1861	3_0_0	EXIST::FUNCTION:
 EC_GROUP_set_curve_GFp                  1862	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,EC
 X509_PURPOSE_get0                       1863	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_set1_DSA                       1864	3_0_0	EXIST::FUNCTION:DSA
+EVP_PKEY_set1_DSA                       1864	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA
 X509_NAME_it                            1865	3_0_0	EXIST::FUNCTION:
 OBJ_add_object                          1866	3_0_0	EXIST::FUNCTION:
 DSA_generate_key                        1867	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA
@@ -1938,7 +1938,7 @@ OBJ_NAME_do_all                         1983	3_0_0	EXIST::FUNCTION:
 d2i_TS_MSG_IMPRINT_fp                   1984	3_0_0	EXIST::FUNCTION:STDIO,TS
 X509_CRL_verify                         1985	3_0_0	EXIST::FUNCTION:
 X509_get0_uids                          1986	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_get0_DSA                       1987	3_0_0	EXIST::FUNCTION:DSA
+EVP_PKEY_get0_DSA                       1987	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA
 d2i_CMS_ContentInfo                     1988	3_0_0	EXIST::FUNCTION:CMS
 EVP_CIPHER_meth_get_do_cipher           1989	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 i2d_DSA_PUBKEY                          1990	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA
@@ -2093,7 +2093,7 @@ BN_GF2m_mod_sqr                         2138	3_0_0	EXIST::FUNCTION:EC2M
 ASN1_PRINTABLE_new                      2139	3_0_0	EXIST::FUNCTION:
 OBJ_NAME_new_index                      2140	3_0_0	EXIST::FUNCTION:
 EVP_PKEY_asn1_add_alias                 2141	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_get1_DSA                       2142	3_0_0	EXIST::FUNCTION:DSA
+EVP_PKEY_get1_DSA                       2142	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA
 SEED_cbc_encrypt                        2143	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,SEED
 EVP_rc2_40_cbc                          2144	3_0_0	EXIST::FUNCTION:RC2
 ECDSA_SIG_new                           2145	3_0_0	EXIST::FUNCTION:EC
@@ -2603,7 +2603,7 @@ TS_MSG_IMPRINT_print_bio                2658	3_0_0	EXIST::FUNCTION:TS
 CONF_module_set_usr_data                2659	3_0_0	EXIST::FUNCTION:
 EC_KEY_generate_key                     2660	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,EC
 BIO_ctrl_get_write_guarantee            2661	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_assign                         2662	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_assign                         2662	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 EVP_aes_128_ofb                         2663	3_0_0	EXIST::FUNCTION:
 CMS_data                                2664	3_0_0	EXIST::FUNCTION:CMS
 X509_load_cert_file                     2665	3_0_0	EXIST::FUNCTION:
@@ -2809,7 +2809,7 @@ i2d_OCSP_CERTID                         2870	3_0_0	EXIST::FUNCTION:OCSP
 BN_CTX_start                            2871	3_0_0	EXIST::FUNCTION:
 BN_print                                2872	3_0_0	EXIST::FUNCTION:
 EC_KEY_set_flags                        2873	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,EC
-EVP_PKEY_get0                           2874	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_get0                           2874	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 ENGINE_set_default                      2875	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,ENGINE
 NCONF_get_number_e                      2876	3_0_0	EXIST::FUNCTION:
 OPENSSL_cleanse                         2877	3_0_0	EXIST::FUNCTION:
@@ -4002,7 +4002,7 @@ PEM_write_bio_PrivateKey_traditional    4091	3_0_0	EXIST::FUNCTION:
 X509_get_pathlen                        4092	3_0_0	EXIST::FUNCTION:
 ECDSA_SIG_set0                          4093	3_0_0	EXIST::FUNCTION:EC
 DSA_SIG_set0                            4094	3_0_0	EXIST::FUNCTION:DSA
-EVP_PKEY_get0_hmac                      4095	3_0_0	EXIST::FUNCTION:
+EVP_PKEY_get0_hmac                      4095	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 HMAC_CTX_get_md                         4096	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 NAME_CONSTRAINTS_check_CN               4097	3_0_0	EXIST::FUNCTION:
 OCSP_resp_get0_id                       4098	3_0_0	EXIST::FUNCTION:OCSP
@@ -4089,9 +4089,9 @@ UI_method_set_ex_data                   4178	3_0_0	EXIST::FUNCTION:
 UI_method_get_ex_data                   4179	3_0_0	EXIST::FUNCTION:
 UI_UTIL_wrap_read_pem_callback          4180	3_0_0	EXIST::FUNCTION:
 X509_VERIFY_PARAM_get_time              4181	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_get0_poly1305                  4182	3_0_0	EXIST::FUNCTION:POLY1305
+EVP_PKEY_get0_poly1305                  4182	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,POLY1305
 DH_check_params                         4183	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DH
-EVP_PKEY_get0_siphash                   4184	3_0_0	EXIST::FUNCTION:SIPHASH
+EVP_PKEY_get0_siphash                   4184	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,SIPHASH
 EVP_aria_256_ofb                        4185	3_0_0	EXIST::FUNCTION:ARIA
 EVP_aria_256_cfb128                     4186	3_0_0	EXIST::FUNCTION:ARIA
 EVP_aria_128_cfb1                       4187	3_0_0	EXIST::FUNCTION:ARIA
@@ -4234,7 +4234,7 @@ EVP_PKEY_meth_set_check                 4341	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_
 EVP_PKEY_meth_get_check                 4342	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 EVP_PKEY_meth_remove                    4343	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0
 OPENSSL_sk_reserve                      4344	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_set1_engine                    4347	3_0_0	EXIST::FUNCTION:ENGINE
+EVP_PKEY_set1_engine                    4347	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,ENGINE
 DH_new_by_nid                           4348	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DH
 DH_get_nid                              4349	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,DH
 CRYPTO_get_alloc_counts                 4350	3_0_0	EXIST::FUNCTION:CRYPTO_MDEBUG
@@ -4572,7 +4572,7 @@ OSSL_PARAM_get_octet_ptr                ?	3_0_0	EXIST::FUNCTION:
 OSSL_PARAM_set_octet_ptr                ?	3_0_0	EXIST::FUNCTION:
 X509_set0_distinguishing_id             ?	3_0_0	EXIST::FUNCTION:
 X509_get0_distinguishing_id             ?	3_0_0	EXIST::FUNCTION:
-EVP_PKEY_get0_engine                    ?	3_0_0	EXIST::FUNCTION:ENGINE
+EVP_PKEY_get0_engine                    ?	3_0_0	EXIST::FUNCTION:DEPRECATEDIN_3_0,ENGINE
 EVP_MD_up_ref                           ?	3_0_0	EXIST::FUNCTION:
 EVP_MD_fetch                            ?	3_0_0	EXIST::FUNCTION:
 EVP_set_default_properties              ?	3_0_0	EXIST::FUNCTION:


More information about the openssl-commits mailing list