[openssl] master update

Richard Levitte levitte at openssl.org
Wed Oct 27 10:44:54 UTC 2021


The branch master has been updated
       via  051228353a9842eede597294603cc06a55e3a22c (commit)
       via  dc010ca6ec01d313a84c3c4b040232655a1772ad (commit)
       via  433e13455ede1a39d415b690b8a564b4f36b8dee (commit)
       via  839ffdd11cd48d329a1d89565d62e0be082f9d08 (commit)
       via  ff7781462dd04ab99c159136b47672252bad7fa8 (commit)
       via  5246183e7a9f9fb1819d50ab40e2fecc68235e0d (commit)
       via  33561e0d5b89a06d1c03b952196d008b5014914a (commit)
       via  2fd3392c8f4e2f3481fa4d7e6a683dc19c6c1cd2 (commit)
       via  cfce50f791511c8fee7dec90c57f02d9410d039f (commit)
      from  8c590a219fe30b97cfde2efdd8ea94c03a90a8c6 (commit)


- Log -----------------------------------------------------------------
commit 051228353a9842eede597294603cc06a55e3a22c
Author: Tomas Mraz <tomas at openssl.org>
Date:   Wed Oct 6 19:21:53 2021 +0200

    test: fetching proper signature provider for non-exportable keys
    
    Co-author: Selva Nair <selva.nair at gmail.com>
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit dc010ca6ec01d313a84c3c4b040232655a1772ad
Author: Richard Levitte <levitte at openssl.org>
Date:   Mon Oct 4 15:33:37 2021 +0200

    CORE: Encure that cached fetches can be done per provider
    
    This mostly entails passing around a provider pointer, and handling
    queries that includes a pointer to a provider, where NULL means "any".
    
    This also means that there's a need to pass the provider pointer, not
    just down to the cache functions, but also be able to get it from
    ossl_method_store_fetch().  To this end, that function's OSSL_PROVIDER
    pointer argument is modified to be a pointer reference, so the
    function can answer back what provider the method comes from.
    
    Test added.
    
    Fixes #16614
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit 433e13455ede1a39d415b690b8a564b4f36b8dee
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Oct 1 15:02:15 2021 +0200

    EVP: For all operations that use an EVP_PKEY, check that there is one
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit 839ffdd11cd48d329a1d89565d62e0be082f9d08
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Oct 1 14:05:02 2021 +0200

    EVP: Allow a fallback for operations that work with an EVP_PKEY
    
    Functions like EVP_PKEY_sign_init() do an implicit fetch of the
    operation implementation (EVP_SIGNATURE in this case), then get the
    KEYMGMT from the same provider, and tries to export the key there if
    necessary.
    
    If an export of the key isn't possible (because the provider that
    holds the key is an HSM and therefore can't export), we would simply
    fail without looking any further.
    
    This change modifies the behaviour a bit by trying a second fetch of
    the operation implementation, but specifically from the provider of
    the EVP_PKEY that's being used.  This is done with the same properties
    that were used with the initial operation implementation fetch, and
    should therefore be safe, allowing only what those properties allow.
    
    Fixes #16614
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit ff7781462dd04ab99c159136b47672252bad7fa8
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Oct 1 12:06:52 2021 +0200

    EVP: Add internal functions to fetch type specific EVP methods from provider
    
    Added functions:
    
    evp_signature_fetch_from_prov(), evp_asym_cipher_fetch_from_prov(),
    evp_keyexch_fetch_from_prov(), evp_kem_fetch_from_prov()
    
    These are all like the public conterparts, except they all take a
    provider instead of a library context as first argument.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit 5246183e7a9f9fb1819d50ab40e2fecc68235e0d
Author: Richard Levitte <levitte at openssl.org>
Date:   Fri Oct 1 08:57:03 2021 +0200

    EVP: Reverse the fetch logic in all pkey using functionality
    
    In all initializing functions for functionality that use an EVP_PKEY, the
    coded logic was to find an KEYMGMT implementation first, and then try to
    find the operation method (for example, SIGNATURE implementation) in the
    same provider.
    
    This implies that in providers where there is a KEYMGMT implementation,
    there must also be a SIGNATURE implementation, along with a KEYEXCH,
    ASYM_CIPHER, etc implementation.
    
    The intended design was, however, the opposite implication, i.e. that
    where there is a SIGNATURE implementation, there must also be KEYMGMT.
    
    This change reverses the logic of the code to be closer to the intended
    design.
    
    There is a consequence; we now use the query_operation_name function from
    the KEYMGMT of the EVP_PKEY given by the EVP_PKEY_CTX (ultimately given by
    the application).  Previously, we used the query_operation_name function
    from the KEYMGMT found alongside the SIGNATURE implementation.
    
    Another minor consequence is that the |keymgmt| field in EVP_PKEY_CTX
    is now always a reference to the KEYMGMT of the |pkey| field if that
    one is given (|pkey| isn't NULL) and is provided (|pkey->keymgmt|
    isn't NULL).
    
    Fixes #16614
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit 33561e0d5b89a06d1c03b952196d008b5014914a
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Sep 30 17:40:16 2021 +0200

    EVP: Add evp_keymgmt_fetch_from_prov()
    
    This is an internal function to fetch a keymgmt method from a specific
    provider.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit 2fd3392c8f4e2f3481fa4d7e6a683dc19c6c1cd2
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Sep 30 09:44:10 2021 +0200

    EVP: Add the internal function evp_generic_fetch_from_prov()
    
    This function leverages the generic possibility to fetch EVP methods
    from a specific provider.
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

commit cfce50f791511c8fee7dec90c57f02d9410d039f
Author: Richard Levitte <levitte at openssl.org>
Date:   Thu Sep 30 09:32:57 2021 +0200

    CORE: add a provider argument to ossl_method_construct()
    
    This makes it possible to limit the search of methods to that
    particular provider.  This uses already available possibilities in
    ossl_algorithm_do_all().
    
    Reviewed-by: Tomas Mraz <tomas at openssl.org>
    Reviewed-by: Matt Caswell <matt at openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/16725)

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

Summary of changes:
 crypto/core_algorithm.c                            |  17 +-
 crypto/core_fetch.c                                |  13 +-
 crypto/encode_decode/decoder_meth.c                |  11 +-
 crypto/encode_decode/encoder_meth.c                |  11 +-
 crypto/evp/asymcipher.c                            | 129 +++++++++---
 crypto/evp/evp_fetch.c                             |  55 +++--
 crypto/evp/evp_local.h                             |  22 ++
 crypto/evp/exchange.c                              | 139 ++++++++----
 crypto/evp/kem.c                                   | 121 +++++++++--
 crypto/evp/keymgmt_lib.c                           |  19 ++
 crypto/evp/keymgmt_meth.c                          |  11 +
 crypto/evp/m_sigver.c                              | 119 ++++++++---
 crypto/evp/pmeth_lib.c                             |  15 +-
 crypto/evp/signature.c                             | 128 ++++++++---
 crypto/property/property.c                         |  73 ++++---
 crypto/store/store_meth.c                          |  11 +-
 doc/build.info                                     |  12 +-
 doc/internal/man3/OSSL_METHOD_STORE.pod            |  22 +-
 doc/internal/man3/evp_generic_fetch.pod            |  18 +-
 doc/internal/man3/ossl_method_construct.pod        |  31 +--
 .../{EVP_SIGNATURE_free.pod => EVP_SIGNATURE.pod}  |   3 +
 doc/man7/crypto.pod                                |  29 +++
 include/crypto/evp.h                               |   3 +
 include/internal/core.h                            |   4 +-
 include/internal/property.h                        |  13 +-
 test/build.info                                    |   5 +
 test/fake_rsaprov.c                                | 234 +++++++++++++++++++++
 test/{filterprov.h => fake_rsaprov.h}              |   6 +-
 test/property_test.c                               | 146 ++++++++++---
 test/provider_pkey_test.c                          | 132 ++++++++++++
 ...provider_fallback.t => 04-test_provider_pkey.t} |   4 +-
 util/other.syms                                    |   1 +
 32 files changed, 1264 insertions(+), 293 deletions(-)
 rename doc/man3/{EVP_SIGNATURE_free.pod => EVP_SIGNATURE.pod} (98%)
 create mode 100644 test/fake_rsaprov.c
 copy test/{filterprov.h => fake_rsaprov.h} (70%)
 create mode 100644 test/provider_pkey_test.c
 copy test/recipes/{04-test_provider_fallback.t => 04-test_provider_pkey.t} (81%)

diff --git a/crypto/core_algorithm.c b/crypto/core_algorithm.c
index 1a2e798c2c..5ff33eff7c 100644
--- a/crypto/core_algorithm.c
+++ b/crypto/core_algorithm.c
@@ -105,10 +105,23 @@ void ossl_algorithm_do_all(OSSL_LIB_CTX *libctx, int operation_id,
     cbdata.post = post;
     cbdata.data = data;
 
-    if (provider == NULL)
+    if (provider == NULL) {
         ossl_provider_doall_activated(libctx, algorithm_do_this, &cbdata);
-    else
+    } else {
+        OSSL_LIB_CTX *libctx2 = ossl_provider_libctx(provider);
+
+        /*
+         * If a provider is given, its library context MUST match the library
+         * context we're passed.  If this turns out not to be true, there is
+         * a programming error in the functions up the call stack.
+         */
+        if (!ossl_assert(ossl_lib_ctx_get_concrete(libctx)
+                         == ossl_lib_ctx_get_concrete(libctx2)))
+            return;
+
+        cbdata.libctx = libctx2;
         algorithm_do_this(provider, &cbdata);
+    }
 }
 
 char *ossl_algorithm_get1_first_name(const OSSL_ALGORITHM *algo)
