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
9 changes: 9 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@

* Fixed `wc_PKCS7_DecodeEnvelopedData()` and `wc_PKCS7_DecodeAuthEnvelopedData()` failing on a message addressed to more than one recipient; AuthEnvelopedData never supported it at all. A message carrying no recipient for the reader now reports `PKCS7_RECIP_E` rather than a parse error. Streaming an AuthEnvelopedData now buffers the whole RecipientInfo set, as the EnvelopedData decoder already did, so peak memory rises by the size of that set. by @Frauschi (PR 11350)

## Behavioral Changes
* **Behavioral change (`X509_get0_pubkey()` returns a borrowed key)**:
`wolfSSL_X509_get_pubkey()` now returns the public key cached on the
certificate with a new reference, and the new `wolfSSL_X509_get0_pubkey()`
(`X509_get0_pubkey()`, `X509_REQ_get0_pubkey()`) returns it without one, as
in OpenSSL. Code that freed the result of `X509_get0_pubkey()` to avoid a
leak must stop doing so, and the returned key is shared with the
certificate, so it must be treated as read only.

# wolfSSL Release 5.9.4 (Sep 25, 2026)

Release 5.9.4 has been developed according to wolfSSL's development and QA
Expand Down
86 changes: 86 additions & 0 deletions doc/dox_comments/header_files/ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5411,6 +5411,92 @@ long wolfSSL_BIO_get_mem_ptr(WOLFSSL_BIO *bio, WOLFSSL_BUF_MEM **m);
*/
char* wolfSSL_X509_NAME_oneline(WOLFSSL_X509_NAME* name, char* in, int sz);

/*!
\ingroup CertsKeys

\brief This function returns the public key of the certificate with a new
reference. The key is decoded once and cached on the certificate, so the
same object is returned on every call. Treat it as read only. Free the
reference with wolfSSL_EVP_PKEY_free().

\return pointer to the WOLFSSL_EVP_PKEY on success.
\return NULL if x509 is NULL, has no public key, or the key cannot be
decoded.

\param x509 pointer to a WOLFSSL_X509 structure.

_Example_
\code
WOLFSSL_X509* x509;
WOLFSSL_EVP_PKEY* key;
...
key = wolfSSL_X509_get_pubkey(x509);
if (key == NULL) {
// failed to get the public key
}
...
wolfSSL_EVP_PKEY_free(key);
\endcode

\sa wolfSSL_X509_get0_pubkey
\sa wolfSSL_EVP_PKEY_free
*/
WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509);

/*!
\ingroup CertsKeys

\brief This function returns the public key of the certificate without a
new reference. The key is owned by the certificate and must not be freed.
It stays valid until the certificate is freed or its public key is
changed. Treat it as read only.

\return pointer to the WOLFSSL_EVP_PKEY on success.
\return NULL if x509 is NULL, has no public key, or the key cannot be
decoded.

\param x509 pointer to a WOLFSSL_X509 structure.

_Example_
\code
WOLFSSL_X509* x509;
WOLFSSL_EVP_PKEY* key;
...
key = wolfSSL_X509_get0_pubkey(x509);
if (key == NULL) {
// failed to get the public key
}
// do not free key
\endcode

\sa wolfSSL_X509_get_pubkey
*/
WOLFSSL_EVP_PKEY* wolfSSL_X509_get0_pubkey(const WOLFSSL_X509* x509);

/*!
\ingroup CertsKeys

\brief This function returns the key held by a WOLFSSL_X509_PUBKEY without
a new reference. The key must not be freed.

\return pointer to the WOLFSSL_EVP_PKEY on success.
\return NULL if key is NULL or holds no key.

\param key pointer to a WOLFSSL_X509_PUBKEY structure.

_Example_
\code
WOLFSSL_X509* x509;
WOLFSSL_EVP_PKEY* pkey;
...
pkey = wolfSSL_X509_PUBKEY_get0(wolfSSL_X509_get_X509_PUBKEY(x509));
\endcode

\sa wolfSSL_X509_PUBKEY_get
\sa wolfSSL_X509_get_X509_PUBKEY
*/
WOLFSSL_EVP_PKEY* wolfSSL_X509_PUBKEY_get0(const WOLFSSL_X509_PUBKEY* key);

