Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/pk.c
Original file line number Diff line number Diff line change
Expand Up @@ -7373,6 +7373,22 @@ int pkcs8_encode(WOLFSSL_EVP_PKEY* pkey, byte* key, word32* keySz)
curveOid = NULL;
oidSz = 0;
}
#endif
#if defined(HAVE_ED25519)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] pkcs8_encode Ed25519 special case lacks NULL guard on pkey-pkey.ptr

The new Ed25519 branch copies the cached DER with XMEMCPY(key, pkey->pkey.ptr, pkey->pkey_sz) but only checks keySz == NULL. Unlike the DH special case it mirrors, it does not validate that pkey->pkey.ptr (or pkey->ed25519) is non-NULL / pkey_sz > 0. The DH branch above is reached only after confirming pkey->dh->priv_key || pub_key are set, which implies the buffer is populated; the Ed25519 branch has no such precondition. An Ed25519 WOLFSSL_EVP_PKEY whose pkey.ptr was never cached (e.g. a key populated only via the in-memory ed25519 object) would dereference NULL here.

Fix: Add a pkey->pkey.ptr == NULL || pkey->pkey_sz <= 0 guard before the copy, returning BAD_FUNC_ARG, to match the defensive style of the surrounding branches.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] pkcs8_encode Ed25519 branch assumes cached DER is a private PKCS#8 without validating

The new Ed25519 branch unconditionally returns pkey->pkey.ptr / pkey->pkey_sz as a PKCS#8 PrivateKeyInfo and never NULL-checks pkey->pkey.ptr. For an Ed25519 EVP_PKEY produced by wolfSSL_X509_get_pubkey(), pkey.ptr holds the raw 32-byte PUBLIC key (pkey_sz==32), not a PKCS#8 private key. Calling a PKCS#8 export on such a public-only key would silently copy 32 public bytes and report success rather than failing. The DH special case above shares the same loose assumption, but DH at least dereferences pkey->dh first; here there is no guard at all. This is a misuse path (encoding a public key as a private key), so impact is low, but a NULL/empty check on pkey->pkey.ptr would make the failure explicit.

Fix: Add a pkey->pkey.ptr != NULL / pkey->pkey_sz > 0 validation before the XMEMCPY so a public-only or uninitialized Ed25519 key returns a clean error instead of bogus output.

else if (pkey->type == WC_EVP_PKEY_ED25519) {
/* The cached DER is already a PKCS#8 PrivateKeyInfo (set when the
* key was generated or decoded), so return it as-is (same as the

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Non-ASCII em-dash in comment violates wolfSSL ASCII-only convention

The new comment contains a UTF-8 em-dash (U+2014) instead of an ASCII hyphen: "so return it as-is — same as the". wolfSSL source is expected to be 7-bit ASCII only; this is the sole non-ASCII byte introduced across all five changed files and will trip ASCII/whitespace lint checks. Use - (or --) instead.

Fix: Replace the em-dash with an ASCII hyphen to comply with the repository's ASCII-only source policy.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Non-ASCII em-dash in comment violates ASCII-only convention

The comment introduced by this PR contains a UTF-8 em-dash (U+2014) rather than ASCII. wolfSSL source must be 7-bit ASCII (no em/en dashes, smart quotes, etc.). This is the only non-ASCII byte introduced across the changed files (verified via a non-ASCII scan of all five files; the rest are clean).

Fix: Replace the em-dash with -- (or -) so the file remains pure ASCII.

* DH special case above). */
if (keySz == NULL)
return BAD_FUNC_ARG;

*keySz = (word32)pkey->pkey_sz;
if (key == NULL)
return LENGTH_ONLY_E;

XMEMCPY(key, pkey->pkey.ptr, (size_t)pkey->pkey_sz);
return pkey->pkey_sz;
}
Comment on lines +7378 to +7391
#endif
else {
ret = NOT_COMPILED_IN;
Expand Down
122 changes: 121 additions & 1 deletion src/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -6481,6 +6481,11 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509)
x509->pubKeyOID == ML_DSA_87k) {
key->type = WC_EVP_PKEY_DILITHIUM;
}
#endif
#if defined(HAVE_ED25519)
else if (x509->pubKeyOID == ED25519k) {
key->type = WC_EVP_PKEY_ED25519;
}
#endif
else {
key->type = WC_EVP_PKEY_EC;
Expand Down Expand Up @@ -6572,6 +6577,37 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509)
}
}
#endif /* NO_DSA */