diff --git a/crypto/core_fetch.c b/crypto/core_fetch.c
index d315599ce6..367f6ba8a4 100644
--- a/crypto/core_fetch.c
+++ b/crypto/core_fetch.c
@@ -105,19 +105,21 @@ static void ossl_method_construct_this(OSSL_PROVIDER *provider,
 }
 
 void *ossl_method_construct(OSSL_LIB_CTX *libctx, int operation_id,
-                            int force_store,
+                            OSSL_PROVIDER **provider_rw, int force_store,
                             OSSL_METHOD_CONSTRUCT_METHOD *mcm, void *mcm_data)
 {
     void *method = NULL;
 
-    if ((method = mcm->get(NULL, mcm_data)) == NULL) {
+    if ((method = mcm->get(NULL, (const OSSL_PROVIDER **)provider_rw,
+                           mcm_data)) == NULL) {
+        OSSL_PROVIDER *provider = provider_rw != NULL ? *provider_rw : NULL;
         struct construct_data_st cbdata;
 
         cbdata.store = NULL;
         cbdata.force_store = force_store;
         cbdata.mcm = mcm;
         cbdata.mcm_data = mcm_data;
-        ossl_algorithm_do_all(libctx, operation_id, NULL,
+        ossl_algorithm_do_all(libctx, operation_id, provider,
                               ossl_method_construct_precondition,
                               ossl_method_construct_this,
                               ossl_method_construct_postcondition,
@@ -125,11 +127,12 @@ void *ossl_method_construct(OSSL_LIB_CTX *libctx, int operation_id,
 
         /* If there is a temporary store, try there first */
         if (cbdata.store != NULL)
-            method = mcm->get(cbdata.store, mcm_data);
+            method = mcm->get(cbdata.store, (const OSSL_PROVIDER **)provider_rw,
+                              mcm_data);
 
         /* If no method was found yet, try the global store */
         if (method == NULL)
-            method = mcm->get(NULL, mcm_data);
+            method = mcm->get(NULL, (const OSSL_PROVIDER **)provider_rw, mcm_data);
     }
 
     return method;
diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c
index 8f0786c941..6d44437314 100644
--- a/crypto/encode_decode/decoder_meth.c
+++ b/crypto/encode_decode/decoder_meth.c
@@ -125,7 +125,8 @@ static OSSL_METHOD_STORE *get_decoder_store(OSSL_LIB_CTX *libctx)
 }
 
 /* Get decoder methods from a store, or put one in */
-static void *get_decoder_from_store(void *store, void *data)
+static void *get_decoder_from_store(void *store, const OSSL_PROVIDER **prov,
+                                    void *data)
 {
     struct decoder_data_st *methdata = data;
     void *method = NULL;
@@ -154,7 +155,7 @@ static void *get_decoder_from_store(void *store, void *data)
         && (store = get_decoder_store(methdata->libctx)) == NULL)
         return NULL;
 
-    if (!ossl_method_store_fetch(store, id, methdata->propquery, &method))
+    if (!ossl_method_store_fetch(store, id, methdata->propquery, prov, &method))
         return NULL;
     return method;
 }
@@ -366,7 +367,7 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, int id,
         unsupported = 1;
 
     if (id == 0
-        || !ossl_method_store_cache_get(store, id, properties, &method)) {
+        || !ossl_method_store_cache_get(store, NULL, id, properties, &method)) {
         OSSL_METHOD_CONSTRUCT_METHOD mcm = {
             get_tmp_decoder_store,
             get_decoder_from_store,
@@ -380,7 +381,7 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, int id,
         methdata->propquery = properties;
         methdata->flag_construct_error_occurred = 0;
         if ((method = ossl_method_construct(methdata->libctx, OSSL_OP_DECODER,
-                                            0 /* !force_cache */,
+                                            NULL, 0 /* !force_cache */,
                                             &mcm, methdata)) != NULL) {
             /*
              * If construction did create a method for us, we know that
@@ -391,7 +392,7 @@ inner_ossl_decoder_fetch(struct decoder_data_st *methdata, int id,
             if (id == 0 && name != NULL)
                 id = ossl_namemap_name2num(namemap, name);
             if (id != 0)
-                ossl_method_store_cache_set(store, id, properties, method,
+                ossl_method_store_cache_set(store, NULL, id, properties, method,
                                             up_ref_decoder, free_decoder);
         }
 
diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c
index 9f7ecc82cb..9c0214db6b 100644
--- a/crypto/encode_decode/encoder_meth.c
+++ b/crypto/encode_decode/encoder_meth.c
@@ -125,7 +125,8 @@ static OSSL_METHOD_STORE *get_encoder_store(OSSL_LIB_CTX *libctx)
 }
 
 /* Get encoder methods from a store, or put one in */
-static void *get_encoder_from_store(void *store, void *data)
+static void *get_encoder_from_store(void *store, const OSSL_PROVIDER **prov,
+                                    void *data)
 {
     struct encoder_data_st *methdata = data;
     void *method = NULL;
@@ -154,7 +155,7 @@ static void *get_encoder_from_store(void *store, void *data)
         && (store = get_encoder_store(methdata->libctx)) == NULL)
         return NULL;
 
-    if (!ossl_method_store_fetch(store, id, methdata->propquery, &method))
+    if (!ossl_method_store_fetch(store, id, methdata->propquery, prov, &method))
         return NULL;
     return method;
 }
@@ -376,7 +377,7 @@ inner_ossl_encoder_fetch(struct encoder_data_st *methdata, int id,
         unsupported = 1;
 
     if (id == 0
-        || !ossl_method_store_cache_get(store, id, properties, &method)) {
+        || !ossl_method_store_cache_get(store, NULL, id, properties, &method)) {
         OSSL_METHOD_CONSTRUCT_METHOD mcm = {
             get_tmp_encoder_store,
             get_encoder_from_store,
@@ -390,7 +391,7 @@ inner_ossl_encoder_fetch(struct encoder_data_st *methdata, int id,
         methdata->propquery = properties;
         methdata->flag_construct_error_occurred = 0;
         if ((method = ossl_method_construct(methdata->libctx, OSSL_OP_ENCODER,
-                                            0 /* !force_cache */,
+                                            NULL, 0 /* !force_cache */,
                                             &mcm, methdata)) != NULL) {
             /*
              * If construction did create a method for us, we know that
@@ -400,7 +401,7 @@ inner_ossl_encoder_fetch(struct encoder_data_st *methdata, int id,
              */
             if (id == 0)
                 id = ossl_namemap_name2num(namemap, name);
-            ossl_method_store_cache_set(store, id, properties, method,
+            ossl_method_store_cache_set(store, NULL, id, properties, method,
                                         up_ref_encoder, free_encoder);
         }
 
diff --git a/crypto/evp/asymcipher.c b/crypto/evp/asymcipher.c
index 3150bfa94b..b7784c8994 100644
--- a/crypto/evp/asymcipher.c
+++ b/crypto/evp/asymcipher.c
@@ -24,7 +24,9 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
     void *provkey = NULL;
     EVP_ASYM_CIPHER *cipher = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
     const char *supported_ciph = NULL;
+    int iter;
 
     if (ctx == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -39,56 +41,102 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
     if (evp_pkey_ctx_is_legacy(ctx))
         goto legacy;
 
+    if (ctx->pkey == NULL) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, EVP_R_NO_KEY_SET);
+        goto err;
+    }
+
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
-     *  If not, go legacy
+     * Try to derive the supported asym cipher from |ctx->keymgmt|.
      */
-    tmp_keymgmt = ctx->keymgmt;
-    provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
-                                          &tmp_keymgmt, ctx->propquery);
-    if (provkey == NULL)
-        goto legacy;
-    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+    if (!ossl_assert(ctx->pkey->keymgmt == NULL
+                     || ctx->pkey->keymgmt == ctx->keymgmt)) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    supported_ciph
+        = evp_keymgmt_util_query_operation_name(ctx->keymgmt,
+                                                OSSL_OP_ASYM_CIPHER);
+    if (supported_ciph == NULL) {
         ERR_clear_last_mark();
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    EVP_KEYMGMT_free(ctx->keymgmt);
-    ctx->keymgmt = tmp_keymgmt;
-
-    if (ctx->keymgmt->query_operation_name != NULL)
-        supported_ciph =
-            ctx->keymgmt->query_operation_name(OSSL_OP_ASYM_CIPHER);
 
     /*
-     * If we didn't get a supported ciph, assume there is one with the
-     * same name as the key type.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal asym cipher fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific asym cipher fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * asym cipher, and try to export |ctx->pkey| to that keymgmt (when
+     * this keymgmt happens to be the same as |ctx->keymgmt|, the export
+     * is a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_ciph == NULL)
-        supported_ciph = ctx->keytype;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree;
 
-    /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if cipher is already there.
-     */
-    cipher =
-        EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph, ctx->propquery);
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_ASYM_CIPHER_free(cipher);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            cipher = EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph,
+                                           ctx->propquery);
+            if (cipher != NULL)
+                tmp_prov = EVP_ASYM_CIPHER_get0_provider(cipher);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(ctx->keymgmt);
+            cipher =
+                evp_asym_cipher_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                                supported_ciph, ctx->propquery);
+            if (cipher == NULL)
+                goto legacy;
+            break;
+        }
+        if (cipher == NULL)
+            continue;
 
-    if (cipher == NULL
-        || (EVP_KEYMGMT_get0_provider(ctx->keymgmt)
-            != EVP_ASYM_CIPHER_get0_provider(cipher))) {
         /*
-         * We don't need to free ctx->keymgmt here, as it's not necessarily
-         * tied to this operation.  It will be freed by EVP_PKEY_CTX_free().
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |ctx->pkey|, but from the provider of the asym cipher method, using
+         * the same property query as when fetching the asym cipher method.
+         * With the keymgmt we found (if we did), we try to export |ctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+         * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
          */
+        tmp_keymgmt_tofree = tmp_keymgmt
+            = evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                          EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                          ctx->propquery);
+        if (tmp_keymgmt != NULL)
+            provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                                  &tmp_keymgmt, ctx->propquery);
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
         EVP_ASYM_CIPHER_free(cipher);
         goto legacy;
     }
 
-    /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
-     */
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -125,6 +173,7 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
 
     if (ret <= 0)
         goto err;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return 1;
 
  legacy:
@@ -133,6 +182,8 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
      * let's go see if legacy does.
      */
     ERR_pop_to_mark();
+    EVP_KEYMGMT_free(tmp_keymgmt);
+    tmp_keymgmt = NULL;
 
     if (ctx->pmeth == NULL || ctx->pmeth->encrypt == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -159,6 +210,7 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
         evp_pkey_ctx_free_old_ops(ctx);
         ctx->operation = EVP_PKEY_OP_UNDEFINED;
     }
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
 }
 
@@ -423,6 +475,17 @@ EVP_ASYM_CIPHER *EVP_ASYM_CIPHER_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                              (void (*)(void *))EVP_ASYM_CIPHER_free);
 }
 
+EVP_ASYM_CIPHER *evp_asym_cipher_fetch_from_prov(OSSL_PROVIDER *prov,
+                                                 const char *algorithm,
+                                                 const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_ASYM_CIPHER,
+                                       algorithm, properties,
+                                       evp_asym_cipher_from_algorithm,
+                                       (int (*)(void *))EVP_ASYM_CIPHER_up_ref,
+                                       (void (*)(void *))EVP_ASYM_CIPHER_free);
+}
+
 int EVP_ASYM_CIPHER_is_a(const EVP_ASYM_CIPHER *cipher, const char *name)
 {
     return evp_is_a(cipher->prov, cipher->name_id, NULL, name);
diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c
index 5303cf8859..80da3fa4bf 100644
--- a/crypto/evp/evp_fetch.c
+++ b/crypto/evp/evp_fetch.c
@@ -115,7 +115,8 @@ static uint32_t evp_method_id(int name_id, unsigned int operation_id)
             | (operation_id & METHOD_ID_OPERATION_MASK));
 }
 
-static void *get_evp_method_from_store(void *store, void *data)
+static void *get_evp_method_from_store(void *store, const OSSL_PROVIDER **prov,
+                                       void *data)
 {
     struct evp_method_data_st *methdata = data;
     void *method = NULL;
@@ -146,7 +147,7 @@ static void *get_evp_method_from_store(void *store, void *data)
         && (store = get_evp_method_store(methdata->libctx)) == NULL)
         return NULL;
 