/*!
\ingroup CertsKeys

Expand Down
26 changes: 18 additions & 8 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -5798,19 +5798,27 @@ static void FreeX509Contents(WOLFSSL_X509* x509)
wolfSSL_ASN1_OBJECT_free(x509->algor.algorithm);
x509->algor.algorithm = NULL;
}
if (x509->subjAltNameSrc != NULL) {
XFREE(x509->subjAltNameSrc, x509->heap, DYNAMIC_TYPE_X509_EXT);
x509->subjAltNameSrc= NULL;
}
#endif /* OPENSSL_ALL */
#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
(defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \
defined(WOLFSSL_APACHE_HTTPD) || defined(WOLFSSL_HAPROXY) || \
defined(WOLFSSL_WPAS))
if (x509->key.algor) {
wolfSSL_X509_ALGOR_free(x509->key.algor);
x509->key.algor = NULL;
}
#endif
#ifdef OPENSSL_EXTRA_X509_SMALL
/* Public key cached by wolfSSL_X509_get_pubkey() and friends. */
if (x509->key.pkey) {
wolfSSL_EVP_PKEY_free(x509->key.pkey);
x509->key.pkey = NULL;
}
if (x509->subjAltNameSrc != NULL) {
XFREE(x509->subjAltNameSrc, x509->heap, DYNAMIC_TYPE_X509_EXT);
x509->subjAltNameSrc= NULL;
}
#endif /* OPENSSL_ALL */
#endif
#if defined(WOLFSSL_CERT_REQ) && defined(OPENSSL_ALL)
if (x509->reqAttributes) {
wolfSSL_sk_pop_free(x509->reqAttributes, NULL);
Expand Down Expand Up @@ -15563,6 +15571,11 @@ static void CopyDecodedSepFields(WOLFSSL_X509* x509, DecodedCert* dCert)
* error, matching the original. */
static int CopyDecodedPubKey(WOLFSSL_X509* x509, DecodedCert* dCert, int ret)
{
#ifdef OPENSSL_EXTRA_X509_SMALL
/* Drop the key decoded from a previous public key. */
wolfSSL_EVP_PKEY_free(x509->key.pkey);
x509->key.pkey = NULL;
#endif
if (dCert->publicKey != NULL && dCert->pubKeySize != 0) {
x509->pubKey.buffer = (byte*)XMALLOC(
dCert->pubKeySize, x509->heap, DYNAMIC_TYPE_PUBLIC_KEY);
Expand Down Expand Up @@ -15592,9 +15605,6 @@ static int CopyDecodedPubKey(WOLFSSL_X509* x509, DecodedCert* dCert, int ret)
}
}

wolfSSL_EVP_PKEY_free(x509->key.pkey);
x509->key.pkey = NULL;

switch (dCert->keyOID) {
#ifdef HAVE_ED25519
case ED25519k:
Expand Down
5 changes: 5 additions & 0 deletions src/pk_ec.c
Original file line number Diff line number Diff line change
Expand Up @@ -4394,6 +4394,11 @@ int SetECKeyExternal(WOLFSSL_EC_KEY* eckey)
WOLFSSL_MSG("SetECKeyExternal ec_point_external_set failed");
ret = WOLFSSL_FATAL_ERROR;
}
/* Both sides of the point match now, so readers of a shared key
* do not rebuild the internal one. */
if (ret == 1) {
eckey->pub_key->inSet = 1;
}
}

/* set the external privkey */
Expand Down
105 changes: 98 additions & 7 deletions src/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -6386,19 +6386,19 @@ int wolfSSL_X509_NAME_get_text_by_NID(WOLFSSL_X509_NAME* name,
return (textSz - 1); /* do not include null character in size */
}

/* Creates a new WOLFSSL_EVP_PKEY structure that has the public key from x509
/* Decodes the public key of x509 into a new WOLFSSL_EVP_PKEY.
*
* returns a pointer to the created WOLFSSL_EVP_PKEY on success and NULL on fail
*/
WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509)
static WOLFSSL_EVP_PKEY* X509DecodePubKey(WOLFSSL_X509* x509)
{
WOLFSSL_EVP_PKEY* key = NULL;
int ret = 0;

(void)ret;

WOLFSSL_ENTER("wolfSSL_X509_get_pubkey");
if (x509 != NULL) {
if (x509 != NULL && x509->pubKey.buffer != NULL &&
x509->pubKey.length > 0) {
key = wolfSSL_EVP_PKEY_new_ex(x509->heap);
if (key != NULL) {
if (x509->pubKeyOID == RSAk) {
Expand Down Expand Up @@ -6543,6 +6543,60 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509)
}
return key;
}

/* Returns the public key cached in x509, decoding it on first use.
* The key is owned by x509 and freed with it. */
static WOLFSSL_EVP_PKEY* X509CachedPubKey(WOLFSSL_X509* x509)
{
WOLFSSL_EVP_PKEY* key;

if (x509 == NULL)
return NULL;
key = x509->key.pkey;
if (key == NULL) {
key = X509DecodePubKey(x509);
if (key != NULL) {
x509->key.pubKeyOID = x509->pubKeyOID;
x509->key.pkey = key;
}
}
return key;
}

/* Returns the public key of x509 with a new reference.
*
* returns a pointer to the WOLFSSL_EVP_PKEY on success and NULL on fail.
* The caller frees it with wolfSSL_EVP_PKEY_free().
*/
WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509)
{
WOLFSSL_EVP_PKEY* key;

WOLFSSL_ENTER("wolfSSL_X509_get_pubkey");
key = X509CachedPubKey(x509);
if (key != NULL) {
int ret;
wolfSSL_RefInc(&key->ref, &ret);
if (ret != 0) {
WOLFSSL_MSG("Failed to lock pkey mutex");
key = NULL;
}
}
return key;
}

/* Returns the public key of x509 without a new reference.
*
* returns a pointer to the WOLFSSL_EVP_PKEY on success and NULL on fail.
* The key must not be freed. It is valid until x509 is freed or its public
* key is changed.
*/
WOLFSSL_EVP_PKEY* wolfSSL_X509_get0_pubkey(const WOLFSSL_X509* x509)
{
WOLFSSL_ENTER("wolfSSL_X509_get0_pubkey");
/* Only the lazily decoded key cache is written. */
return X509CachedPubKey((WOLFSSL_X509*)x509);
Comment thread
padelsbach marked this conversation as resolved.
}
#endif /* OPENSSL_EXTRA_X509_SMALL */

/* End of smaller subset of X509 compatibility functions. Avoid increasing the
Expand Down Expand Up @@ -11578,6 +11632,10 @@ WOLFSSL_X509_PUBKEY* wolfSSL_X509_get_X509_PUBKEY(const WOLFSSL_X509* x509)
return NULL;
}

/* OpenSSL also takes a const X509 and returns a mutable X509_PUBKEY.
* Only the lazily decoded key cache is written here. */
(void)X509CachedPubKey((WOLFSSL_X509*)x509);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This line casts away const. This can lead to undefined behavior, memory faults, or other bad outcomes. At least add a comment about why this must be done.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

x509->key needs to be computed here


return (WOLFSSL_X509_PUBKEY*)&x509->key;
}