/* decode Ed25519 key */
#if defined(HAVE_ED25519)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [High] Ed25519 public-key decode in wolfSSL_X509_get_pubkey not gated on HAVE_ED25519_KEY_IMPORT

The new Ed25519 decode block is guarded only by #if defined(HAVE_ED25519), but it calls wc_ed25519_import_public(), which is compiled only under #ifdef HAVE_ED25519_KEY_IMPORT (wolfcrypt/src/ed25519.c:1235-1469). settings.h defines HAVE_ED25519_KEY_IMPORT by default with HAVE_ED25519, but a user can disable it independently via NO_ED25519_KEY_IMPORT while keeping HAVE_ED25519. In that supported configuration this code still compiles and produces an undefined reference / link error. Note the sibling wolfSSL_X509_set_pubkey (added in this same PR) correctly guards with HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT, so the precise-guard precedent already exists in the diff.

Fix: Change the guard to #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) so the block (and the key->type = WC_EVP_PKEY_ED25519 assignment that feeds it) is only built when the import primitive is available.

if (key->type == WC_EVP_PKEY_ED25519) {
key->ed25519 = (ed25519_key*)XMALLOC(sizeof(ed25519_key),
x509->heap, DYNAMIC_TYPE_ED25519);
if (key->ed25519 == NULL) {
Comment on lines +6582 to +6586
wolfSSL_EVP_PKEY_free(key);
return NULL;
}
if (wc_ed25519_init_ex(key->ed25519, x509->heap,
INVALID_DEVID) != 0) {
XFREE(key->ed25519, x509->heap, DYNAMIC_TYPE_ED25519);
key->ed25519 = NULL;
wolfSSL_EVP_PKEY_free(key);
return NULL;
}
Comment on lines +6584 to +6596

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer to use wolfSSL_ED25519_new.

key->ownEd25519 = 1;

/* The X.509 public key buffer holds the raw Ed25519 key
* (CopyDecodedToX509 / StoreKey store the BIT STRING
* contents), so import it directly. */
if (wc_ed25519_import_public(
(const unsigned char*)key->pkey.ptr,
(word32)key->pkey_sz, key->ed25519) != 0) {
WOLFSSL_MSG("wc_ed25519_import_public failed");
wolfSSL_EVP_PKEY_free(key);
return NULL;
}
}
#endif /* HAVE_ED25519 */
}
}
return key;
Expand Down Expand Up @@ -12225,6 +12261,14 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
int hashType;
int sigType = WOLFSSL_FAILURE;

#if defined(HAVE_ED25519)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Use CTC_ED25519 instead of ED25519k for signature-type return value

wolfSSL_sigTypeFromPKEY returns a signature-type constant (e.g. CTC_SHA256wECDSA), but the new Ed25519 path returns the key-OID constant ED25519k. These happen to be numerically identical (both are 256 / OID 1.3.101.112, per oid_sum.h), so behavior is correct, but returning the CTC_* signature-type constant from a function whose contract is "sig type" reads more clearly and matches the other branches.

Fix: Return CTC_ED25519 (identical value, clearer intent) for consistency with the RSA/ECC CTC_* returns in this function.

/* Ed25519 carries its own hash, so md is unused (and may be NULL).
* Resolve it before touching md. */
if (pkey->type == WC_EVP_PKEY_ED25519) {
return ED25519k;
}
#endif