-    if (!ossl_method_store_fetch(store, meth_id, methdata->propquery,
+    if (!ossl_method_store_fetch(store, meth_id, methdata->propquery, prov,
                                  &method))
         return NULL;
     return method;
@@ -234,7 +235,8 @@ static void destruct_evp_method(void *method, void *data)
 }
 
 static void *
-inner_evp_generic_fetch(struct evp_method_data_st *methdata, int operation_id,
+inner_evp_generic_fetch(struct evp_method_data_st *methdata,
+                        OSSL_PROVIDER *prov, int operation_id,
                         int name_id, const char *name,
                         const char *properties,
                         void *(*new_method)(int name_id,
@@ -297,7 +299,8 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, int operation_id,
         unsupported = 1;
 
     if (meth_id == 0
-        || !ossl_method_store_cache_get(store, meth_id, properties, &method)) {
+        || !ossl_method_store_cache_get(store, prov, meth_id, properties,
+                                        &method)) {
         OSSL_METHOD_CONSTRUCT_METHOD mcm = {
             get_tmp_evp_method_store,
             get_evp_method_from_store,
@@ -315,7 +318,7 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, int operation_id,
         methdata->destruct_method = free_method;
         methdata->flag_construct_error_occurred = 0;
         if ((method = ossl_method_construct(methdata->libctx, operation_id,
-                                            0 /* !force_cache */,
+                                            &prov, 0 /* !force_cache */,
                                             &mcm, methdata)) != NULL) {
             /*
              * If construction did create a method for us, we know that
@@ -327,8 +330,8 @@ inner_evp_generic_fetch(struct evp_method_data_st *methdata, int operation_id,
                 name_id = ossl_namemap_name2num(namemap, name);
             meth_id = evp_method_id(name_id, operation_id);
             if (name_id != 0)
-                ossl_method_store_cache_set(store, meth_id, properties, method,
-                                            up_ref_method, free_method);
+                ossl_method_store_cache_set(store, prov, meth_id, properties,
+                                            method, up_ref_method, free_method);
         }
 
         /*
@@ -366,8 +369,8 @@ void *evp_generic_fetch(OSSL_LIB_CTX *libctx, int operation_id,
 
     methdata.libctx = libctx;
     methdata.tmp_store = NULL;
-    method = inner_evp_generic_fetch(&methdata,
-                                     operation_id, 0, name, properties,
+    method = inner_evp_generic_fetch(&methdata, NULL, operation_id,
+                                     0, name, properties,
                                      new_method, up_ref_method, free_method);
     dealloc_tmp_evp_method_store(methdata.tmp_store);
     return method;
@@ -378,7 +381,7 @@ void *evp_generic_fetch(OSSL_LIB_CTX *libctx, int operation_id,
  * already known names, i.e. it refuses to work if no name_id can be found
  * (it's considered an internal programming error).
  * This is meant to be used when one method needs to fetch an associated
- * other method.
+ * method.
  */
 void *evp_generic_fetch_by_number(OSSL_LIB_CTX *libctx, int operation_id,
                                   int name_id, const char *properties,
@@ -393,8 +396,34 @@ void *evp_generic_fetch_by_number(OSSL_LIB_CTX *libctx, int operation_id,
 
     methdata.libctx = libctx;
     methdata.tmp_store = NULL;
-    method = inner_evp_generic_fetch(&methdata,
-                                     operation_id, name_id, NULL, properties,
+    method = inner_evp_generic_fetch(&methdata, NULL, operation_id,
+                                     name_id, NULL, properties,
+                                     new_method, up_ref_method, free_method);
+    dealloc_tmp_evp_method_store(methdata.tmp_store);
+    return method;
+}
+
+/*
+ * evp_generic_fetch_from_prov() is special, and only returns methods from
+ * the given provider.
+ * This is meant to be used when one method needs to fetch an associated
+ * method.
+ */
+void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id,
+                                  const char *name, const char *properties,
+                                  void *(*new_method)(int name_id,
+                                                      const OSSL_ALGORITHM *algodef,
+                                                      OSSL_PROVIDER *prov),
+                                  int (*up_ref_method)(void *),
+                                  void (*free_method)(void *))
+{
+    struct evp_method_data_st methdata;
+    void *method;
+
+    methdata.libctx = ossl_provider_libctx(prov);
+    methdata.tmp_store = NULL;
+    method = inner_evp_generic_fetch(&methdata, prov, operation_id,
+                                     0, name, properties,
                                      new_method, up_ref_method, free_method);
     dealloc_tmp_evp_method_store(methdata.tmp_store);
     return method;
@@ -588,7 +617,7 @@ void evp_generic_do_all(OSSL_LIB_CTX *libctx, int operation_id,
 
     methdata.libctx = libctx;
     methdata.tmp_store = NULL;
-    (void)inner_evp_generic_fetch(&methdata, operation_id, 0, NULL, NULL,
+    (void)inner_evp_generic_fetch(&methdata, NULL, operation_id, 0, NULL, NULL,
                                   new_method, up_ref_method, free_method);
 
     data.operation_id = operation_id;
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h
index f8fd3f05f5..d9e1ca997e 100644
--- a/crypto/evp/evp_local.h
+++ b/crypto/evp/evp_local.h
@@ -276,6 +276,13 @@ void *evp_generic_fetch_by_number(OSSL_LIB_CTX *ctx, int operation_id,
                                                       OSSL_PROVIDER *prov),
                                   int (*up_ref_method)(void *),
                                   void (*free_method)(void *));
+void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id,
+                                  const char *name, const char *properties,
+                                  void *(*new_method)(int name_id,
+                                                      const OSSL_ALGORITHM *algodef,
+                                                      OSSL_PROVIDER *prov),
+                                  int (*up_ref_method)(void *),
+                                  void (*free_method)(void *));
 void evp_generic_do_all_prefetched(OSSL_LIB_CTX *libctx, int operation_id,
                                    void (*user_fn)(void *method, void *arg),
                                    void *user_arg);
@@ -291,6 +298,21 @@ void evp_generic_do_all(OSSL_LIB_CTX *libctx, int operation_id,
 /* Internal fetchers for method types that are to be combined with others */
 EVP_KEYMGMT *evp_keymgmt_fetch_by_number(OSSL_LIB_CTX *ctx, int name_id,
                                          const char *properties);
+EVP_KEYMGMT *evp_keymgmt_fetch_from_prov(OSSL_PROVIDER *prov,
+                                         const char *name,
+                                         const char *properties);
+EVP_SIGNATURE *evp_signature_fetch_from_prov(OSSL_PROVIDER *prov,
+                                             const char *name,
+                                             const char *properties);
+EVP_ASYM_CIPHER *evp_asym_cipher_fetch_from_prov(OSSL_PROVIDER *prov,
+                                                 const char *name,
+                                                 const char *properties);
+EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov,
+                                         const char *name,
+                                         const char *properties);
+EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov,
+                                 const char *name,
+                                 const char *properties);
 
 /* Internal structure constructors for fetched methods */
 EVP_MD *evp_md_new(void);
diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c
index ca8a049a1b..e2ca30c94d 100644
--- a/crypto/evp/exchange.c
+++ b/crypto/evp/exchange.c
@@ -10,6 +10,7 @@
 #include <openssl/crypto.h>
 #include <openssl/evp.h>
 #include <openssl/err.h>
+#include "internal/cryptlib.h"
 #include "internal/refcount.h"
 #include "internal/provider.h"
 #include "internal/core.h"
@@ -180,6 +181,17 @@ EVP_KEYEXCH *EVP_KEYEXCH_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                              (void (*)(void *))EVP_KEYEXCH_free);
 }
 
+EVP_KEYEXCH *evp_keyexch_fetch_from_prov(OSSL_PROVIDER *prov,
+                                         const char *algorithm,
+                                         const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_KEYEXCH,
+                                       algorithm, properties,
+                                       evp_keyexch_from_algorithm,
+                                       (int (*)(void *))EVP_KEYEXCH_up_ref,
+                                       (void (*)(void *))EVP_KEYEXCH_free);
+}
+
 int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx)
 {
     return EVP_PKEY_derive_init_ex(ctx, NULL);
@@ -191,7 +203,9 @@ int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
     void *provkey = NULL;
     EVP_KEYEXCH *exchange = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
     const char *supported_exch = NULL;
+    int iter;
 
     if (ctx == NULL) {
         ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER);
@@ -207,73 +221,113 @@ int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
         goto legacy;
 
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
-     * If not, goto legacy
+     * Some algorithms (e.g. legacy KDFs) don't have a pkey - so we create
+     * a blank one.
      */
-    tmp_keymgmt = ctx->keymgmt;
     if (ctx->pkey == NULL) {
-        /*
-         * Some algorithms (e.g. legacy KDFs) don't have a pkey - so we create
-         * a blank one.
-         */
         EVP_PKEY *pkey = EVP_PKEY_new();
 
-        if (pkey == NULL || !EVP_PKEY_set_type_by_keymgmt(pkey, tmp_keymgmt)) {
+        if (pkey == NULL
+            || !EVP_PKEY_set_type_by_keymgmt(pkey, ctx->keymgmt)
+            || (pkey->keydata = evp_keymgmt_newdata(ctx->keymgmt)) == NULL) {
             ERR_clear_last_mark();
             EVP_PKEY_free(pkey);
             ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
             goto err;
         }
-        provkey = pkey->keydata = evp_keymgmt_newdata(tmp_keymgmt);
-        if (provkey == NULL)
-            EVP_PKEY_free(pkey);
-        else
-            ctx->pkey = pkey;
-    } else {
-        provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
-                                            &tmp_keymgmt, ctx->propquery);
+        ctx->pkey = pkey;
     }
-    if (provkey == NULL)
-        goto legacy;
-    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+
+    /*
+     * Try to derive the supported exch from |ctx->keymgmt|.
+     */
+    if (!ossl_assert(ctx->pkey->keymgmt == NULL
+                     || ctx->pkey->keymgmt == ctx->keymgmt)) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    supported_exch = evp_keymgmt_util_query_operation_name(ctx->keymgmt,
+                                                           OSSL_OP_KEYEXCH);
+    if (supported_exch == NULL) {
         ERR_clear_last_mark();
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    EVP_KEYMGMT_free(ctx->keymgmt);
-    ctx->keymgmt = tmp_keymgmt;
 
-    if (ctx->keymgmt->query_operation_name != NULL)
-        supported_exch = ctx->keymgmt->query_operation_name(OSSL_OP_KEYEXCH);
 
     /*
-     * If we didn't get a supported exch, assume there is one with the
-     * same name as the key type.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal exchange fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific exchange fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * exchange, and try to export |ctx->pkey| to that keymgmt (when
+     * this keymgmt happens to be the same as |ctx->keymgmt|, the export
+     * is a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_exch == NULL)
-        supported_exch = ctx->keytype;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
 
-    /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if exchange is already there.
-     */
-    exchange = EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_KEYEXCH_free(exchange);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            exchange =
+                EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
+            if (exchange != NULL)
+                tmp_prov = EVP_KEYEXCH_get0_provider(exchange);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(ctx->keymgmt);
+            exchange =
+                evp_keyexch_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                              supported_exch, ctx->propquery);
+            if (exchange == NULL)
+                goto legacy;
+            break;
+        }
+        if (exchange == NULL)
+            continue;
 
-    if (exchange == NULL
-        || (EVP_KEYMGMT_get0_provider(ctx->keymgmt)
-            != EVP_KEYEXCH_get0_provider(exchange))) {
         /*
-         * We don't need to free ctx->keymgmt here, as it's not necessarily
-         * tied to this operation.  It will be freed by EVP_PKEY_CTX_free().
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |ctx->pkey|, but from the provider of the exchange method, using
+         * the same property query as when fetching the exchange method.
+         * With the keymgmt we found (if we did), we try to export |ctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+         * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
          */
+        tmp_keymgmt_tofree = tmp_keymgmt =
+            evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                        EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                        ctx->propquery);
+        if (tmp_keymgmt != NULL)
+            provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                                  &tmp_keymgmt, ctx->propquery);
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
         EVP_KEYEXCH_free(exchange);
         goto legacy;
     }
 
-    /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
-     */
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -287,10 +341,12 @@ int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
     }
     ret = exchange->init(ctx->op.kex.algctx, provkey, params);
 
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret ? 1 : 0;
  err:
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return 0;
 
  legacy:
@@ -313,6 +369,7 @@ int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
     ret = ctx->pmeth->derive_init(ctx);
     if (ret <= 0)
         ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
 #endif
 }
diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c
index cb904a6b26..7594888b97 100644
--- a/crypto/evp/kem.c
+++ b/crypto/evp/kem.c
@@ -23,8 +23,10 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
     int ret = 0;
     EVP_KEM *kem = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
     void *provkey = NULL;
     const char *supported_kem = NULL;
+    int iter;
 
     if (ctx == NULL || ctx->keytype == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
@@ -34,35 +36,101 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = operation;
 
+    if (ctx->pkey == NULL) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_NO_KEY_SET);
+        goto err;
+    }
+
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
+     * Try to derive the supported kem from |ctx->keymgmt|.
      */
-    tmp_keymgmt = ctx->keymgmt;
-    provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
-                                          &tmp_keymgmt, ctx->propquery);
-    if (provkey == NULL
-        || !EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+    if (!ossl_assert(ctx->pkey->keymgmt == NULL
+                     || ctx->pkey->keymgmt == ctx->keymgmt)) {
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    supported_kem = evp_keymgmt_util_query_operation_name(ctx->keymgmt,
+                                                          OSSL_OP_KEM);
+    if (supported_kem == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    EVP_KEYMGMT_free(ctx->keymgmt);
-    ctx->keymgmt = tmp_keymgmt;
-
-    if (ctx->keymgmt->query_operation_name != NULL)
-        supported_kem = ctx->keymgmt->query_operation_name(OSSL_OP_KEM);
 
     /*
-     * If we didn't get a supported kem, assume there is one with the
-     * same name as the key type.
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if kem is already there.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal kem fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific kem fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * kem, and try to export |ctx->pkey| to that keymgmt (when this
+     * keymgmt happens to be the same as |ctx->keymgmt|, the export is
+     * a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_kem == NULL)
-        supported_kem = ctx->keytype;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
 
-    kem = EVP_KEM_fetch(ctx->libctx, supported_kem, ctx->propquery);
-    if (kem == NULL
-        || (EVP_KEYMGMT_get0_provider(ctx->keymgmt) != EVP_KEM_get0_provider(kem))) {
-        ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-        ret = -2;
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_KEM_free(kem);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            kem = EVP_KEM_fetch(ctx->libctx, supported_kem, ctx->propquery);
+            if (kem != NULL)
+                tmp_prov = EVP_KEM_get0_provider(kem);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(ctx->keymgmt);
+            kem = evp_kem_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                          supported_kem, ctx->propquery);
+
+            if (kem == NULL) {
+                ERR_raise(ERR_LIB_EVP,
+                          EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+                ret = -2;
+                goto err;
+            }
+        }
+        if (kem == NULL)
+            continue;
+
+        /*
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |ctx->pkey|, but from the provider of the kem method, using the
+         * same property query as when fetching the kem method.
+         * With the keymgmt we found (if we did), we try to export |ctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+
+         * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
+         */
+        tmp_keymgmt_tofree = tmp_keymgmt =
+            evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                        EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                        ctx->propquery);
+        if (tmp_keymgmt != NULL)
+            provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                                  &tmp_keymgmt, ctx->propquery);
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
+        EVP_KEM_free(kem);
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
 
@@ -96,6 +164,9 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
         goto err;
     }
 