Expand All @@ -11593,16 +11651,26 @@ int wolfSSL_X509_PUBKEY_get0_param(WOLFSSL_ASN1_OBJECT **ppkalg,
WOLFSSL_MSG("X509_PUBKEY struct not populated");
return WOLFSSL_FAILURE;
}
if ((pk || ppklen) && !pub->pkey) {
WOLFSSL_MSG("X509_PUBKEY has no decoded key");
return WOLFSSL_FAILURE;
}

if (!pub->algor) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI says there is now a race condition in this block since x509->key.pubKeyOID is now set in X509CachedPubKey. If this block fails in nid2obj for example, pub->algor is not fully built, but subsequent calls succeed. Not sure if this is valid

if (!(pub->algor = wolfSSL_X509_ALGOR_new())) {
/* Build the algorithm fully before storing it, so a failure
* never leaves a half built one behind. */
WOLFSSL_X509_ALGOR* algor = wolfSSL_X509_ALGOR_new();
if (algor == NULL) {
return WOLFSSL_FAILURE;
}
pub->algor->algorithm = wolfSSL_OBJ_nid2obj(pub->pubKeyOID);
if (pub->algor->algorithm == NULL) {
algor->algorithm = wolfSSL_OBJ_nid2obj(
oid2nid((word32)pub->pubKeyOID, oidKeyType));
if (algor->algorithm == NULL) {
WOLFSSL_MSG("Failed to create object from NID");
wolfSSL_X509_ALGOR_free(algor);
return WOLFSSL_FAILURE;
}
pub->algor = algor;
}

if (pa)
Expand Down Expand Up @@ -11633,6 +11701,15 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_PUBKEY_get(WOLFSSL_X509_PUBKEY* key)
return key->pkey;
}

/* Returns the pkey without a new reference. */
WOLFSSL_EVP_PKEY* wolfSSL_X509_PUBKEY_get0(const WOLFSSL_X509_PUBKEY* key)
{
WOLFSSL_ENTER("wolfSSL_X509_PUBKEY_get0");
if (key == NULL)
return NULL;
return key->pkey;
}

int wolfSSL_X509_PUBKEY_set(WOLFSSL_X509_PUBKEY **x, WOLFSSL_EVP_PKEY *key)
{
WOLFSSL_X509_PUBKEY *pk = NULL;
Expand Down Expand Up @@ -16846,6 +16923,7 @@ int wolfSSL_X509_set_pubkey(WOLFSSL_X509 *cert, WOLFSSL_EVP_PKEY *pkey)
return WOLFSSL_FAILURE;
}
cert->pubKeyOID = ECDSAk;
cert->pkCurveOID = ecc->dp->oidSum;
}
break;
#endif
Expand Down Expand Up @@ -16963,6 +17041,19 @@ int wolfSSL_X509_set_pubkey(WOLFSSL_X509 *cert, WOLFSSL_EVP_PKEY *pkey)
XFREE(cert->pubKey.buffer, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
cert->pubKey.buffer = p;
cert->pubKey.length = (unsigned int)derSz;
/* Drop what was decoded from the previous public key, unless the caller
* passed that very key. */
if (cert->key.pkey != pkey) {
wolfSSL_EVP_PKEY_free(cert->key.pkey);
cert->key.pkey = NULL;
}
cert->key.pubKeyOID = cert->pubKeyOID;
#if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \
defined(WOLFSSL_APACHE_HTTPD) || defined(WOLFSSL_HAPROXY) || \
defined(WOLFSSL_WPAS)
wolfSSL_X509_ALGOR_free(cert->key.algor);
cert->key.algor = NULL;
#endif

return WOLFSSL_SUCCESS;
}
Expand Down
Loading
Loading