/* Convert key type and hash algorithm to a signature algorithm */
if (wolfSSL_EVP_get_hashinfo(md, &hashType, NULL)
== WC_NO_ERR_TRACE(WOLFSSL_FAILURE))
Expand Down Expand Up @@ -12337,6 +12381,9 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
#ifndef NO_DSA
DsaKey* dsa = NULL;
#endif
#if defined(HAVE_ED25519)
ed25519_key* ed25519 = NULL;
#endif
#if defined(HAVE_FALCON)
falcon_key* falcon = NULL;
#endif
Expand Down Expand Up @@ -12472,6 +12519,36 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
key = (void*)dsa;
}
#endif
#if defined(HAVE_ED25519)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [High] Ed25519 pubkey import in CertFromX509/wolfssl_x509_make_der not gated on HAVE_ED25519_KEY_IMPORT

The new Ed25519 public-key object-creation block is guarded only by #if defined(HAVE_ED25519) but calls wc_ed25519_import_public() (KEY_IMPORT-only, ed25519.c:1235-1469). With HAVE_ED25519 but NO_ED25519_KEY_IMPORT, this block is compiled and references an unbuilt symbol, breaking the link. The matching cleanup block at src/x509.c:12803-12808 uses only wc_ed25519_free/XFREE (always available) so it does not have the same problem, but the import site does.

Fix: Add && defined(HAVE_ED25519_KEY_IMPORT) to the #if guard around the ED25519k public-key decode block.

if (x509->pubKeyOID == ED25519k) {
ed25519 = (ed25519_key*)XMALLOC(sizeof(ed25519_key), NULL,
DYNAMIC_TYPE_ED25519);
if (ed25519 == NULL) {
Comment on lines +12522 to +12526
WOLFSSL_MSG("Failed to allocate memory for ed25519_key");
XFREE(cert, NULL, DYNAMIC_TYPE_CERT);
return WOLFSSL_FAILURE;
}

type = ED25519_TYPE;
ret = wc_ed25519_init(ed25519);
if (ret != 0) {
XFREE(ed25519, NULL, DYNAMIC_TYPE_ED25519);
XFREE(cert, NULL, DYNAMIC_TYPE_CERT);
return ret;
}
Comment on lines +12524 to +12538

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also use wolfSSL_ED25519_new

/* The X.509 public key buffer holds the raw Ed25519 key. */
ret = wc_ed25519_import_public(x509->pubKey.buffer,
x509->pubKey.length, ed25519);
if (ret != 0) {
WOLFSSL_ERROR_VERBOSE(ret);
wc_ed25519_free(ed25519);
XFREE(ed25519, NULL, DYNAMIC_TYPE_ED25519);
XFREE(cert, NULL, DYNAMIC_TYPE_CERT);
return ret;
}
key = (void*)ed25519;
}
#endif
#if defined(HAVE_FALCON)
if ((x509->pubKeyOID == FALCON_LEVEL1k) ||
(x509->pubKeyOID == FALCON_LEVEL5k)) {
Expand Down Expand Up @@ -12723,6 +12800,12 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
XFREE(ecc, NULL, DYNAMIC_TYPE_ECC);
}
#endif
#if defined(HAVE_ED25519)
if (x509->pubKeyOID == ED25519k) {
wc_ed25519_free(ed25519);
XFREE(ed25519, NULL, DYNAMIC_TYPE_ED25519);
}
#endif
#ifndef NO_DSA
if (x509->pubKeyOID == DSAk) {
wc_FreeDsaKey(dsa);
Expand Down Expand Up @@ -12807,6 +12890,12 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
key = pkey->ecc->internal;
}
#endif
#if defined(HAVE_ED25519)
if (pkey->type == WC_EVP_PKEY_ED25519) {
type = ED25519_TYPE;
key = pkey->ed25519;
}
#endif