+    EVP_KEYMGMT_free(tmp_keymgmt);
+    tmp_keymgmt = NULL;
+
     if (ret > 0)
         return 1;
  err:
@@ -103,6 +174,7 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
         evp_pkey_ctx_free_old_ops(ctx);
         ctx->operation = EVP_PKEY_OP_UNDEFINED;
     }
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
 }
 
@@ -338,6 +410,15 @@ EVP_KEM *EVP_KEM_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                              (void (*)(void *))EVP_KEM_free);
 }
 
+EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov, const char *algorithm,
+                                 const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_KEM, algorithm, properties,
+                                       evp_kem_from_algorithm,
+                                       (int (*)(void *))EVP_KEM_up_ref,
+                                       (void (*)(void *))EVP_KEM_free);
+}
+
 int EVP_KEM_is_a(const EVP_KEM *kem, const char *name)
 {
     return evp_is_a(kem->prov, kem->name_id, NULL, name);
diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c
index 875c9a83de..2a73e9a2be 100644
--- a/crypto/evp/keymgmt_lib.c
+++ b/crypto/evp/keymgmt_lib.c
@@ -562,3 +562,22 @@ int evp_keymgmt_util_get_deflt_digest_name(EVP_KEYMGMT *keymgmt,
         OPENSSL_strlcpy(mdname, result, mdname_sz);
     return rv;
 }
+
+/*
+ * If |keymgmt| has the method function |query_operation_name|, use it to get
+ * the name of a supported operation identity.  Otherwise, return the keytype,
+ * assuming that it works as a default operation name.
+ */
+const char *evp_keymgmt_util_query_operation_name(EVP_KEYMGMT *keymgmt,
+                                                  int op_id)
+{
+    const char *name = NULL;
+
+    if (keymgmt != NULL) {
+        if (keymgmt->query_operation_name != NULL)
+            name = keymgmt->query_operation_name(op_id);
+        if (name == NULL)
+            name = EVP_KEYMGMT_get0_name(keymgmt);
+    }
+    return name;
+}
diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c
index 47a0350cc2..fb999c7fd0 100644
--- a/crypto/evp/keymgmt_meth.c
+++ b/crypto/evp/keymgmt_meth.c
@@ -213,6 +213,17 @@ EVP_KEYMGMT *evp_keymgmt_fetch_by_number(OSSL_LIB_CTX *ctx, int name_id,
                                        (void (*)(void *))EVP_KEYMGMT_free);
 }
 
+EVP_KEYMGMT *evp_keymgmt_fetch_from_prov(OSSL_PROVIDER *prov,
+                                         const char *name,
+                                         const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_KEYMGMT,
+                                       name, properties,
+                                       keymgmt_from_algorithm,
+                                       (int (*)(void *))EVP_KEYMGMT_up_ref,
+                                       (void (*)(void *))EVP_KEYMGMT_free);
+}
+
 EVP_KEYMGMT *EVP_KEYMGMT_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                                const char *properties)
 {
diff --git a/crypto/evp/m_sigver.c b/crypto/evp/m_sigver.c
index 70669c3e6d..2972734d8d 100644
--- a/crypto/evp/m_sigver.c
+++ b/crypto/evp/m_sigver.c
@@ -45,10 +45,11 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     EVP_PKEY_CTX *locpctx = NULL;
     EVP_SIGNATURE *signature = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
     const char *supported_sig = NULL;
     char locmdname[80] = "";     /* 80 chars should be enough */
     void *provkey = NULL;
-    int ret;
+    int ret, iter;
 
     if (ctx->algctx != NULL) {
         if (!ossl_assert(ctx->digest != NULL)) {
@@ -80,58 +81,104 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     if (evp_pkey_ctx_is_legacy(locpctx))
         goto legacy;
 
+    if (locpctx->pkey == NULL) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, EVP_R_NO_KEY_SET);
+        goto err;
+    }
+
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
+     * Try to derive the supported signature from |locpctx->keymgmt|.
      */
-    tmp_keymgmt = locpctx->keymgmt;
-    provkey = evp_pkey_export_to_provider(locpctx->pkey, locpctx->libctx,
-                                          &tmp_keymgmt, locpctx->propquery);
-    if (provkey == NULL) {
+    if (!ossl_assert(locpctx->pkey->keymgmt == NULL
+                     || locpctx->pkey->keymgmt == locpctx->keymgmt)) {
         ERR_clear_last_mark();
-        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
         goto err;
     }
-    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+    supported_sig = evp_keymgmt_util_query_operation_name(locpctx->keymgmt,
+                                                          OSSL_OP_SIGNATURE);
+    if (supported_sig == NULL) {
         ERR_clear_last_mark();
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    EVP_KEYMGMT_free(locpctx->keymgmt);
-    locpctx->keymgmt = tmp_keymgmt;
-
-    if (locpctx->keymgmt->query_operation_name != NULL)
-        supported_sig =
-            locpctx->keymgmt->query_operation_name(OSSL_OP_SIGNATURE);
 
     /*
-     * If we didn't get a supported sig, assume there is one with the
-     * same name as the key type.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal signature fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific signature fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * signature, and try to export |ctx->pkey| to that keymgmt (when
+     * this keymgmt happens to be the same as |ctx->keymgmt|, the export
+     * is a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_sig == NULL)
-        supported_sig = locpctx->keytype;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
 
-    /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if signature is already there.
-     */
-    signature = EVP_SIGNATURE_fetch(locpctx->libctx, supported_sig,
-                                    locpctx->propquery);
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_SIGNATURE_free(signature);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            signature = EVP_SIGNATURE_fetch(locpctx->libctx, supported_sig,
+                                            locpctx->propquery);
+            if (signature != NULL)
+                tmp_prov = EVP_SIGNATURE_get0_provider(signature);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(locpctx->keymgmt);
+            signature =
+                evp_signature_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                              supported_sig, locpctx->propquery);
+            if (signature == NULL)
+                goto legacy;
+            break;
+        }
+        if (signature == NULL)
+            continue;
 
-    if (signature == NULL
-        || (EVP_KEYMGMT_get0_provider(locpctx->keymgmt)
-            != EVP_SIGNATURE_get0_provider(signature))) {
         /*
-         * We don't need to free ctx->keymgmt here, as it's not necessarily
-         * tied to this operation.  It will be freed by EVP_PKEY_CTX_free().
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |locpctx->pkey|, but from the provider of the signature method, using
+         * the same property query as when fetching the signature method.
+         * With the keymgmt we found (if we did), we try to export |locpctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+
+         * export it if |tmp_keymgmt| is different from |locpctx->pkey|'s keymgmt)
          */
+        tmp_keymgmt_tofree = tmp_keymgmt =
+            evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                        EVP_KEYMGMT_get0_name(locpctx->keymgmt),
+                                        locpctx->propquery);
+        if (tmp_keymgmt != NULL)
+            provkey = evp_pkey_export_to_provider(locpctx->pkey, locpctx->libctx,
+                                                  &tmp_keymgmt, locpctx->propquery);
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
         EVP_SIGNATURE_free(signature);
-        goto legacy;
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
     }
 
-    /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
-     */
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -221,6 +268,7 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
  err:
     evp_pkey_ctx_free_old_ops(locpctx);
     locpctx->operation = EVP_PKEY_OP_UNDEFINED;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return 0;
 
  legacy:
@@ -229,6 +277,8 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
      * let's go see if legacy does.
      */
     ERR_pop_to_mark();
+    EVP_KEYMGMT_free(tmp_keymgmt);
+    tmp_keymgmt = NULL;
 
     if (type == NULL && mdname != NULL)
         type = evp_get_digestbyname_ex(locpctx->libctx, mdname);
@@ -299,6 +349,7 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
         ret = evp_pkey_ctx_use_cached_data(locpctx);
 #endif
 
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret > 0 ? 1 : 0;
 }
 
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 1af1628823..2b9c6c2351 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -265,7 +265,20 @@ static EVP_PKEY_CTX *int_ctx_new(OSSL_LIB_CTX *libctx,
      * fetching a provider implementation.
      */
     if (e == NULL && app_pmeth == NULL && keytype != NULL) {
-        keymgmt = EVP_KEYMGMT_fetch(libctx, keytype, propquery);
+        /*
+         * If |pkey| is given and is provided, we take a reference to its
+         * keymgmt.  Otherwise, we fetch one for the keytype we got. This
+         * is to ensure that operation init functions can access what they
+         * need through this single pointer.
+         */
+        if (pkey != NULL && pkey->keymgmt != NULL) {
+            if (!EVP_KEYMGMT_up_ref(pkey->keymgmt))
+                ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+            else
+                keymgmt = pkey->keymgmt;
+        } else {
+            keymgmt = EVP_KEYMGMT_fetch(libctx, keytype, propquery);
+        }
         if (keymgmt == NULL)
             return NULL;   /* EVP_KEYMGMT_fetch() recorded an error */
 
diff --git a/crypto/evp/signature.c b/crypto/evp/signature.c
index b636889c3b..49f40c8cec 100644
--- a/crypto/evp/signature.c
+++ b/crypto/evp/signature.c
@@ -314,6 +314,17 @@ EVP_SIGNATURE *EVP_SIGNATURE_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                              (void (*)(void *))EVP_SIGNATURE_free);
 }
 
+EVP_SIGNATURE *evp_signature_fetch_from_prov(OSSL_PROVIDER *prov,
+                                             const char *algorithm,
+                                             const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_SIGNATURE,
+                                       algorithm, properties,
+                                       evp_signature_from_algorithm,
+                                       (int (*)(void *))EVP_SIGNATURE_up_ref,
+                                       (void (*)(void *))EVP_SIGNATURE_free);
+}
+
 int EVP_SIGNATURE_is_a(const EVP_SIGNATURE *signature, const char *name)
 {
     return evp_is_a(signature->prov, signature->name_id, NULL, name);
@@ -386,7 +397,9 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
     void *provkey = NULL;
     EVP_SIGNATURE *signature = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
     const char *supported_sig = NULL;
+    int iter;
 
     if (ctx == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -401,55 +414,102 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
     if (evp_pkey_ctx_is_legacy(ctx))
         goto legacy;
 
+    if (ctx->pkey == NULL) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, EVP_R_NO_KEY_SET);
+        goto err;
+    }
+
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
-     *  If not, go legacy
+     * Try to derive the supported signature from |ctx->keymgmt|.
      */
-    tmp_keymgmt = ctx->keymgmt;
-    provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
-                                          &tmp_keymgmt, ctx->propquery);
-    if (tmp_keymgmt == NULL)
-        goto legacy;
-    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+    if (!ossl_assert(ctx->pkey->keymgmt == NULL
+                     || ctx->pkey->keymgmt == ctx->keymgmt)) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    supported_sig = evp_keymgmt_util_query_operation_name(ctx->keymgmt,
+                                                          OSSL_OP_SIGNATURE);
+    if (supported_sig == NULL) {
         ERR_clear_last_mark();
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
-    EVP_KEYMGMT_free(ctx->keymgmt);
-    ctx->keymgmt = tmp_keymgmt;
-
-    if (ctx->keymgmt->query_operation_name != NULL)
-        supported_sig = ctx->keymgmt->query_operation_name(OSSL_OP_SIGNATURE);
 
     /*
-     * If we didn't get a supported sig, assume there is one with the
-     * same name as the key type.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal signature fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific signature fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * signature, and try to export |ctx->pkey| to that keymgmt (when
+     * this keymgmt happens to be the same as |ctx->keymgmt|, the export
+     * is a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_sig == NULL)
-        supported_sig = ctx->keytype;
+    for (iter = 1; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
 
-    /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if signature is already there.
-     */
-    signature =
-        EVP_SIGNATURE_fetch(ctx->libctx, supported_sig, ctx->propquery);
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_SIGNATURE_free(signature);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            signature =
+                EVP_SIGNATURE_fetch(ctx->libctx, supported_sig, ctx->propquery);
+            if (signature != NULL)
+                tmp_prov = EVP_SIGNATURE_get0_provider(signature);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(ctx->keymgmt);
+            signature =
+                evp_signature_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                              supported_sig, ctx->propquery);
+            if (signature == NULL)
+                goto legacy;
+            break;
+        }
+        if (signature == NULL)
+            continue;
 
