EVP: Reverse the fetch logic in all pkey using functionality
authorRichard Levitte <levitte@openssl.org>
Fri, 1 Oct 2021 06:57:03 +0000 (08:57 +0200)
committerRichard Levitte <levitte@openssl.org>
Wed, 27 Oct 2021 10:41:12 +0000 (12:41 +0200)
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@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/16725)

crypto/evp/asymcipher.c
crypto/evp/exchange.c
crypto/evp/kem.c
crypto/evp/keymgmt_lib.c
crypto/evp/m_sigver.c
crypto/evp/pmeth_lib.c
crypto/evp/signature.c
include/crypto/evp.h

index 3150bfa94b2ba7becd3062c41196876b7e9e624c..41aea6b1d8a616485b0a4ef2546146feb477c506 100644 (file)
@@ -40,55 +40,53 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
         goto legacy;
 
     /*
-     * 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.
-     */
-    if (supported_ciph == NULL)
-        supported_ciph = ctx->keytype;
 
     /*
      * 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);
+    cipher = EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph,
+                                   ctx->propquery);
 
-    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().
-         */
-        EVP_ASYM_CIPHER_free(cipher);
+    if (cipher == NULL)
         goto legacy;
-    }
 
     /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
+     * 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
+        = evp_keymgmt_fetch_from_prov(EVP_ASYM_CIPHER_get0_provider(cipher),
+                                      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 (provkey == NULL)
+        goto legacy;
+
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -125,6 +123,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 +132,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 +160,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;
 }
 
index ca8a049a1bdf4de075a2c60fff95687dc4737e92..14e734dde865a8244d1178271196391fafa30d2a 100644 (file)
@@ -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"
@@ -207,73 +208,69 @@ 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.
-     */
-    if (supported_exch == NULL)
-        supported_exch = ctx->keytype;
 
     /*
      * 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 (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().
-         */
-        EVP_KEYEXCH_free(exchange);
+    if (exchange == NULL)
         goto legacy;
-    }
 
     /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
+     * 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 exch method, using the same property
+     * query as when fetching the exch 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
+        = evp_keymgmt_fetch_from_prov(EVP_KEYEXCH_get0_provider(exchange),
+                                      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 (provkey == NULL)
+        goto legacy;
+
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -287,10 +284,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 +312,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
 }
index cb904a6b2670d726f1103ed7d21332ffbe42d0af..0640e311445142f10dbba67fb789382269ac9528 100644 (file)
@@ -35,37 +35,49 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
     ctx->operation = operation;
 
     /*
-     * 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.
-     */
-    if (supported_kem == NULL)
-        supported_kem = ctx->keytype;
 
     kem = EVP_KEM_fetch(ctx->libctx, supported_kem, ctx->propquery);
-    if (kem == NULL
-        || (EVP_KEYMGMT_get0_provider(ctx->keymgmt) != EVP_KEM_get0_provider(kem))) {
+    if (kem == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
         ret = -2;
         goto err;
     }
 
+    /*
+     * 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
+        = evp_keymgmt_fetch_from_prov(EVP_KEM_get0_provider(kem),
+                                      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 (provkey == NULL) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
+    }
+
     ctx->op.encap.kem = kem;
     ctx->op.encap.algctx = kem->newctx(ossl_provider_ctx(kem->prov));
     if (ctx->op.encap.algctx == NULL) {
@@ -96,6 +108,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 +118,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;
 }
 
index 875c9a83de1f919e7b749e73b3bdc63293fb1b8e..2a73e9a2be9485ec0cfe380cb5dd98117b7ba781 100644 (file)
@@ -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;
+}
index 70669c3e6db5a0c5cf7a96a84be0f9e97052d0f8..55675c36e12d6e65b51703a2e4fa714cc7f81f78 100644 (file)
@@ -81,34 +81,21 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
         goto legacy;
 
     /*
-     * 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.
-     */
-    if (supported_sig == NULL)
-        supported_sig = locpctx->keytype;
 
     /*
      * Because we cleared out old ops, we shouldn't need to worry about
@@ -117,21 +104,32 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     signature = EVP_SIGNATURE_fetch(locpctx->libctx, supported_sig,
                                     locpctx->propquery);
 
-    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().
-         */
-        EVP_SIGNATURE_free(signature);
+    if (signature == NULL)
         goto legacy;
-    }
 
     /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
+     * 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
+        = evp_keymgmt_fetch_from_prov(EVP_SIGNATURE_get0_provider(signature),
+                                      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 (provkey == NULL) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
+    }
+
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -221,6 +219,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 +228,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 +300,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;
 }
 
index 1af16288236e0f1fde5780c0ef608f9125dfcf83..2b9c6c2351da2d2cb94b57e4939bf93288c8f1e7 100644 (file)
@@ -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 */
 
index b636889c3b55fa33ee628082a793eafa24310126..b893a2fc12818a7d79cc3d5b9570690a94f2eaba 100644 (file)
@@ -402,31 +402,21 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
         goto legacy;
 
     /*
-     * 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.
-     */
-    if (supported_sig == NULL)
-        supported_sig = ctx->keytype;
 
     /*
      * Because we cleared out old ops, we shouldn't need to worry about
@@ -435,21 +425,29 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
     signature =
         EVP_SIGNATURE_fetch(ctx->libctx, supported_sig, ctx->propquery);
 
-    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().
-         */
-        EVP_SIGNATURE_free(signature);
+    if (signature == NULL)
         goto legacy;
-    }
 
     /*
-     * If we don't have the full support we need with provided methods,
-     * let's go see if legacy does.
+     * 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
+        = evp_keymgmt_fetch_from_prov(EVP_SIGNATURE_get0_provider(signature),
+                                      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 (provkey == NULL)
+        goto legacy;
+
     ERR_pop_to_mark();
 
     /* No more legacy from here down to legacy: */
@@ -507,6 +505,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 +545,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;
 }
 
index 41ac80ed9dbeb499545fac224352b24f7db3e8ef..c5d3a930f749772aaab6e837d50326f8d2c5440a 100644 (file)
@@ -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