/* Sign the certificate (request) body. */
ret = wc_InitRng(&rng);
Expand Down Expand Up @@ -12895,7 +12984,14 @@ int wolfSSL_X509_sign(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey,

WOLFSSL_ENTER("wolfSSL_X509_sign");

if (x509 == NULL || pkey == NULL || md == NULL) {
/* Ed25519 signs with a NULL digest (the key has a built-in hash); every
* other key type requires an explicit md. */
if (x509 == NULL || pkey == NULL ||
(md == NULL
#if defined(HAVE_ED25519)
&& pkey->type != WC_EVP_PKEY_ED25519
#endif
)) {
Comment on lines -12898 to +12994

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's separate this check out into a new block after this one. Something like this. I suspect other algos in the future will have similar edge cases.

if (md == null) {
  if (pkey->type != WC_EVP_PKEY_ED25519)
    error;
}

ret = WOLFSSL_FAILURE;
goto out;
}
Expand Down Expand Up @@ -16408,6 +16504,30 @@ int wolfSSL_X509_set_pubkey(WOLFSSL_X509 *cert, WOLFSSL_EVP_PKEY *pkey)
cert->pubKeyOID = ECDSAk;
}
break;
#endif
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
case WC_EVP_PKEY_ED25519:
{
word32 rawLen = ED25519_PUB_KEY_SIZE;

if (pkey->ed25519 == NULL)
return WOLFSSL_FAILURE;

/* Store the RAW public key: wolfSSL keeps an X.509 Ed25519
* public key as the bare key bytes (see StoreKey /
* CopyDecodedToX509), not a SubjectPublicKeyInfo. */
p = (byte*)XMALLOC(rawLen, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
if (p == NULL)
return WOLFSSL_FAILURE;

if (wc_ed25519_export_public(pkey->ed25519, p, &rawLen) != 0) {
XFREE(p, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
return WOLFSSL_FAILURE;
}
derSz = (int)rawLen;
cert->pubKeyOID = ED25519k;
}
break;
#endif
default:
return WOLFSSL_FAILURE;
Expand Down
112 changes: 112 additions & 0 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -10074,6 +10074,117 @@ static int test_wolfSSL_PKCS8(void)
return EXPECT_RESULT();
}

/* Exercise Ed25519 through the OpenSSL-compatibility EVP/X509 surface the
* way an application does: generate a key, serialise the public
* (SubjectPublicKeyInfo) and private (PKCS#8) parts, build and sign an
* in-memory self-signed certificate with a NULL digest, read the public key
* back out, and load the key + cert into an SSL_CTX. */
static int test_wolfSSL_EVP_PKEY_ED25519_openssl(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && defined(HAVE_ED25519) && \
defined(HAVE_ED25519_KEY_EXPORT) && defined(HAVE_ED25519_KEY_IMPORT) && \
defined(WOLFSSL_CERT_GEN) && !defined(NO_CERTS)
Comment on lines +10085 to +10087
WOLFSSL_EVP_PKEY_CTX* ctx = NULL;
WOLFSSL_EVP_PKEY* pkey = NULL;
WOLFSSL_EVP_PKEY* certPub = NULL;
WOLFSSL_X509* x509 = NULL;
WOLFSSL_X509_NAME* name = NULL;
WOLFSSL_ASN1_TIME* notBefore = NULL;
WOLFSSL_ASN1_TIME* notAfter = NULL;
unsigned char* spki = NULL;
unsigned char* spki2 = NULL;
int spkiSz = 0;
int spki2Sz = 0;
time_t t = 0;

/* (1) Generate an Ed25519 key purely through the EVP API. */
ExpectNotNull(ctx = wolfSSL_EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL));
ExpectIntEQ(wolfSSL_EVP_PKEY_keygen_init(ctx), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_EVP_PKEY_keygen(ctx, &pkey), WOLFSSL_SUCCESS);
ExpectNotNull(pkey);

/* (2) Encode the public key as SubjectPublicKeyInfo. */
ExpectIntGT((spkiSz = wolfSSL_i2d_PUBKEY(pkey, &spki)), 0);

/* (3) Encode the private key as PKCS#8 PrivateKeyInfo and (7) decode it
* back. These compat helpers (EVP_PKEY2PKCS8, i2d_PKCS8_PKEY,
* d2i_AutoPrivateKey) are only built with OPENSSL_ALL. */
#if defined(OPENSSL_ALL) && !defined(NO_AES)
{
WOLFSSL_PKCS8_PRIV_KEY_INFO* p8 = NULL;
WOLFSSL_EVP_PKEY* decPriv = NULL;
unsigned char* p8der = NULL;
const unsigned char* tmp = NULL;
int p8Sz = 0;

ExpectNotNull(p8 = wolfSSL_EVP_PKEY2PKCS8(pkey));
ExpectIntGT((p8Sz = wolfSSL_i2d_PKCS8_PKEY(p8, &p8der)), 0);
tmp = p8der;
ExpectNotNull(decPriv = wolfSSL_d2i_AutoPrivateKey(NULL, &tmp, p8Sz));

XFREE(p8der, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
wolfSSL_EVP_PKEY_free(decPriv);
wolfSSL_EVP_PKEY_free((WOLFSSL_EVP_PKEY*)p8);
}
#endif

/* (4)(5) Build an in-memory self-signed cert; Ed25519 signs with a NULL
* digest (it carries its own hash). */
ExpectNotNull(x509 = wolfSSL_X509_new());
ExpectIntEQ(wolfSSL_X509_set_version(x509, 2), WOLFSSL_SUCCESS);
ExpectNotNull(name = wolfSSL_X509_NAME_new());
ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
(const byte*)"ed25519 test", -1, -1, 0), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_X509_set_pubkey(x509, pkey), WOLFSSL_SUCCESS);
t = XTIME(NULL);
ExpectNotNull(notBefore = wolfSSL_ASN1_TIME_adj(NULL, t, 0, 0));
ExpectNotNull(notAfter = wolfSSL_ASN1_TIME_adj(NULL, t, 365, 0));
ExpectTrue(wolfSSL_X509_set_notBefore(x509, notBefore));
ExpectTrue(wolfSSL_X509_set_notAfter(x509, notAfter));
ExpectIntGT(wolfSSL_X509_sign(x509, pkey, NULL), 0);

/* (8) The certificate's public key round-trips to the same SPKI. */
ExpectNotNull(certPub = wolfSSL_X509_get_pubkey(x509));
ExpectIntGT((spki2Sz = wolfSSL_i2d_PUBKEY(certPub, &spki2)), 0);
ExpectIntEQ(spki2Sz, spkiSz);
if ((spki != NULL) && (spki2 != NULL) && (spkiSz == spki2Sz)) {
ExpectIntEQ(XMEMCMP(spki, spki2, (size_t)spkiSz), 0);
}

/* (6) Load the generated key and cert into an SSL_CTX. */
#if !defined(NO_TLS) && \
(!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER))
{
WOLFSSL_CTX* sslCtx = NULL;
#ifndef NO_WOLFSSL_SERVER
ExpectNotNull(sslCtx = wolfSSL_CTX_new(wolfSSLv23_server_method()));
#else
ExpectNotNull(sslCtx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
#endif
ExpectIntEQ(wolfSSL_CTX_use_certificate(sslCtx, x509),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_use_PrivateKey(sslCtx, pkey),
WOLFSSL_SUCCESS);
wolfSSL_CTX_free(sslCtx);
}
#endif

XFREE(spki, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
XFREE(spki2, HEAP_HINT, DYNAMIC_TYPE_OPENSSL);
Comment on lines +10175 to +10176
wolfSSL_ASN1_TIME_free(notBefore);
wolfSSL_ASN1_TIME_free(notAfter);
wolfSSL_X509_NAME_free(name);
wolfSSL_X509_free(x509);
wolfSSL_EVP_PKEY_free(certPub);
wolfSSL_EVP_PKEY_free(pkey);
wolfSSL_EVP_PKEY_CTX_free(ctx);
#endif
return EXPECT_RESULT();
}

static int test_wolfSSL_PKCS8_ED25519(void)
{
EXPECT_DECLS;
Expand Down Expand Up @@ -35068,6 +35179,7 @@ TEST_CASE testCases[] = {
/* PKCS8 testing */
TEST_DECL(test_wolfSSL_no_password_cb),
TEST_DECL(test_wolfSSL_PKCS8),
TEST_DECL(test_wolfSSL_EVP_PKEY_ED25519_openssl),
TEST_DECL(test_wolfSSL_PKCS8_ED25519),
TEST_DECL(test_wolfSSL_PKCS8_ED448),

Expand Down
49 changes: 49 additions & 0 deletions wolfcrypt/src/evp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3820,6 +3820,9 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx,
#endif
#ifdef HAVE_CURVE448
ctx->pkey->type != WC_EVP_PKEY_X448 &&
#endif
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)
ctx->pkey->type != WC_EVP_PKEY_ED25519 &&
#endif
Comment on lines +3824 to 3826
ctx->pkey->type != WC_EVP_PKEY_DH)) {
WOLFSSL_MSG("Key not set or key type not supported");
Expand Down Expand Up @@ -3932,6 +3935,52 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx,
ret = WOLFSSL_SUCCESS;
}
break;
#endif
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Ed25519 keygen path uses KEY_EXPORT guard but calls MAKE_KEY-only wc_ed25519_make_key

