EVP: Allow a fallback for operations that work with an EVP_PKEY
authorRichard Levitte <levitte@openssl.org>
Fri, 1 Oct 2021 12:05:02 +0000 (14:05 +0200)
committerRichard Levitte <levitte@openssl.org>
Wed, 27 Oct 2021 10:41:13 +0000 (12:41 +0200)
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@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/m_sigver.c
crypto/evp/signature.c
doc/build.info
doc/man3/EVP_SIGNATURE.pod [moved from doc/man3/EVP_SIGNATURE_free.pod with 98% similarity]
doc/man7/crypto.pod
util/other.syms

index e2912829e2e23e73f0842a7094cb99120596b3e3..f158b815cff68a0dbd1cf0861ccc73c07b150293 100644 (file)
@@ -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);
@@ -58,34 +60,76 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation,
     }
 
     /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if cipher is already there.
+     * 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.
      */
-    cipher = EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph,
-                                   ctx->propquery);
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree;
 
-    if (cipher == NULL)
-        goto legacy;
+        /*
+         * 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;
 
-    /*
-     * 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)
+        /*
+         * 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;
+    }
 
     ERR_pop_to_mark();
 
index 35da4743a40d97cca3a09e99ab98d581d2c547e4..d12dcee9479da5e70316abe33f21180d4008c470 100644 (file)
@@ -203,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);
@@ -255,32 +257,76 @@ int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
 
 
     /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if exchange is already there.
+     * 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.
      */
-    exchange = EVP_KEYEXCH_fetch(ctx->libctx, supported_exch, ctx->propquery);
-    if (exchange == NULL)
-        goto legacy;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree;
 
-    /*
-     * 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)
+        /*
+         * 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;
+
+        /*
+         * 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;
+    }
 
     ERR_pop_to_mark();
 
index a3537cc4524113005431e22afa56433669a2791c..d5bdc6290a3ab97c4f5fff7fc5c757bd01065681 100644 (file)
@@ -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);
@@ -49,31 +51,80 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
         goto err;
     }
 
-    kem = EVP_KEM_fetch(ctx->libctx, supported_kem, ctx->propquery);
-    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)
+     * 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.
      */
-    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);
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree;
+
+        /*
+         * 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;
     }
index 55675c36e12d6e65b51703a2e4fa714cc7f81f78..eeb1a9adfa91b649cd4716760ba8664a6c4b1834 100644 (file)
@@ -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)) {
@@ -98,33 +99,75 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     }
 
     /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if signature is already there.
+     * 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.
      */
-    signature = EVP_SIGNATURE_fetch(locpctx->libctx, supported_sig,
-                                    locpctx->propquery);
-
-    if (signature == NULL)
-        goto legacy;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
+
+        /*
+         * 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;
+
+        /*
+         * 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);
+    }
 
-    /*
-     * 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) {
+        EVP_SIGNATURE_free(signature);
         ERR_clear_last_mark();
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
index f5c3bbcb1e5656b95790c1a4c989bdcef908a971..b33fe0d95253e2eb65f5d7ad8099a61d00e941e6 100644 (file)
@@ -397,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);
@@ -430,34 +432,77 @@ static int evp_pkey_signature_init(EVP_PKEY_CTX *ctx, int operation,
     }
 
     /*
-     * Because we cleared out old ops, we shouldn't need to worry about
-     * checking if signature is already there.
+     * 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.
      */
-    signature =
-        EVP_SIGNATURE_fetch(ctx->libctx, supported_sig, ctx->propquery);
+    for (iter = 1; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree;
 
-    if (signature == NULL)
-        goto legacy;
+        /*
+         * 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;
 
-    /*
-     * 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)
+        /*
+         * 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;
+    }
 
     ERR_pop_to_mark();
 
index 5f446e38681c9b528e5759fc646e6c912d36cebb..97e6bd3b51932edb652b34cc21a5752bc0e0a9b0 100644 (file)
@@ -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 \
similarity index 98%
rename from doc/man3/EVP_SIGNATURE_free.pod
rename to doc/man3/EVP_SIGNATURE.pod
index 4642f40efc146a54b757a466f1cd1ccdb347eb76..9fb389e7aeb0ec6749c25368c7baf610ca6ea2cb 100644 (file)
@@ -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);
index 16a07fc0ac7dbfcff2b3a791c6914f920f9984d4..9aa667118d33cb44c525f1b33d3434d28c7b39d0 100644 (file)
@@ -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
index 38aaacd6cfcaf1d7280d40b93f91a559528679ca..1ebffd1d2621ae16866c70e1ddd406b6d6446e74 100644 (file)
@@ -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