Skip to content

Commit 2ab737b

Browse files
committed
evp: fix EVP_PKEY2PKCS8 returning NULL for private-key-only EC keys
When an EC_KEY is created via EC_KEY_new + EC_KEY_set_group + EC_KEY_set_private_key (no public point set), SetECKeyInternal incorrectly marks the internal ecc_key as ECC_PRIVATEKEY (instead of ECC_PRIVATEKEY_ONLY) because pub_key is always non-NULL — EC_KEY_new always allocates it as an empty, zero-initialised EC_POINT. ECC_populate_EVP_PKEY only calls wc_ecc_make_pub for ECC_PRIVATEKEY_ONLY keys, so the zero public-key point was serialised into the DER stored in pkey->pkey.ptr. After commit 929dd99 made wc_ecc_import_x963_ex always pass untrusted=1, the re-decode inside wolfSSL_EVP_PKEY2PKCS8 → wolfSSL_d2i_PrivateKey_EVP correctly rejected that zero point with an on-curve failure, causing EVP_PKEY2PKCS8 to return NULL. Fix: in ECC_populate_EVP_PKEY, also call wc_ecc_make_pub when the key type is ECC_PRIVATEKEY but pubkey.x is zero (meaning the public key was never actually populated). This reconstructs the public key from the private scalar so that the encoded DER contains a valid on-curve point. Reproduces the failure seen in SoftHSM's ECDHTests::testPKCS8 and ECDSATests::testPKCS8 which use exactly this pattern.
1 parent b7c770d commit 2ab737b

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

tests/api.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16265,6 +16265,78 @@ static int test_wolfSSL_BUF(void)
1626516265
return EXPECT_RESULT();
1626616266
}
1626716267