-    if (signature == NULL
-        || (EVP_KEYMGMT_get0_provider(ctx->keymgmt)
-            != EVP_SIGNATURE_get0_provider(signature))) {
         /*
-         * We don't need to free ctx->keymgmt here, as it's not necessarily
-         * tied to this operation.  It will be freed by EVP_PKEY_CTX_free().
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |ctx->pkey|, but from the provider of the signature method, using
+         * the same property query as when fetching the signature method.
+         * With the keymgmt we found (if we did), we try to export |ctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+
+         * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
          */
+        tmp_keymgmt_tofree = tmp_keymgmt =
+            evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                        EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                        ctx->propquery);
+        if (tmp_keymgmt != NULL)
+            provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                                  &tmp_keymgmt, ctx->propquery);
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
         EVP_SIGNATURE_free(signature);
         goto legacy;
     }
 
-    /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
-     */
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -507,6 +567,8 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
      * let's go see if legacy does.
      */
     ERR_pop_to_mark();
+    EVP_KEYMGMT_free(tmp_keymgmt);
+    tmp_keymgmt = NULL;
 
     if (ctx->pmeth == NULL
             || (operation == EVP_PKEY_OP_SIGN && ctx->pmeth->sign == NULL)
@@ -545,10 +607,12 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
         ret = evp_pkey_ctx_use_cached_data(ctx);
 #endif
 
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
  err:
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = EVP_PKEY_OP_UNDEFINED;
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
 }
 
diff --git a/crypto/property/property.c b/crypto/property/property.c
index a4cd612b9d..5df1bfc221 100644
--- a/crypto/property/property.c
+++ b/crypto/property/property.c
@@ -45,6 +45,7 @@ typedef struct {
 DEFINE_STACK_OF(IMPLEMENTATION)
 
 typedef struct {
+    const OSSL_PROVIDER *provider;
     const char *query;
     METHOD method;
     char body[1];
@@ -172,7 +173,13 @@ static unsigned long query_hash(const QUERY *a)
 
 static int query_cmp(const QUERY *a, const QUERY *b)
 {
-    return strcmp(a->query, b->query);
+    int res = strcmp(a->query, b->query);
+
+    if (res == 0 && a->provider != NULL && b->provider != NULL)
+        res = b->provider > a->provider ? 1
+            : b->provider < a->provider ? -1
+            : 0;
+    return res;
 }
 
 static void impl_free(IMPLEMENTATION *impl)
@@ -260,6 +267,9 @@ int ossl_method_store_add(OSSL_METHOD_STORE *store, const OSSL_PROVIDER *prov,
     if (properties == NULL)
         properties = "";
 
+    if (!ossl_assert(prov != NULL))
+        return 0;
+
     /* Create new entry */
     impl = OPENSSL_malloc(sizeof(*impl));
     if (impl == NULL)
@@ -393,15 +403,15 @@ void ossl_method_store_do_all(OSSL_METHOD_STORE *store,
         ossl_sa_ALGORITHM_doall_arg(store->algs, alg_do_each, &data);
 }
 
-int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int nid,
-                            const char *prop_query,
-                            void **method)
+int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
+                            int nid, const char *prop_query,
+                            const OSSL_PROVIDER **prov_rw, void **method)
 {
     OSSL_PROPERTY_LIST **plp;
     ALGORITHM *alg;
-    IMPLEMENTATION *impl;
+    IMPLEMENTATION *impl, *best_impl = NULL;
     OSSL_PROPERTY_LIST *pq = NULL, *p2 = NULL;
-    METHOD *best_method = NULL;
+    const OSSL_PROVIDER *prov = prov_rw != NULL ? *prov_rw : NULL;
     int ret = 0;
     int j, best = -1, score, optional;
 
@@ -438,29 +448,38 @@ int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int nid,
     }
 
     if (pq == NULL) {
-        if ((impl = sk_IMPLEMENTATION_value(alg->impls, 0)) != NULL) {
-            best_method = &impl->method;
-            ret = 1;
+        for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
+            if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL
+                && (prov == NULL || impl->provider == prov)) {
+                best_impl = impl;
+                ret = 1;
+                break;
+            }
         }
         goto fin;
     }
     optional = ossl_property_has_optional(pq);
     for (j = 0; j < sk_IMPLEMENTATION_num(alg->impls); j++) {
-        impl = sk_IMPLEMENTATION_value(alg->impls, j);
-        score = ossl_property_match_count(pq, impl->properties);
-        if (score > best) {
-            best_method = &impl->method;
-            best = score;
-            ret = 1;
-            if (!optional)
-                goto fin;
+        if ((impl = sk_IMPLEMENTATION_value(alg->impls, j)) != NULL
+            && (prov == NULL || impl->provider == prov)) {
+            score = ossl_property_match_count(pq, impl->properties);
+            if (score > best) {
+                best_impl = impl;
+                best = score;
+                ret = 1;
+                if (!optional)
+                    goto fin;
+            }
         }
     }
 fin:
-    if (ret && ossl_method_up_ref(best_method))
-        *method = best_method->method;
-    else
+    if (ret && ossl_method_up_ref(&best_impl->method)) {
+        *method = best_impl->method.method;
+        if (prov_rw != NULL)
+            *prov_rw = best_impl->provider;
+    } else {
         ret = 0;
+    }
     ossl_property_unlock(store);
     ossl_property_free(p2);
     return ret;
@@ -569,8 +588,8 @@ static void ossl_method_cache_flush_some(OSSL_METHOD_STORE *store)
     store->nelem = state.nelem;
 }
 
-int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, int nid,
-                                const char *prop_query, void **method)
+int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+                                int nid, const char *prop_query, void **method)
 {
     ALGORITHM *alg;
     QUERY elem, *r;
@@ -586,6 +605,7 @@ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, int nid,
         goto err;
 
     elem.query = prop_query != NULL ? prop_query : "";
+    elem.provider = prov;
     r = lh_QUERY_retrieve(alg->cache, &elem);
     if (r == NULL)
         goto err;
@@ -598,8 +618,8 @@ err:
     return res;
 }
 
-int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
-                                const char *prop_query, void *method,
+int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+                                int nid, const char *prop_query, void *method,
                                 int (*method_up_ref)(void *),
                                 void (*method_destruct)(void *))
 {
@@ -613,6 +633,9 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
     if (prop_query == NULL)
         return 1;
 
+    if (!ossl_assert(prov != NULL))
+        return 0;
+
     if (!ossl_property_write_lock(store))
         return 0;
     if (store->need_flush)
@@ -623,6 +646,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
 
     if (method == NULL) {
         elem.query = prop_query;
+        elem.provider = prov;
         if ((old = lh_QUERY_delete(alg->cache, &elem)) != NULL) {
             impl_cache_free(old);
             store->nelem--;
@@ -632,6 +656,7 @@ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
     p = OPENSSL_malloc(sizeof(*p) + (len = strlen(prop_query)));
     if (p != NULL) {
         p->query = p->body;
+        p->provider = prov;
         p->method.method = method;
         p->method.up_ref = method_up_ref;
         p->method.free = method_destruct;
diff --git a/crypto/store/store_meth.c b/crypto/store/store_meth.c
index 61230a6c24..e79ec871fd 100644
--- a/crypto/store/store_meth.c
+++ b/crypto/store/store_meth.c
@@ -128,7 +128,8 @@ static OSSL_METHOD_STORE *get_loader_store(OSSL_LIB_CTX *libctx)
 }
 
 /* Get loader methods from a store, or put one in */
-static void *get_loader_from_store(void *store, void *data)
+static void *get_loader_from_store(void *store, const OSSL_PROVIDER **prov,
+                                   void *data)
 {
     struct loader_data_st *methdata = data;
     void *method = NULL;
@@ -144,7 +145,7 @@ static void *get_loader_from_store(void *store, void *data)
         && (store = get_loader_store(methdata->libctx)) == NULL)
         return NULL;
 
-    if (!ossl_method_store_fetch(store, id, methdata->propquery, &method))
+    if (!ossl_method_store_fetch(store, id, methdata->propquery, prov, &method))
         return NULL;
     return method;
 }
@@ -308,7 +309,7 @@ inner_loader_fetch(struct loader_data_st *methdata, int id,
         unsupported = 1;
 
     if (id == 0
-        || !ossl_method_store_cache_get(store, id, properties, &method)) {
+        || !ossl_method_store_cache_get(store, NULL, id, properties, &method)) {
         OSSL_METHOD_CONSTRUCT_METHOD mcm = {
             get_tmp_loader_store,
             get_loader_from_store,
@@ -322,7 +323,7 @@ inner_loader_fetch(struct loader_data_st *methdata, int id,
         methdata->propquery = properties;
         methdata->flag_construct_error_occurred = 0;
         if ((method = ossl_method_construct(methdata->libctx, OSSL_OP_STORE,
-                                            0 /* !force_cache */,
+                                            NULL, 0 /* !force_cache */,
                                             &mcm, methdata)) != NULL) {
             /*
              * If construction did create a method for us, we know that there
@@ -331,7 +332,7 @@ inner_loader_fetch(struct loader_data_st *methdata, int id,
              */
             if (id == 0)
                 id = ossl_namemap_name2num(namemap, scheme);
-            ossl_method_store_cache_set(store, id, properties, method,
+            ossl_method_store_cache_set(store, NULL, id, properties, method,
                                         up_ref_loader, free_loader);
         }
 
diff --git a/doc/build.info b/doc/build.info
index 5f446e3868..97e6bd3b51 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -1307,10 +1307,10 @@ DEPEND[html/man3/EVP_RAND.html]=man3/EVP_RAND.pod
 GENERATE[html/man3/EVP_RAND.html]=man3/EVP_RAND.pod
 DEPEND[man/man3/EVP_RAND.3]=man3/EVP_RAND.pod
 GENERATE[man/man3/EVP_RAND.3]=man3/EVP_RAND.pod
-DEPEND[html/man3/EVP_SIGNATURE_free.html]=man3/EVP_SIGNATURE_free.pod
-GENERATE[html/man3/EVP_SIGNATURE_free.html]=man3/EVP_SIGNATURE_free.pod
-DEPEND[man/man3/EVP_SIGNATURE_free.3]=man3/EVP_SIGNATURE_free.pod
-GENERATE[man/man3/EVP_SIGNATURE_free.3]=man3/EVP_SIGNATURE_free.pod
+DEPEND[html/man3/EVP_SIGNATURE.html]=man3/EVP_SIGNATURE.pod
+GENERATE[html/man3/EVP_SIGNATURE.html]=man3/EVP_SIGNATURE.pod
+DEPEND[man/man3/EVP_SIGNATURE.3]=man3/EVP_SIGNATURE.pod
+GENERATE[man/man3/EVP_SIGNATURE.3]=man3/EVP_SIGNATURE.pod
 DEPEND[html/man3/EVP_SealInit.html]=man3/EVP_SealInit.pod
 GENERATE[html/man3/EVP_SealInit.html]=man3/EVP_SealInit.pod
 DEPEND[man/man3/EVP_SealInit.3]=man3/EVP_SealInit.pod
@@ -3046,7 +3046,7 @@ html/man3/EVP_PKEY_todata.html \
 html/man3/EVP_PKEY_verify.html \
 html/man3/EVP_PKEY_verify_recover.html \
 html/man3/EVP_RAND.html \
-html/man3/EVP_SIGNATURE_free.html \
+html/man3/EVP_SIGNATURE.html \
 html/man3/EVP_SealInit.html \
 html/man3/EVP_SignInit.html \
 html/man3/EVP_VerifyInit.html \
@@ -3638,7 +3638,7 @@ man/man3/EVP_PKEY_todata.3 \
 man/man3/EVP_PKEY_verify.3 \
 man/man3/EVP_PKEY_verify_recover.3 \
 man/man3/EVP_RAND.3 \
-man/man3/EVP_SIGNATURE_free.3 \
+man/man3/EVP_SIGNATURE.3 \
 man/man3/EVP_SealInit.3 \
 man/man3/EVP_SignInit.3 \
 man/man3/EVP_VerifyInit.3 \
diff --git a/doc/internal/man3/OSSL_METHOD_STORE.pod b/doc/internal/man3/OSSL_METHOD_STORE.pod
index 7d9b80778d..5d9219fd0e 100644
--- a/doc/internal/man3/OSSL_METHOD_STORE.pod
+++ b/doc/internal/man3/OSSL_METHOD_STORE.pod
@@ -27,14 +27,14 @@ ossl_method_store_flush_cache
                               int nid, const void *method);
  int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
                              int nid, const char *properties,
-                             void **method);
- int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, int nid,
-                                 const char *prop_query, void **method);
- int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
-                                 const char *prop_query, void *method,
+                             void **method, const OSSL_PROVIDER **prov_rw);
+ int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+                                 int nid, const char *prop_query, void **method);
+ int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+                                 int nid, const char *prop_query, void *method,
                                  int (*method_up_ref)(void *),
                                  void (*method_destruct)(void *));
- void ossl_method_store_flush_cache(OSSL_METHOD_STORE *store);
+ void ossl_method_store_flush_cache(OSSL_METHOD_STORE *store, int all);
 
 =head1 DESCRIPTION
 
@@ -79,7 +79,9 @@ I<store>.
 
 ossl_method_store_fetch() queries I<store> for a method identified by I<nid>
 that matches the property query I<prop_query>.
-The result, if any, is returned in I<method>.
+I<*prop> may be a pointer to a provider, which will narrow the search
+to methods from that provider.
+The result, if any, is returned in I<*method>, and its provider in I<*prov>.
 
 ossl_method_store_flush_cache() flushes all cached entries associated with
 I<store>.
@@ -89,10 +91,12 @@ I<store>.
 ossl_method_store_cache_get() queries the cache associated with the I<store>
 for a method identified by I<nid> that matches the property query
 I<prop_query>.
+Additionally, if I<prov> isn't NULL, it will be used to narrow the search
+to only include methods from that provider.
 The result, if any, is returned in I<method>.
 
-ossl_method_store_cache_set() sets a cache entry identified by I<nid> with the
-property query I<prop_query> in the I<store>.
+ossl_method_store_cache_set() sets a cache entry identified by I<nid> from the
+provider I<prov>, with the property query I<prop_query> in the I<store>.
 Future calls to ossl_method_store_cache_get() will return the specified I<method>.
 The I<method_up_ref> function is called to increment the
 reference count of the method and the I<method_destruct> function is called
diff --git a/doc/internal/man3/evp_generic_fetch.pod b/doc/internal/man3/evp_generic_fetch.pod
index bc9a3a0770..b23d2ec0ea 100644
--- a/doc/internal/man3/evp_generic_fetch.pod
+++ b/doc/internal/man3/evp_generic_fetch.pod
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-evp_generic_fetch, evp_generic_fetch_by_number
+evp_generic_fetch, evp_generic_fetch_by_number, evp_generic_fetch_from_prov
 - generic algorithm fetchers and method creators for EVP
 
 =head1 SYNOPSIS
@@ -29,6 +29,15 @@ evp_generic_fetch, evp_generic_fetch_by_number
                                    void *method_data,
                                    int (*up_ref_method)(void *),
                                    void (*free_method)(void *));
