Commit f9971f0936 for openssl.org

commit f9971f0936547abae6dec1e4d778ad333eb38ea6
Author: Simo Sorce <simo@redhat.com>
Date:   Mon Nov 3 16:53:41 2025 -0500

    Derive EC public key from private key if missing

    Update ossl_ec_key_fromdata to compute the public key if it is not provided in
    the input parameters but the private key is. This allows for the creation of a
    complete EC_KEY object from only private key data.

    Signed-off-by: Simo Sorce <simo@redhat.com>

    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    Reviewed-by: Neil Horman <nhorman@openssl.org>
    (Merged from https://github.com/openssl/openssl/pull/29054)

diff --git a/crypto/ec/ec_backend.c b/crypto/ec/ec_backend.c
index 2062711785..b100af0c60 100644
--- a/crypto/ec/ec_backend.c
+++ b/crypto/ec/ec_backend.c
@@ -483,6 +483,15 @@ int ossl_ec_key_fromdata(EC_KEY *ec, const OSSL_PARAM params[], int include_priv
         && !EC_KEY_set_public_key(ec, pub_point))
         goto err;

+    /* Fallback computation of public key if not provided */
+    if (priv_key != NULL && pub_point == NULL) {
+        if ((pub_point = EC_POINT_new(ecg)) == NULL
+            || !EC_KEY_set_public_key(ec, pub_point))
+            goto err;
+        if (!ossl_ec_key_simple_generate_public_key(ec))
+            goto err;
+    }
+
     ok = 1;

 err:
diff --git a/test/evp_extra_test.c b/test/evp_extra_test.c
index 532983a69f..176587bcd2 100644
--- a/test/evp_extra_test.c
+++ b/test/evp_extra_test.c
@@ -1477,13 +1477,11 @@ static int test_EC_priv_pub(void)
     bld = NULL;

     /*
-     * We indicate only parameters here, in spite of having built a key that
-     * has a private part, because the PEM_write_bio_PrivateKey_ex call is
-     * expected to fail because it does not support exporting a private EC
-     * key without a corresponding public key
+     * ossl_ec_key_fromdata() automatically generates the public key on import
+     * if one is not provided, so fail the test if a public key is not
+     * available.
      */
-    if (!test_selection(params_and_priv, OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)
-        || test_selection(params_and_priv, OSSL_KEYMGMT_SELECT_PUBLIC_KEY))
+    if (!test_selection(params_and_priv, OSSL_KEYMGMT_SELECT_KEYPAIR))
         goto err;

     /* Test !priv and pub */
diff --git a/test/evp_pkey_dhkem_test.c b/test/evp_pkey_dhkem_test.c
index 9833899360..b2903a9b95 100644
--- a/test/evp_pkey_dhkem_test.c
+++ b/test/evp_pkey_dhkem_test.c
@@ -394,8 +394,7 @@ err:
 }

 /*
- * ECX keys autogen the public key if a private key is loaded,
- * So this test passes for ECX, but fails for EC
+ * ECX and EC keys autogen the public key if a private key is loaded.
  */
 static int test_nopublic(int tstid)
 {
@@ -405,7 +404,6 @@ static int test_nopublic(int tstid)
     int encap = ((tstid & 1) == 0);
     int keytype = tstid >= TEST_KEM_ENCAP_DECAP;
     const TEST_ENCAPDATA *t = &ec_encapdata[keytype];
-    int expected = (keytype == TEST_KEYTYPE_X25519);

     TEST_note("%s %s", t->curve, encap ? "Encap" : "Decap");
     if (!TEST_ptr(priv = new_raw_private_key(t->curve, t->rpriv, t->rprivlen,
@@ -415,15 +413,12 @@ static int test_nopublic(int tstid)
         goto err;

     if (encap) {
-        if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, opparam), expected))
+        if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, opparam), 1))
             goto err;
     } else {
-        if (!TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), expected))
+        if (!TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), 1))
             goto err;
     }
-    if (expected == 0
-        && !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), PROV_R_NOT_A_PUBLIC_KEY))
-        goto err;
     ret = 1;
 err:
     EVP_PKEY_free(priv);
@@ -431,7 +426,10 @@ err:
     return ret;
 }

-/* Test that not setting the auth public key fails the auth encap/decap init */
+/*
+ * Test that not setting the auth public key does not fail the auth
+ * encap/decap init
+ */
 static int test_noauthpublic(int tstid)
 {
     int ret = 0;
@@ -440,28 +438,23 @@ static int test_noauthpublic(int tstid)
     int keytype = tstid >= TEST_KEM_ENCAP_DECAP;
     const TEST_ENCAPDATA *t = &ec_encapdata[keytype];
     EVP_PKEY_CTX *ctx = rctx[keytype];
-    int expected = (keytype == TEST_KEYTYPE_X25519);

     TEST_note("%s %s", t->curve, encap ? "Encap" : "Decap");
     if (!TEST_ptr(auth = new_raw_private_key(t->curve, t->rpriv,
-                      t->rprivlen, NULL, expected)))
+                      t->rprivlen, NULL, 1)))
         goto err;

     if (encap) {
         if (!TEST_int_eq(EVP_PKEY_auth_encapsulate_init(ctx, auth,
                              opparam),
-                expected))
+                1))
             goto err;
     } else {
         if (!TEST_int_eq(EVP_PKEY_auth_decapsulate_init(ctx, auth,
                              opparam),
-                expected))
+                1))
             goto err;
     }
-    if (expected == 0
-        && !TEST_int_eq(ERR_GET_REASON(ERR_get_error()),
-            PROV_R_NOT_A_PUBLIC_KEY))
-        goto err;
     ret = 1;
 err:
     EVP_PKEY_free(auth);