]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Allow keygen after dup of minimal PKEY ctx
authorViktor Dukhovni <openssl-users@dukhovni.org>
Tue, 13 May 2025 15:23:25 +0000 (01:23 +1000)
committerTomas Mraz <tomas@openssl.org>
Tue, 10 Jun 2025 17:44:04 +0000 (19:44 +0200)
It should be possible to repeatedly duplicate a PKEY CTX created via
EVP_PKEY_CTX_new_from_name() that has not yet been assigned an
"operation" (e.g. via EVP_PKEY_CTX_keygen_init()), and then perform
keygen_init() and keygen() on the duplicated copies.

When the operation is not yet set, all that's needed is to not try to
use the key if one isn't set yet.

Reviewed-by: Paul Dale <ppzgs1@gmail.com>
Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/27662)

(cherry picked from commit 2c74a8d1ef4e9c4b4468afefedb1f72425772a37)

crypto/evp/pmeth_lib.c
doc/man3/EVP_PKEY_CTX_new.pod
test/evp_pkey_provided_test.c

index 65e0a0c8206722de936c9fd8a0c489e31528fe9b..08c0d6a7b2b7d0152d5995069ea8a4ad2ff30d70 100644 (file)
@@ -590,6 +590,9 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx)
             EVP_KEYMGMT *tmp_keymgmt = pctx->keymgmt;
             void *provkey;
 
+            if (pctx->pkey == NULL)
+                return rctx;
+
             provkey = evp_pkey_export_to_provider(pctx->pkey, pctx->libctx,
                                                   &tmp_keymgmt, pctx->propquery);
             if (provkey == NULL)
index d7ac221f7c192928c678ad1815c4b20e8fa400a1..abf9b67d50dd5bdbb19983ad7eb3ca1d5772a2e6 100644 (file)
@@ -49,8 +49,11 @@ used when no B<EVP_PKEY> structure is associated with the operations,
 for example during parameter generation or key generation for some
 algorithms.
 
-EVP_PKEY_CTX_dup() duplicates the context I<ctx>. It is not supported for a
-keygen operation.
+EVP_PKEY_CTX_dup() duplicates the context I<ctx>.
+It is not supported for a keygen operation.
+It is however possible to duplicate a context freshly created via any of the
+above C<new> functions, provided L<EVP_PKEY_keygen_init(3)> has not yet been
+called on the source context, and then use the copy for key generation.
 
 EVP_PKEY_CTX_free() frees up the context I<ctx>.
 If I<ctx> is NULL, nothing is done.
index a51a4a3c073af421f8ebb06a905b546ebf95c23d..ba3d1b82b0aaa5199e854d56c4b95748c15201dd 100644 (file)
@@ -2180,6 +2180,53 @@ err:
     return ret;
 }
 
+static const char *name_dup_algs[] = {
+#ifndef OPENSSL_NO_ECX
+    "ED25519",
+#endif
+#ifndef OPENSSL_NO_ML_KEM
+    "ML-KEM-512",
+#endif
+#ifndef OPENSSL_NO_ML_DSA
+    "ML-DSA-44",
+#endif
+    NULL
+};
+
+static int test_name_dup(int idx)
+{
+    const char *alg = name_dup_algs[idx];
+    EVP_PKEY *key = NULL;
+    EVP_PKEY_CTX *factory = NULL, *ctx = NULL;
+    int i, ret = 0;
+
+    if (alg == NULL
+        || (factory = EVP_PKEY_CTX_new_from_name(NULL, alg, NULL)) == NULL)
+        return 1;
+    TEST_info("Testing fresh context dup for: %s", alg);
+
+    /* Run twice to check that *repeated* use works */
+    for (i = 0; i < 2; ++i) {
+        EVP_PKEY_CTX_free(ctx);
+        EVP_PKEY_free(key);
+        key = NULL;
+        if (!TEST_ptr(ctx = EVP_PKEY_CTX_dup(factory))
+            || !TEST_int_gt(EVP_PKEY_keygen_init(ctx), 0)
+            || !TEST_int_gt(EVP_PKEY_keygen(ctx, &key), 0)) {
+            ERR_print_errors(bio_err);
+            goto end;
+        }
+    }
+    ret = 1;
+
+ end:
+    EVP_PKEY_CTX_free(factory);
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(key);
+
+    return ret;
+}
+
 int setup_tests(void)
 {
     if (!test_skip_common_options()) {
@@ -2191,6 +2238,7 @@ int setup_tests(void)
         return 0;
 
     ADD_TEST(test_evp_pkey_ctx_dup_kdf);
+    ADD_ALL_TESTS(test_name_dup, OSSL_NELEM(name_dup_algs));
     ADD_TEST(test_evp_pkey_get_bn_param_large);
     ADD_TEST(test_fromdata_rsa);
     ADD_TEST(test_fromdata_rsa_derive_from_pq_sp800);