+ void *evp_generic_fetch_from_prov(OSSL_PROVIDER *prov, int operation_id,
+                                   int name_id, const char *properties,
+                                   void *(*new_method)(int name_id,
+                                                       const OSSL_DISPATCH *fns,
+                                                       OSSL_PROVIDER *prov,
+                                                       void *method_data),
+                                   void *method_data,
+                                   int (*up_ref_method)(void *),
+                                   void (*free_method)(void *));
 
 =head1 DESCRIPTION
 
@@ -42,9 +51,14 @@ but takes a numeric I<name_id> instead of a name.
 I<name_id> must always be nonzero; as a matter of fact, it being zero
 is considered a programming error.
 This is meant to be used when one method needs to fetch an associated
-other method, and is typically called from inside the given function
+method, and is typically called from inside the given function
 I<new_method>.
 
+evp_generic_fetch_from_prov() does the same thing as evp_generic_fetch(),
+but limits the search of methods to the provider given with I<prov>.
+This is meant to be used when one method needs to fetch an associated
+method in the same provider.
+
 The three functions I<new_method>, I<up_ref_method>, and
 I<free_method> are supposed to:
 
diff --git a/doc/internal/man3/ossl_method_construct.pod b/doc/internal/man3/ossl_method_construct.pod
index 46a17ba7b6..3683798b06 100644
--- a/doc/internal/man3/ossl_method_construct.pod
+++ b/doc/internal/man3/ossl_method_construct.pod
@@ -13,21 +13,20 @@ OSSL_METHOD_CONSTRUCT_METHOD, ossl_method_construct
      /* Get a temporary store */
      void *(*get_tmp_store)(void *data);
      /* Get an already existing method from a store */
-     void *(*get)(void *store, void *data);
+     void *(*get)(void *store, const OSSL_PROVIDER *prov, void *data);
      /* Store a method in a store */
-     int (*put)(void *store, void *method,
-                const OSSL_PROVIDER *prov, const char *name,
-                const char *propdef, void *data);
+     int (*put)(void *store, void *method, const OSSL_PROVIDER *prov,
+                const char *name, const char *propdef, void *data);
      /* Construct a new method */
-     void *(*construct)(const char *name, const OSSL_DISPATCH *fns,
-                        OSSL_PROVIDER *prov, void *data);
+     void *(*construct)(const OSSL_ALGORITHM *algodef, OSSL_PROVIDER *prov,
+                        void *data);
      /* Destruct a method */
-     void (*destruct)(void *method);
+     void (*destruct)(void *method, void *data);
  };
  typedef struct ossl_method_construct_method OSSL_METHOD_CONSTRUCT_METHOD;
 
  void *ossl_method_construct(OSSL_LIB_CTX *ctx, int operation_id,
-                             int force_cache,
+                             OSSL_PROVIDER *prov, int force_cache,
                              OSSL_METHOD_CONSTRUCT_METHOD *mcm, void *mcm_data);
 
 
@@ -57,6 +56,9 @@ providers for a dispatch table given an I<operation_id>, and then
 calling the appropriate functions given by the subsystem specific
 method creator through I<mcm> and the data in I<mcm_data> (which is
 passed by ossl_method_construct()).
+If I<prov> is not NULL, only that provider is considered, which is
+useful in the case a method must be found in that particular
+provider.
 
 This function assumes that the subsystem method creator implements
 reference counting and acts accordingly (i.e. it will call the
@@ -72,17 +74,13 @@ function pointers:
 
 =over 4
 
-=item alloc_tmp_store()
+=item get_tmp_store()
 
 Create a temporary method store in the scope of the library context I<ctx>.
 This store is used to temporarily store methods for easier lookup, for
 when the provider doesn't want its dispatch table stored in a longer
 term cache.
 
-=item dealloc_tmp_store()
-
-Remove a temporary store.
-
 =item get()
 
 Look up an already existing method from a store by name.
@@ -97,7 +95,10 @@ The method to be looked up should be identified with data found in I<data>
 In other words, the ossl_method_construct() caller is entirely responsible
 for ensuring the necesssary data is made available.
 
-This function is expected to increment the method's reference count.
+Optionally, I<prov> may be given as a search criterion, to narrow down the
+search of a method belonging to just one provider.
+
+This function is expected to increment the resulting method's reference count.
 
 =item put()
 
@@ -109,7 +110,7 @@ NULL is a valid value and means that a subsystem default store
 must be used.
 This default store should be stored in the library context I<libctx>.
 
-The method should be associated with the given I<operation_id>,
+The method should be associated with the given provider I<prov>,
 I<name> and property definition I<propdef> as well as any
 identification data given through I<data> (which is the I<mcm_data>
 that was passed to ossl_construct_method()).
diff --git a/doc/man3/EVP_SIGNATURE_free.pod b/doc/man3/EVP_SIGNATURE.pod
similarity index 98%
rename from doc/man3/EVP_SIGNATURE_free.pod
rename to doc/man3/EVP_SIGNATURE.pod
index 4642f40efc..9fb389e7ae 100644
--- a/doc/man3/EVP_SIGNATURE_free.pod
+++ b/doc/man3/EVP_SIGNATURE.pod
@@ -2,6 +2,7 @@
 
 =head1 NAME
 
+EVP_SIGNATURE,
 EVP_SIGNATURE_fetch, EVP_SIGNATURE_free, EVP_SIGNATURE_up_ref,
 EVP_SIGNATURE_is_a, EVP_SIGNATURE_get0_provider,
 EVP_SIGNATURE_do_all_provided, EVP_SIGNATURE_names_do_all,
@@ -13,6 +14,8 @@ EVP_SIGNATURE_gettable_ctx_params, EVP_SIGNATURE_settable_ctx_params
 
  #include <openssl/evp.h>
 
+ typedef struct evp_signature_st EVP_SIGNATURE;
+
  EVP_SIGNATURE *EVP_SIGNATURE_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                                     const char *properties);
  void EVP_SIGNATURE_free(EVP_SIGNATURE *signature);
diff --git a/doc/man7/crypto.pod b/doc/man7/crypto.pod
index 16a07fc0ac..9aa667118d 100644
--- a/doc/man7/crypto.pod
+++ b/doc/man7/crypto.pod
@@ -181,6 +181,35 @@ is supplied. In this case an algorithm implementation is implicitly fetched
 using default search criteria and an algorithm name that is consistent with
 the context in which it is being used.
 