16268+
/* Regression: EVP_PKEY_keygen for EC must populate pkey->pkey.ptr so that
16269+
* EVP_PKEY2PKCS8 can encode the key (reported via SoftHSM ECDHTests::testPKCS8
16270+
* and ECDSATests::testPKCS8 after commit that enabled on-curve validation). */
16271+
static int test_wolfSSL_EVP_PKEY2PKCS8_keygen(void)
16272+
{
16273+
EXPECT_DECLS;
16274+
#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC) && \
16275+
(defined(OPENSSL_ALL) || defined(WOLFSSL_WPAS_SMALL))
16276+
EVP_PKEY_CTX *ctx = NULL;
16277+
EVP_PKEY *pkey = NULL;
16278+
PKCS8_PRIV_KEY_INFO *p8 = NULL;
16279+
16280+
ExpectNotNull(ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL));
16281+
ExpectIntGT(EVP_PKEY_keygen_init(ctx), 0);
16282+
ExpectIntGT(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx,
16283+
NID_X9_62_prime256v1), 0);
16284+
ExpectIntGT(EVP_PKEY_keygen(ctx, &pkey), 0);
16285+
/* pkey->pkey.ptr must be populated so PKEY2PKCS8 can encode the key */
16286+
if (pkey != NULL) {
16287+
ExpectNotNull(pkey->pkey.ptr);
16288+
}
16289+
ExpectNotNull(p8 = EVP_PKEY2PKCS8(pkey));
16290+
16291+
PKCS8_PRIV_KEY_INFO_free(p8);
16292+
EVP_PKEY_free(pkey);
16293+
EVP_PKEY_CTX_free(ctx);
16294+
#endif
16295+
return EXPECT_RESULT();
16296+
}
16297+
16298+
/* Regression test for SoftHSM pattern: EC_KEY created with only group and
16299+
* private scalar set (no public key point), then wrapped in EVP_PKEY and
16300+
* passed to EVP_PKEY2PKCS8. Before the fix, ECC_populate_EVP_PKEY would
16301+
* serialise the zero public-key point that SetECKeyInternal copied from the
16302+
* uninitialised pub_key EC_POINT, and the subsequent re-decode in
16303+
* wolfSSL_EVP_PKEY2PKCS8 would fail the on-curve check. */
16304+
static int test_wolfSSL_EVP_PKEY2PKCS8_priv_only(void)
16305+
{
16306+
EXPECT_DECLS;
16307+
#if defined(OPENSSL_EXTRA) && defined(HAVE_ECC) && \
16308+
(defined(OPENSSL_ALL) || defined(WOLFSSL_WPAS_SMALL))
16309+
EC_KEY *full = NULL;
16310+
EC_KEY *priv_only = NULL;
16311+
EVP_PKEY *pkey = NULL;
16312+
PKCS8_PRIV_KEY_INFO *p8 = NULL;
16313+
const BIGNUM *priv_bn = NULL;
16314+
const EC_GROUP *grp = NULL;
16315+
16316+
/* Generate a full keypair to extract the private scalar from. */
16317+
ExpectNotNull(full = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
16318+
ExpectIntEQ(EC_KEY_generate_key(full), WOLFSSL_SUCCESS);
16319+
16320+
/* Build a key with only group + private scalar - no public point. */
16321+
ExpectNotNull(grp = EC_KEY_get0_group(full));
16322+
ExpectNotNull(priv_bn = EC_KEY_get0_private_key(full));
16323+
ExpectNotNull(priv_only = EC_KEY_new());
16324+
ExpectIntEQ(EC_KEY_set_group(priv_only, (EC_GROUP*)grp), WOLFSSL_SUCCESS);
16325+
ExpectIntEQ(EC_KEY_set_private_key(priv_only, priv_bn), WOLFSSL_SUCCESS);
16326+
16327+
/* Wrap in EVP_PKEY and call EVP_PKEY2PKCS8 - must not return NULL. */
16328+
ExpectNotNull(pkey = EVP_PKEY_new());
16329+
ExpectIntEQ(EVP_PKEY_set1_EC_KEY(pkey, priv_only), WOLFSSL_SUCCESS);
16330+
ExpectNotNull(p8 = EVP_PKEY2PKCS8(pkey));
16331+
16332+
PKCS8_PRIV_KEY_INFO_free(p8);
16333+
EVP_PKEY_free(pkey);
16334+
EC_KEY_free(priv_only);
16335+
EC_KEY_free(full);
16336+
#endif
16337+
return EXPECT_RESULT();
16338+
}
16339+
1626816340
static int test_wolfSSL_PKCS8_Compat(void)
1626916341
{
1627016342
EXPECT_DECLS;
@@ -34923,6 +34995,8 @@ TEST_CASE testCases[] = {
3492334995
TEST_DECL(test_wolfSSL_PKCS5),
3492434996

3492534997
/* OpenSSL PKCS8 API test */
34998+
TEST_DECL(test_wolfSSL_EVP_PKEY2PKCS8_keygen),
34999+
TEST_DECL(test_wolfSSL_EVP_PKEY2PKCS8_priv_only),
3492635000
TEST_DECL(test_wolfSSL_PKCS8_Compat),
3492735001
TEST_DECL(test_wolfSSL_PKCS8_d2i),
3492835002

wolfcrypt/src/evp.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3715,6 +3715,10 @@ int wolfSSL_EVP_PKEY_keygen_init(WOLFSSL_EVP_PKEY_CTX *ctx)
37153715
return WOLFSSL_SUCCESS;
37163716
}
37173717

3718+
#ifdef HAVE_ECC
3719+
static int ECC_populate_EVP_PKEY(WOLFSSL_EVP_PKEY* pkey, WOLFSSL_EC_KEY *key);
3720+
#endif
3721+
37183722
int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx,
37193723
WOLFSSL_EVP_PKEY **ppkey)
37203724
{
@@ -3769,6 +3773,8 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx,
37693773
ret = wolfSSL_EC_KEY_generate_key(pkey->ecc);
37703774
if (ret == WOLFSSL_SUCCESS) {
37713775
pkey->ownEcc = 1;
3776+
if (ECC_populate_EVP_PKEY(pkey, pkey->ecc) != WOLFSSL_SUCCESS)
3777+
ret = WOLFSSL_FAILURE;
37723778
}
37733779
}
37743780
break;
@@ -9521,7 +9527,15 @@ static int ECC_populate_EVP_PKEY(WOLFSSL_EVP_PKEY* pkey, WOLFSSL_EC_KEY *key)
95219527
else
95229528
#endif /* HAVE_PKCS8 */
95239529
{
9524-
if (ecc->type == ECC_PRIVATEKEY_ONLY) {
9530+
if (ecc->type == ECC_PRIVATEKEY_ONLY ||
9531+
(ecc->type == ECC_PRIVATEKEY &&
9532+
mp_iszero(ecc->pubkey.x))) {
9533+
/* Reconstruct public key from private scalar. This covers
9534+
* both ECC_PRIVATEKEY_ONLY keys and ECC_PRIVATEKEY keys whose
9535+
* public-key point was never populated (e.g. when only
9536+
* EC_KEY_set_private_key was called, SetECKeyInternal copies
9537+
* the zero-initialized pub_key point and marks the type as
9538+
* ECC_PRIVATEKEY, leaving pubkey.x == 0). */
95259539
if (wc_ecc_make_pub(ecc, NULL) != MP_OKAY) {
95269540
return WOLFSSL_FAILURE;
95279541
}

0 commit comments

Comments
 (0)