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);