The Ed25519 keygen case (and the corresponding type allowlist at evp.c:3824-3826) is guarded by HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT, but the body calls wc_ed25519_make_key(), which exists only under HAVE_ED25519_MAKE_KEY (ed25519.c:266-409). A build with key export enabled but NO_ED25519_MAKE_KEY (hence NO_ED25519_SIGN) would compile the case yet fail to link. Less common than the KEY_IMPORT cases, but the guard does not match the primitives used.

Fix: Add && defined(HAVE_ED25519_MAKE_KEY) to the Ed25519 keygen guard (both the case label and the allowlist check).

case WC_EVP_PKEY_ED25519:
Comment on lines +3939 to +3940
if (pkey->ed25519 == NULL) {
pkey->ed25519 = (ed25519_key*)XMALLOC(sizeof(ed25519_key),
pkey->heap, DYNAMIC_TYPE_ED25519);
if (pkey->ed25519 == NULL) {
ret = MEMORY_E;
break;
}
if (wc_ed25519_init_ex(pkey->ed25519, pkey->heap,
INVALID_DEVID) != 0) {
XFREE(pkey->ed25519, pkey->heap, DYNAMIC_TYPE_ED25519);
pkey->ed25519 = NULL;
break;
}
Comment on lines +3942 to +3953

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wolfSSL_ED25519_new

pkey->ownEd25519 = 1;
}
/* Reuse the RNG already initialized on the EVP_PKEY. */
if (wc_ed25519_make_key(&pkey->rng, ED25519_KEY_SIZE,
pkey->ed25519) == 0) {
/* Cache the PKCS#8 PrivateKeyInfo DER so the EVP/SSL paths
* (use_PrivateKey, EVP_PKEY2PKCS8) can load and serialize the
* key, mirroring the state the decode path produces. */
Comment on lines +3957 to +3961
int edDerSz = wc_Ed25519PrivateKeyToDer(pkey->ed25519, NULL, 0);
if (edDerSz > 0) {
byte* edDer = (byte*)XMALLOC((size_t)edDerSz, pkey->heap,
DYNAMIC_TYPE_OPENSSL);
if (edDer != NULL) {
if (wc_Ed25519PrivateKeyToDer(pkey->ed25519, edDer,
(word32)edDerSz) == edDerSz) {
if (pkey->pkey.ptr != NULL) {
XFREE(pkey->pkey.ptr, pkey->heap,
DYNAMIC_TYPE_OPENSSL);
}
Comment on lines +3969 to +3972

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (pkey->pkey.ptr != NULL) {
XFREE(pkey->pkey.ptr, pkey->heap,
DYNAMIC_TYPE_OPENSSL);
}
XFREE(pkey->pkey.ptr, pkey->heap,
DYNAMIC_TYPE_OPENSSL);

pkey->pkey.ptr = (char*)edDer;
pkey->pkey_sz = edDerSz;
ret = WOLFSSL_SUCCESS;
}
else {
XFREE(edDer, pkey->heap, DYNAMIC_TYPE_OPENSSL);
}
}
}
}
break;
#endif
default:
break;
Expand Down
Loading