+Functions that revolve around B<EVP_PKEY_CTX> and L<EVP_PKEY(3)>, such as
+L<EVP_DigestSignInit(3)> and friends, all fetch the implementations
+implicitly.  Because these functions involve both an operation type (such as
+L<EVP_SIGNATURE(3)>) and an L<EVP_KEYMGMT(3)> for the L<EVP_PKEY(3)>, they try
+the following:
+
+=over 4
+
+=item 1.
+
+Fetch the operation type implementation from any provider given a library
+context and property string stored in the B<EVP_PKEY_CTX>.
+
+If the provider of the operation type implementation is different from the
+provider of the L<EVP_PKEY(3)>'s L<EVP_KEYMGMT(3)> implementation, try to
+fetch a L<EVP_KEYMGMT(3)> implementation in the same provider as the operation
+type implementation and export the L<EVP_PKEY(3)> to it (effectively making a
+temporary copy of the original key).
+
+If anything in this step fails, the next step is used as a fallback.
+
+=item 2.
+
+As a fallback, try to fetch the operation type implementation from the same
+provider as the original L<EVP_PKEY(3)>'s L<EVP_KEYMGMT(3)>, still using the
+propery string from the B<EVP_PKEY_CTX>.
+
+=back
+
 =head1 FETCHING EXAMPLES
 
 The following section provides a series of examples of fetching algorithm
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 41ac80ed9d..c5d3a930f7 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -38,6 +38,7 @@ struct evp_pkey_ctx_st {
     OSSL_LIB_CTX *libctx;
     char *propquery;
     const char *keytype;
+    /* If |pkey| below is set, this field is always a reference to its keymgmt */
     EVP_KEYMGMT *keymgmt;
 
     union {
@@ -794,6 +795,8 @@ void *evp_keymgmt_util_gen(EVP_PKEY *target, EVP_KEYMGMT *keymgmt,
 int evp_keymgmt_util_get_deflt_digest_name(EVP_KEYMGMT *keymgmt,
                                            void *keydata,
                                            char *mdname, size_t mdname_sz);
+const char *evp_keymgmt_util_query_operation_name(EVP_KEYMGMT *keymgmt,
+                                                  int op_id);
 
 /*
  * KEYMGMT provider interface functions
diff --git a/include/internal/core.h b/include/internal/core.h
index 035b726894..d9dc424164 100644
--- a/include/internal/core.h
+++ b/include/internal/core.h
@@ -31,7 +31,7 @@ typedef struct ossl_method_construct_method_st {
     /* Get a temporary store */
     void *(*get_tmp_store)(void *data);
     /* Get an already existing method from a store */
-    void *(*get)(void *store, void *data);
+    void *(*get)(void *store, const OSSL_PROVIDER **prov, void *data);
     /* Store a method in a store */
     int (*put)(void *store, void *method, const OSSL_PROVIDER *prov,
                const char *name, const char *propdef, void *data);
@@ -43,7 +43,7 @@ typedef struct ossl_method_construct_method_st {
 } OSSL_METHOD_CONSTRUCT_METHOD;
 
 void *ossl_method_construct(OSSL_LIB_CTX *ctx, int operation_id,
-                            int force_cache,
+                            OSSL_PROVIDER **provider_rw, int force_cache,
                             OSSL_METHOD_CONSTRUCT_METHOD *mcm, void *mcm_data);
 
 void ossl_algorithm_do_all(OSSL_LIB_CTX *libctx, int operation_id,
diff --git a/include/internal/property.h b/include/internal/property.h
index dd9a2dc2d8..8211974595 100644
--- a/include/internal/property.h
+++ b/include/internal/property.h
@@ -61,18 +61,19 @@ int ossl_method_store_remove(OSSL_METHOD_STORE *store, int nid,
 void ossl_method_store_do_all(OSSL_METHOD_STORE *store,
                               void (*fn)(int id, void *method, void *fnarg),
                               void *fnarg);
-int ossl_method_store_fetch(OSSL_METHOD_STORE *store, int nid,
-                            const char *prop_query, void **method);
+int ossl_method_store_fetch(OSSL_METHOD_STORE *store,
+                            int nid, const char *prop_query,
+                            const OSSL_PROVIDER **prov, void **method);
 
 /* Get the global properties associate with the specified library context */
 OSSL_PROPERTY_LIST **ossl_ctx_global_properties(OSSL_LIB_CTX *ctx,
                                                 int loadconfig);
 
 /* property query cache functions */
-int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, int nid,
-                                const char *prop_query, void **result);
-int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, int nid,
-                                const char *prop_query, void *result,
+int ossl_method_store_cache_get(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+                                int nid, const char *prop_query, void **result);
+int ossl_method_store_cache_set(OSSL_METHOD_STORE *store, OSSL_PROVIDER *prov,
+                                int nid, const char *prop_query, void *result,
                                 int (*method_up_ref)(void *),
                                 void (*method_destruct)(void *));
 
diff --git a/test/build.info b/test/build.info
index 1e4205ddb3..0f379e11e2 100644
--- a/test/build.info
+++ b/test/build.info
@@ -844,6 +844,11 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[provider_fallback_test]=../include ../apps/include
   DEPEND[provider_fallback_test]=../libcrypto libtestutil.a
 
+  PROGRAMS{noinst}=provider_pkey_test
+  SOURCE[provider_pkey_test]=provider_pkey_test.c fake_rsaprov.c
+  INCLUDE[provider_pkey_test]=../include ../apps/include
+  DEPEND[provider_pkey_test]=../libcrypto libtestutil.a
+
   PROGRAMS{noinst}=params_test
   SOURCE[params_test]=params_test.c
   INCLUDE[params_test]=.. ../include ../apps/include
diff --git a/test/fake_rsaprov.c b/test/fake_rsaprov.c
new file mode 100644
index 0000000000..e4833a6a99
--- /dev/null
+++ b/test/fake_rsaprov.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * https://www.openssl.org/source/license.html
+ * or in the file LICENSE in the source distribution.
+ */
+
+#include <string.h>
+#include <openssl/core_names.h>
+#include <openssl/rand.h>
+#include <openssl/provider.h>
+#include "testutil.h"
+#include "fake_rsaprov.h"
+
+static OSSL_FUNC_keymgmt_new_fn fake_rsa_keymgmt_new;
+static OSSL_FUNC_keymgmt_free_fn fake_rsa_keymgmt_free;
+static OSSL_FUNC_keymgmt_has_fn fake_rsa_keymgmt_has;
+static OSSL_FUNC_keymgmt_query_operation_name_fn fake_rsa_keymgmt_query;
+static OSSL_FUNC_keymgmt_import_fn fake_rsa_keymgmt_import;
+static OSSL_FUNC_keymgmt_import_types_fn fake_rsa_keymgmt_imptypes;
+
+static int has_selection;
+static int imptypes_selection;
+static int query_id;
+
+static void *fake_rsa_keymgmt_new(void *provctx)
+{
+    unsigned char *keydata = OPENSSL_zalloc(1);
+
+    TEST_ptr(keydata);
+
+    /* clear test globals */
+    has_selection = 0;
+    imptypes_selection = 0;
+    query_id = 0;
+
+    return keydata;
+}
+
+static void fake_rsa_keymgmt_free(void *keydata)
+{
+    OPENSSL_free(keydata);
+}
+
+static int fake_rsa_keymgmt_has(const void *key, int selection)
+{
+    /* record global for checking */
+    has_selection = selection;
+
+    return 1;
+}
+
+
+static const char *fake_rsa_keymgmt_query(int id)
+{
+    /* record global for checking */
+    query_id = id;
+
+    return "RSA";
+}
+
+static int fake_rsa_keymgmt_import(void *keydata, int selection,
+                                   const OSSL_PARAM *p)
+{
+    unsigned char *fake_rsa_key = keydata;
+
+    /* key was imported */
+    *fake_rsa_key = 1;
+
+    return 1;
+}
+
+static const OSSL_PARAM fake_rsa_import_key_types[] = {
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0),
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *fake_rsa_keymgmt_imptypes(int selection)
+{
+    /* record global for checking */
+    imptypes_selection = selection;
+
+    return fake_rsa_import_key_types;
+}
+
+static const OSSL_DISPATCH fake_rsa_keymgmt_funcs[] = {
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))fake_rsa_keymgmt_new },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))fake_rsa_keymgmt_free} ,
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))fake_rsa_keymgmt_has },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+        (void (*)(void))fake_rsa_keymgmt_query },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))fake_rsa_keymgmt_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES,
+        (void (*)(void))fake_rsa_keymgmt_imptypes },
+    { 0, NULL }
+};
+
+static const OSSL_ALGORITHM fake_rsa_keymgmt_algs[] = {
+    { "RSA:rsaEncryption", "provider=fake-rsa", fake_rsa_keymgmt_funcs, "Fake RSA Key Management" },
+    { NULL, NULL, NULL, NULL }
+};
+
+static OSSL_FUNC_signature_newctx_fn fake_rsa_sig_newctx;
+static OSSL_FUNC_signature_freectx_fn fake_rsa_sig_freectx;
+static OSSL_FUNC_signature_sign_init_fn fake_rsa_sig_sign_init;
+static OSSL_FUNC_signature_sign_fn fake_rsa_sig_sign;
+
+static void *fake_rsa_sig_newctx(void *provctx, const char *propq)
+{
+    unsigned char *sigctx = OPENSSL_zalloc(1);
+
+    TEST_ptr(sigctx);
+
+    return sigctx;
+}
+
+static void fake_rsa_sig_freectx(void *sigctx)
+{
+    OPENSSL_free(sigctx);
+}
+
+static int fake_rsa_sig_sign_init(void *ctx, void *provkey,
+                                  const OSSL_PARAM params[])
+{
+    unsigned char *sigctx = ctx;
+    unsigned char *keydata = provkey;
+
+    /* we must have a ctx */
+    if (!TEST_ptr(sigctx))
+        return 0;
+
+    /* we must have some initialized key */
+    if (!TEST_ptr(keydata) || !TEST_int_gt(keydata[0], 0))
+        return 0;
+
+    /* record that sign init was called */
+    *sigctx = 1;
+    return 1;
+}
+
+static int fake_rsa_sig_sign(void *ctx, unsigned char *sig,
+                             size_t *siglen, size_t sigsize,
+                             const unsigned char *tbs, size_t tbslen)
+{
+    unsigned char *sigctx = ctx;
+
+    /* we must have a ctx and init was called upon it */
+    if (!TEST_ptr(sigctx) || !TEST_int_eq(*sigctx, 1))
+        return 0;
+
+    *siglen = 256;
+    /* record that the real sign operation was called */
+    if (sig != NULL) {
+        if (!TEST_int_ge(sigsize, *siglen))
+            return 0;
+        *sigctx = 2;
+        /* produce a fake signature */
+        memset(sig, 'a', *siglen);
+    }
+
+    return 1;
+}
+
+static const OSSL_DISPATCH fake_rsa_sig_funcs[] = {
+    { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))fake_rsa_sig_newctx },
+    { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))fake_rsa_sig_freectx },
+    { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))fake_rsa_sig_sign_init },
+    { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))fake_rsa_sig_sign },
+    { 0, NULL }
+};
+
+static const OSSL_ALGORITHM fake_rsa_sig_algs[] = {
+    { "RSA:rsaEncryption", "provider=fake-rsa", fake_rsa_sig_funcs, "Fake RSA Signature" },
+    { NULL, NULL, NULL, NULL }
+};
+
+static const OSSL_ALGORITHM *fake_rsa_query(void *provctx,
+                                            int operation_id,
+                                            int *no_cache)
+{
+    *no_cache = 0;
+    switch (operation_id) {
+    case OSSL_OP_SIGNATURE:
+        return fake_rsa_sig_algs;
+
+    case OSSL_OP_KEYMGMT:
+        return fake_rsa_keymgmt_algs;
+    }
+    return NULL;
+}
+
+/* Functions we provide to the core */
+static const OSSL_DISPATCH fake_rsa_method[] = {
+    { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))OSSL_LIB_CTX_free },
+    { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))fake_rsa_query },
+    { 0, NULL }
+};
+
+static int fake_rsa_provider_init(const OSSL_CORE_HANDLE *handle,
+                                  const OSSL_DISPATCH *in,
+                                  const OSSL_DISPATCH **out, void **provctx)
+{
+    if (!TEST_ptr(*provctx = OSSL_LIB_CTX_new()))
+        return 0;
+    *out = fake_rsa_method;
+    return 1;
+}
+
+OSSL_PROVIDER *fake_rsa_start(OSSL_LIB_CTX *libctx)
+{
+    OSSL_PROVIDER *p;
+
+    if (!TEST_true(OSSL_PROVIDER_add_builtin(libctx, "fake-rsa",
+                                             fake_rsa_provider_init))
+            || !TEST_ptr(p = OSSL_PROVIDER_try_load(libctx, "fake-rsa", 1)))
+        return NULL;
+
+    return p;
+}
+
+void fake_rsa_finish(OSSL_PROVIDER *p)
+{
+    OSSL_PROVIDER_unload(p);
+}
diff --git a/test/filterprov.h b/test/fake_rsaprov.h
similarity index 70%
copy from test/filterprov.h
copy to test/fake_rsaprov.h
index 3c63071556..57de1ecf8d 100644
--- a/test/filterprov.h
+++ b/test/fake_rsaprov.h
@@ -9,6 +9,6 @@
 
 #include <openssl/core_dispatch.h>
 
-OSSL_provider_init_fn filter_provider_init;
-int filter_provider_set_filter(int operation, const char *name);
-int filter_provider_check_clean_finish(void);
+/* Fake RSA provider implementation */
+OSSL_PROVIDER *fake_rsa_start(OSSL_LIB_CTX *libctx);
+void fake_rsa_finish(OSSL_PROVIDER *p);
diff --git a/test/property_test.c b/test/property_test.c
index c23ddb0f99..ad44cf1513 100644
--- a/test/property_test.c
+++ b/test/property_test.c
@@ -15,6 +15,16 @@
 #include "internal/property.h"
 #include "../crypto/property/property_local.h"
 
+/*
+ * We make our OSSL_PROVIDER for testing purposes.  All we really need is
+ * a pointer.  We know that as long as we don't try to use the method
+ * cache flush functions, the provider pointer is merely a pointer being
+ * passed around, and used as a tag of sorts.
+ */
+struct ossl_provider_st {
+    int x;
+};
+
 static int add_property_names(const char *n, ...)
 {
     va_list args;
@@ -313,13 +323,14 @@ static int test_register_deregister(void)
     size_t i;
     int ret = 0;
     OSSL_METHOD_STORE *store;
+    OSSL_PROVIDER prov = { 1 };
 
     if (!TEST_ptr(store = ossl_method_store_new(NULL))
         || !add_property_names("position", NULL))
         goto err;
 
     for (i = 0; i < OSSL_NELEM(impls); i++)
-        if (!TEST_true(ossl_method_store_add(store, NULL, impls[i].nid,
+        if (!TEST_true(ossl_method_store_add(store, &prov, impls[i].nid,
                                              impls[i].prop, impls[i].impl,
                                              &up_ref, &down_ref))) {
             TEST_note("iteration %zd", i + 1);
@@ -348,34 +359,40 @@ err:
 
 static int test_property(void)
 {
+    static OSSL_PROVIDER fake_provider1 = { 1 };
+    static OSSL_PROVIDER fake_provider2 = { 2 };
+    static const OSSL_PROVIDER *fake_prov1 = &fake_provider1;
+    static const OSSL_PROVIDER *fake_prov2 = &fake_provider2;
     static const struct {
+        const OSSL_PROVIDER **prov;
         int nid;
         const char *prop;
         char *impl;
     } impls[] = {
-        { 1, "fast=no, colour=green", "a" },
-        { 1, "fast, colour=blue", "b" },
-        { 1, "", "-" },
-        { 9, "sky=blue, furry", "c" },
-        { 3, NULL, "d" },
-        { 6, "sky.colour=blue, sky=green, old.data", "e" },
+        { &fake_prov1, 1, "fast=no, colour=green", "a" },
+        { &fake_prov1, 1, "fast, colour=blue", "b" },
+        { &fake_prov1, 1, "", "-" },
+        { &fake_prov2, 9, "sky=blue, furry", "c" },
+        { &fake_prov2, 3, NULL, "d" },
+        { &fake_prov2, 6, "sky.colour=blue, sky=green, old.data", "e" },
     };
     static struct {
+        const OSSL_PROVIDER **prov;
         int nid;
         const char *prop;
         char *expected;
     } queries[] = {
-        { 1, "fast", "b" },
-        { 1, "fast=yes", "b" },
-        { 1, "fast=no, colour=green", "a" },
-        { 1, "colour=blue, fast", "b" },
-        { 1, "colour=blue", "b" },
-        { 9, "furry", "c" },
-        { 6, "sky.colour=blue", "e" },
-        { 6, "old.data", "e" },
-        { 9, "furry=yes, sky=blue", "c" },
-        { 1, "", "a" },
-        { 3, "", "d" },
+        { &fake_prov1, 1, "fast", "b" },
+        { &fake_prov1, 1, "fast=yes", "b" },
+        { &fake_prov1, 1, "fast=no, colour=green", "a" },
+        { &fake_prov1, 1, "colour=blue, fast", "b" },
+        { &fake_prov1, 1, "colour=blue", "b" },
+        { &fake_prov2, 9, "furry", "c" },
+        { &fake_prov2, 6, "sky.colour=blue", "e" },
+        { &fake_prov2, 6, "old.data", "e" },
+        { &fake_prov2, 9, "furry=yes, sky=blue", "c" },
+        { &fake_prov1, 1, "", "a" },
+        { &fake_prov2, 3, "", "d" },
     };
     OSSL_METHOD_STORE *store;
     size_t i;
@@ -387,17 +404,24 @@ static int test_property(void)
         goto err;
 
     for (i = 0; i < OSSL_NELEM(impls); i++)
-        if (!TEST_true(ossl_method_store_add(store, NULL, impls[i].nid,
-                                             impls[i].prop, impls[i].impl,
+        if (!TEST_true(ossl_method_store_add(store, *impls[i].prov,
+                                             impls[i].nid, impls[i].prop,
+                                             impls[i].impl,
                                              &up_ref, &down_ref))) {
             TEST_note("iteration %zd", i + 1);
             goto err;
         }
+    /*
+     * The first check of queries is with NULL given as provider.  All
+     * queries are expected to succeed.
+     */
     for (i = 0; i < OSSL_NELEM(queries); i++) {
+        const OSSL_PROVIDER *nullprov = NULL;
         OSSL_PROPERTY_LIST *pq = NULL;
 
-        if (!TEST_true(ossl_method_store_fetch(store, queries[i].nid,
-                                               queries[i].prop, &result))
+        if (!TEST_true(ossl_method_store_fetch(store,
+                                               queries[i].nid, queries[i].prop,
+                                               &nullprov, &result))
             || !TEST_str_eq((char *)result, queries[i].expected)) {
             TEST_note("iteration %zd", i + 1);
             ossl_property_free(pq);
@@ -405,6 +429,70 @@ static int test_property(void)
         }
         ossl_property_free(pq);
     }
+    /*
+     * The second check of queries is with &address1 given as provider.
+     */
+    for (i = 0; i < OSSL_NELEM(queries); i++) {
+        OSSL_PROPERTY_LIST *pq = NULL;
+
+        result = NULL;
+        if (queries[i].prov == &fake_prov1) {
+            if (!TEST_true(ossl_method_store_fetch(store,
+                                                   queries[i].nid,
+                                                   queries[i].prop,
+                                                   &fake_prov1, &result))
+                || !TEST_ptr_eq(fake_prov1, &fake_provider1)
+                || !TEST_str_eq((char *)result, queries[i].expected)) {
+                TEST_note("iteration %zd", i + 1);
+                ossl_property_free(pq);
+                goto err;
+            }
+        } else {
+            if (!TEST_false(ossl_method_store_fetch(store,
+                                                    queries[i].nid,
+                                                    queries[i].prop,
+                                                    &fake_prov1, &result))
+                || !TEST_ptr_eq(fake_prov1, &fake_provider1)
+                || !TEST_ptr_null(result)) {
+                TEST_note("iteration %zd", i + 1);
+                ossl_property_free(pq);
+                goto err;
+            }
+        }
+        ossl_property_free(pq);
+    }
+    /*
+     * The third check of queries is with &address2 given as provider.
+     */
+    for (i = 0; i < OSSL_NELEM(queries); i++) {
+        OSSL_PROPERTY_LIST *pq = NULL;
+
+        result = NULL;
+        if (queries[i].prov == &fake_prov2) {
+            if (!TEST_true(ossl_method_store_fetch(store,
+                                                   queries[i].nid,
+                                                   queries[i].prop,
+                                                   &fake_prov2, &result))
+                || !TEST_ptr_eq(fake_prov2, &fake_provider2)
+                || !TEST_str_eq((char *)result, queries[i].expected)) {
+                TEST_note("iteration %zd", i + 1);
+                ossl_property_free(pq);
+                goto err;
+            }
+        } else {
+            if (!TEST_false(ossl_method_store_fetch(store,
+                                                    queries[i].nid,
+                                                    queries[i].prop,
+                                                    &fake_prov2, &result))
+                || !TEST_ptr_eq(fake_prov2, &fake_provider2)
+                || !TEST_ptr_null(result)) {
+                TEST_note("iteration %zd", i + 1);
+                ossl_property_free(pq);
+                goto err;
+            }
+        }
+        ossl_property_free(pq);
+    }
     ret = 1;
 err:
     ossl_method_store_free(store);
@@ -420,6 +508,7 @@ static int test_query_cache_stochastic(void)
     void *result;
     int errors = 0;
     int v[10001];
+    OSSL_PROVIDER prov = { 1 };
 
     if (!TEST_ptr(store = ossl_method_store_new(NULL))
         || !add_property_names("n", NULL))
@@ -428,20 +517,21 @@ static int test_query_cache_stochastic(void)
     for (i = 1; i <= max; i++) {
         v[i] = 2 * i;
         BIO_snprintf(buf, sizeof(buf), "n=%d\n", i);
-        if (!TEST_true(ossl_method_store_add(store, NULL, i, buf, "abc",
+        if (!TEST_true(ossl_method_store_add(store, &prov, i, buf, "abc",
                                              &up_ref, &down_ref))
-                || !TEST_true(ossl_method_store_cache_set(store, i, buf, v + i,
+                || !TEST_true(ossl_method_store_cache_set(store, &prov, i,
+                                                          buf, v + i,
                                                           &up_ref, &down_ref))
-                || !TEST_true(ossl_method_store_cache_set(store, i, "n=1234",
-                                                          "miss", &up_ref,
-                                                          &down_ref))) {
+                || !TEST_true(ossl_method_store_cache_set(store, &prov, i,
+                                                          "n=1234", "miss",
+                                                          &up_ref, &down_ref))) {
             TEST_note("iteration %d", i);
             goto err;
         }
     }
     for (i = 1; i <= max; i++) {
         BIO_snprintf(buf, sizeof(buf), "n=%d\n", i);
-        if (!ossl_method_store_cache_get(store, i, buf, &result)
+        if (!ossl_method_store_cache_get(store, NULL, i, buf, &result)
             || result != v + i)
             errors++;
     }
diff --git a/test/provider_pkey_test.c b/test/provider_pkey_test.c
new file mode 100644
index 0000000000..d360c0cf30
--- /dev/null
+++ b/test/provider_pkey_test.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <openssl/provider.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include "testutil.h"
+#include "fake_rsaprov.h"
+
+static OSSL_LIB_CTX *libctx = NULL;
+
+/* Fetch SIGNATURE method using a libctx and propq */
+static int fetch_sig(OSSL_LIB_CTX *ctx, const char *alg, const char *propq,
+                     OSSL_PROVIDER *expected_prov)
+{
+    OSSL_PROVIDER *prov;
+    EVP_SIGNATURE *sig = EVP_SIGNATURE_fetch(ctx, "RSA", propq);
+    int ret = 0;
+
+    if (!TEST_ptr(sig))
+        return 0;
+
+    if (!TEST_ptr(prov = EVP_SIGNATURE_get0_provider(sig)))
+        goto end;
+
+    if (!TEST_ptr_eq(prov, expected_prov)) {
+        TEST_info("Fetched provider: %s, Expected provider: %s",
+                  OSSL_PROVIDER_get0_name(prov),
+                  OSSL_PROVIDER_get0_name(expected_prov));
+        goto end;
+    }
+
+    ret = 1;
+end:
+    EVP_SIGNATURE_free(sig);
+    return ret;
+}
+
+
+static int test_pkey_sig(void)
+{
+    OSSL_PROVIDER *deflt = NULL;
+    OSSL_PROVIDER *fake_rsa = NULL;
+    int i, ret = 0;
+    EVP_PKEY *pkey = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+
+    if (!TEST_ptr(fake_rsa = fake_rsa_start(libctx)))
+        return 0;
+
+    if (!TEST_ptr(deflt = OSSL_PROVIDER_load(libctx, "default")))
+        goto end;
+
+    /* Do a direct fetch to see it works */
+    if (!TEST_true(fetch_sig(libctx, "RSA", "provider=fake-rsa", fake_rsa))
+        || !TEST_true(fetch_sig(libctx, "RSA", "?provider=fake-rsa", fake_rsa)))
+        goto end;
+
+    /* Construct a pkey using precise propq to use our provider */
+    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA",
+                                                   "provider=fake-rsa"))
+        || !TEST_true(EVP_PKEY_fromdata_init(ctx))
+        || !TEST_true(EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, NULL))
+        || !TEST_ptr(pkey))
+        goto end;
+
+    EVP_PKEY_CTX_free(ctx);
+    ctx = NULL;
+
+    /* try exercising signature_init ops a few times */
+    for (i = 0; i < 3; i++) {
+        size_t siglen;
+
+        /*
+         * Create a signing context for our pkey with optional propq.
+         * The sign init should pick both keymgmt and signature from
+         * fake-rsa as the key is not exportable.
+         */
+        if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pkey,
+                                                       "?provider=default")))
+            goto end;
+
+        /*
+         * If this picks the wrong signature without realizing it
+         * we can get a segfault or some internal error. At least watch
+         * whether fake-rsa sign_init is is exercised by calling sign.
+         */
+        if (!TEST_int_eq(EVP_PKEY_sign_init(ctx), 1))
+            goto end;
+
+        if (!TEST_int_eq(EVP_PKEY_sign(ctx, NULL, &siglen, NULL, 0), 1)
+            || !TEST_size_t_eq(siglen, 256))
+            goto end;
+
+        EVP_PKEY_CTX_free(ctx);
+        ctx = NULL;
+    }
+
+    ret = 1;
+
+end:
+    fake_rsa_finish(fake_rsa);
+    OSSL_PROVIDER_unload(deflt);
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(pkey);
+    return ret;
+}
+
+int setup_tests(void)
+{
+    libctx = OSSL_LIB_CTX_new();
+    if (libctx == NULL)
+        return 0;
+
+    ADD_TEST(test_pkey_sig);
+
+    return 1;
+}
+
+void cleanup_tests(void)
+{
+    OSSL_LIB_CTX_free(libctx);
+}
diff --git a/test/recipes/04-test_provider_fallback.t b/test/recipes/04-test_provider_pkey.t
similarity index 81%
copy from test/recipes/04-test_provider_fallback.t
copy to test/recipes/04-test_provider_pkey.t
index 6322c25b0c..f593ac5725 100644
--- a/test/recipes/04-test_provider_fallback.t
+++ b/test/recipes/04-test_provider_pkey.t
@@ -1,5 +1,5 @@
 #! /usr/bin/env perl
-# Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
 #
 # Licensed under the Apache License 2.0 (the "License").  You may not use
 # this file except in compliance with the License.  You can obtain a copy
@@ -15,4 +15,4 @@ use OpenSSL::Test::Simple;
 # which defeats the purpose of this test.  The NUL device is good enough.
 $ENV{OPENSSL_CONF} = File::Spec->devnull();
 
-simple_test("test_provider_fallback", "provider_fallback_test");
+simple_test("test_provider_pkey", "provider_pkey_test");
diff --git a/util/other.syms b/util/other.syms
index 38aaacd6cf..1ebffd1d26 100644
--- a/util/other.syms
+++ b/util/other.syms
@@ -51,6 +51,7 @@ EVP_PKEY_METHOD                         datatype
 EVP_PKEY_ASN1_METHOD                    datatype
 EVP_RAND                                datatype
 EVP_RAND_CTX                            datatype
+EVP_SIGNATURE                           datatype
 GEN_SESSION_CB                          datatype
 OPENSSL_Applink                         external
 OSSL_LIB_CTX                            datatype


More information about the openssl-commits mailing list