From 58a27848a800aadca91f606dc4fe5234b9971011 Mon Sep 17 00:00:00 2001 From: Colton Willey Date: Mon, 13 Apr 2026 20:29:50 -0700 Subject: [PATCH 1/3] Fix NULL derefs, buffer overflow, and i2d contract in EVP/OCSP/X509 Harden OpenSSL compatibility layer against NULL pointers, negative lengths, and buffer overflows across EVP, OCSP, and X509 APIs. Fix DSA SignFinal write-before-check overflow, add missing i2d_OCSP_RESPONSE allocation path, and fix unaligned keyUsage access. --- src/ocsp.c | 39 +++++++++++++++++++++++++------ src/ssl.c | 8 ++++++- src/x509.c | 12 ++++++++-- tests/api.c | 2 +- wolfcrypt/src/evp.c | 50 ++++++++++++++++++++++++++++++++-------- wolfcrypt/src/pwdbased.c | 3 +++ 6 files changed, 94 insertions(+), 20 deletions(-) diff --git a/src/ocsp.c b/src/ocsp.c index 2fff7ced503..9de23cb9ea4 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -749,7 +749,9 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, single = bs->single; while (single != NULL) { - if ((XMEMCMP(single->status->serial, id->status->serial, (size_t)single->status->serialSz) == 0) + if (single->status != NULL && id->status != NULL && + (XMEMCMP(single->status->serial, id->status->serial, + (size_t)single->status->serialSz) == 0) && (XMEMCMP(single->issuerHash, id->issuerHash, OCSP_DIGEST_SIZE) == 0) && (XMEMCMP(single->issuerKeyHash, id->issuerKeyHash, OCSP_DIGEST_SIZE) == 0)) { break; @@ -757,7 +759,7 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, single = single->next; } - if (single == NULL) + if (single == NULL || single->status == NULL) return WOLFSSL_FAILURE; if (status != NULL) @@ -1108,6 +1110,9 @@ int wolfSSL_OCSP_basic_verify(WOLFSSL_OCSP_BASICRESP* bs, int embedded; DecodedCert *cert = NULL; + if (bs == NULL) + return WOLFSSL_FAILURE; + ret = OcspFindSigner(bs, certs, &cert, &embedded, flags); if (ret != 0) { WOLFSSL_MSG("OCSP no signer found"); @@ -1300,15 +1305,31 @@ int wolfSSL_i2d_OCSP_RESPONSE(OcspResponse* response, if (response == NULL) return BAD_FUNC_ARG; + if (response->source == NULL) + return BAD_FUNC_ARG; + if (data == NULL) return (int)response->maxIdx; - XMEMCPY(*data, response->source, response->maxIdx); + if (*data == NULL) { + *data = (unsigned char*)XMALLOC(response->maxIdx, NULL, + DYNAMIC_TYPE_OPENSSL); + if (*data == NULL) + return -1; + XMEMCPY(*data, response->source, response->maxIdx); + } + else { + XMEMCPY(*data, response->source, response->maxIdx); + *data += response->maxIdx; + } + return (int)response->maxIdx; } int wolfSSL_OCSP_response_status(OcspResponse *response) { + if (response == NULL) + return -1; return response->responseStatus; } @@ -1335,8 +1356,12 @@ const char *wolfSSL_OCSP_response_status_str(long s) WOLFSSL_OCSP_BASICRESP* wolfSSL_OCSP_response_get1_basic(OcspResponse* response) { WOLFSSL_OCSP_BASICRESP* bs; - const unsigned char *ptr = response->source; + const unsigned char *ptr; + if (response == NULL || response->source == NULL) + return NULL; + + ptr = response->source; bs = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, response->maxIdx); return bs; } @@ -1637,8 +1662,8 @@ int wolfSSL_OCSP_single_get0_status(WOLFSSL_OCSP_SINGLERESP *single, WOLFSSL_ASN1_TIME **thisupd, WOLFSSL_ASN1_TIME **nextupd) { - if (single == NULL) - return WOLFSSL_FAILURE; + if (single == NULL || single->status == NULL) + return -1; #ifdef WOLFSSL_OCSP_PARSE_STATUS if (thisupd != NULL) @@ -1784,7 +1809,7 @@ int wolfSSL_OCSP_REQ_CTX_add1_header(WOLFSSL_OCSP_REQ_CTX *ctx, { WOLFSSL_ENTER("wolfSSL_OCSP_REQ_CTX_add1_header"); - if (name == NULL) { + if (ctx == NULL || name == NULL) { WOLFSSL_MSG("Bad parameter"); return WOLFSSL_FAILURE; } diff --git a/src/ssl.c b/src/ssl.c index 58cd6701c02..7d6ca462bfe 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -11699,13 +11699,19 @@ char* wolfSSL_CIPHER_description(const WOLFSSL_CIPHER* cipher, char* in, int wolfSSL_OCSP_parse_url(const char* url, char** host, char** port, char** path, int* ssl) { - const char* u = url; + const char* u; const char* upath; /* path in u */ const char* uport; /* port in u */ const char* hostEnd; WOLFSSL_ENTER("OCSP_parse_url"); + if (url == NULL || host == NULL || port == NULL || path == NULL || + ssl == NULL) { + return WOLFSSL_FAILURE; + } + + u = url; *host = NULL; *port = NULL; *path = NULL; diff --git a/src/x509.c b/src/x509.c index 46dfd38ed43..b92f7d73569 100644 --- a/src/x509.c +++ b/src/x509.c @@ -1317,7 +1317,9 @@ int wolfSSL_X509_add_ext(WOLFSSL_X509 *x509, WOLFSSL_X509_EXTENSION *ext, if (ext && ext->value.data) { if (ext->value.length == sizeof(word16)) { /* if ext->value is already word16, set directly */ - x509->keyUsage = *(word16*)ext->value.data; + word16 ku; + XMEMCPY(&ku, ext->value.data, sizeof(word16)); + x509->keyUsage = ku; #ifdef BIG_ENDIAN_ORDER x509->keyUsage = rotlFixed16(x509->keyUsage, 8U); #endif @@ -10998,6 +11000,11 @@ WOLFSSL_ASN1_INTEGER* wolfSSL_X509_get_serialNumber(WOLFSSL_X509* x509) if (x509->serialNumber != NULL) return x509->serialNumber; + if (x509->serialSz < 0) { + WOLFSSL_MSG("Invalid serial number size"); + return NULL; + } + a = wolfSSL_ASN1_INTEGER_new(); if (a == NULL) return NULL; @@ -16120,7 +16127,8 @@ int wolfSSL_X509_set1_notBefore(WOLFSSL_X509* x509, const WOLFSSL_ASN1_TIME *t) int wolfSSL_X509_set_serialNumber(WOLFSSL_X509* x509, WOLFSSL_ASN1_INTEGER* s) { WOLFSSL_ENTER("wolfSSL_X509_set_serialNumber"); - if (x509 == NULL || s == NULL || s->length >= EXTERNAL_SERIAL_SIZE) + if (x509 == NULL || s == NULL || s->data == NULL || + s->length >= EXTERNAL_SERIAL_SIZE) return WOLFSSL_FAILURE; /* WOLFSSL_ASN1_INTEGER has type | size | data diff --git a/tests/api.c b/tests/api.c index 0e9cbe29824..352bdd2ad21 100644 --- a/tests/api.c +++ b/tests/api.c @@ -20274,7 +20274,7 @@ static int test_wolfSSL_OCSP_single_get0_status(void) ExpectPtrEq(nextDate, &certStatus.nextDateParsed); ExpectIntEQ(wolfSSL_OCSP_single_get0_status(NULL, NULL, NULL, NULL, NULL), - CERT_GOOD); + -1); ExpectIntEQ(wolfSSL_OCSP_single_get0_status(&single, NULL, NULL, NULL, NULL), CERT_GOOD); #endif diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 997abc308dc..eeb9aec4da9 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -1314,7 +1314,11 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out, #ifndef WOLFSSL_AESGCM_STREAM if ((ctx->authBuffer && ctx->authBufferLen > 0) || (ctx->authBufferLen == 0)) { - if (ctx->enc) + if (ctx->authBufferLen > 0 && out == NULL) { + ret = WOLFSSL_FAILURE; + *outl = 0; + } + else if (ctx->enc) ret = wc_AesGcmEncrypt(&ctx->cipher.aes, out, ctx->authBuffer, ctx->authBufferLen, ctx->iv, ctx->ivSz, ctx->authTag, ctx->authTagSz, @@ -1397,7 +1401,11 @@ int wolfSSL_EVP_CipherFinal(WOLFSSL_EVP_CIPHER_CTX *ctx, unsigned char *out, case WC_AES_256_CCM_TYPE: if ((ctx->authBuffer && ctx->authBufferLen > 0) || (ctx->authBufferLen == 0)) { - if (ctx->enc) { + if (ctx->authBufferLen > 0 && out == NULL) { + ret = WOLFSSL_FAILURE; + *outl = 0; + } + else if (ctx->enc) { ret = wc_AesCcmEncrypt(&ctx->cipher.aes, out, ctx->authBuffer, (word32)ctx->authBufferLen, ctx->iv, (word32)ctx->ivSz, ctx->authTag, @@ -4309,16 +4317,19 @@ int wolfSSL_EVP_SignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sigret, #ifndef NO_DSA case WC_EVP_PKEY_DSA: { int bytes; - ret = wolfSSL_DSA_do_sign(md, sigret, pkey->dsa); + unsigned char tmpSig[DSA_MAX_SIG_SIZE]; + ret = wolfSSL_DSA_do_sign(md, tmpSig, pkey->dsa); /* wolfSSL_DSA_do_sign() can return WOLFSSL_FATAL_ERROR */ if (ret != WOLFSSL_SUCCESS) return ret; bytes = wolfSSL_BN_num_bytes(pkey->dsa->q); if (bytes == WC_NO_ERR_TRACE(WOLFSSL_FAILURE) || - (int)*siglen < bytes * 2) + bytes > DSA_MAX_HALF_SIZE || + bytes * 2 > (int)*siglen) { return WOLFSSL_FAILURE; } + XMEMCPY(sigret, tmpSig, bytes * 2); *siglen = (unsigned int)(bytes * 2); return WOLFSSL_SUCCESS; } @@ -4398,7 +4409,8 @@ int wolfSSL_EVP_VerifyFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char md[WC_MAX_DIGEST_SIZE]; unsigned int mdsize; - if (ctx == NULL) return WOLFSSL_FAILURE; + if (ctx == NULL || pkey == NULL || sig == NULL) + return WOLFSSL_FAILURE; WOLFSSL_ENTER("EVP_VerifyFinal"); ret = wolfSSL_EVP_DigestFinal(ctx, md, &mdsize); if (ret <= 0) @@ -4459,6 +4471,9 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_mac_key(int type, WOLFSSL_ENGINE* e, if (type != WC_EVP_PKEY_HMAC || (key == NULL && keylen != 0)) return NULL; + if (keylen < 0) + return NULL; + pkey = wolfSSL_EVP_PKEY_new(); if (pkey != NULL) { pkey->pkey.ptr = (char*)XMALLOC((size_t)keylen, NULL, @@ -4870,6 +4885,9 @@ int wolfSSL_EVP_DigestSignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sig, return WOLFSSL_SUCCESS; } } + else if (ctx->pctx == NULL || ctx->pctx->pkey == NULL) { + return WOLFSSL_FAILURE; + } #ifndef NO_RSA else if (ctx->pctx->pkey->type == WC_EVP_PKEY_RSA) { if (sig == NULL) { @@ -5007,6 +5025,8 @@ int wolfSSL_EVP_DigestVerifyFinal(WOLFSSL_EVP_MD_CTX *ctx, return WOLFSSL_FAILURE; } else { + if (ctx->pctx == NULL || ctx->pctx->pkey == NULL) + return WOLFSSL_FAILURE; /* Verify the signature with the digest. */ switch (ctx->pctx->pkey->type) { #if !defined(NO_RSA) @@ -10233,6 +10253,9 @@ int wolfSSL_EVP_Digest(const unsigned char* in, int inSz, unsigned char* out, return WOLFSSL_FAILURE; } + if (inSz < 0) + return WOLFSSL_FAILURE; + err = wolfSSL_EVP_get_hashinfo(evp, &hashType, &hashSz); if (err != WOLFSSL_SUCCESS) return err; @@ -11280,6 +11303,7 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) enum wc_HashType macType; WOLFSSL_ENTER("wolfSSL_EVP_DigestFinal"); + macType = EvpMd2MacType(wolfSSL_EVP_MD_CTX_md(ctx)); switch (macType) { case WC_HASH_TYPE_MD4: @@ -11305,16 +11329,18 @@ int wolfSSL_EVP_MD_type(const WOLFSSL_EVP_MD* type) case WC_HASH_TYPE_SHAKE128: #if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE128) - *s = 16; /* if mixing up XOF with plain digest 128 bit is - * default for SHAKE128 */ + if (s != NULL) + *s = 16; /* if mixing up XOF with plain digest 128 bit is + * default for SHAKE128 */ #else return WOLFSSL_FAILURE; #endif break; case WC_HASH_TYPE_SHAKE256: #if defined(WOLFSSL_SHA3) && defined(WOLFSSL_SHAKE256) - *s = 32; /* if mixing up XOF with plain digest 256 bit is - * default for SHAKE256 */ + if (s != NULL) + *s = 32; /* if mixing up XOF with plain digest 256 bit is + * default for SHAKE256 */ #else return WOLFSSL_FAILURE; #endif @@ -12882,6 +12908,9 @@ int wolfSSL_EVP_EncodeBlock(unsigned char *out, const unsigned char *in, if (out == NULL || in == NULL) return WOLFSSL_FATAL_ERROR; + if (inLen < 0) + return WOLFSSL_FATAL_ERROR; + if (Base64_Encode_NoNl(in, (word32)inLen, out, &ret) == 0) return (int)ret; else @@ -12898,6 +12927,9 @@ int wolfSSL_EVP_DecodeBlock(unsigned char *out, const unsigned char *in, if (out == NULL || in == NULL) return WOLFSSL_FATAL_ERROR; + if (inLen < 0) + return WOLFSSL_FATAL_ERROR; + if (Base64_Decode(in, (word32)inLen, out, &ret) == 0) return (int)ret; else diff --git a/wolfcrypt/src/pwdbased.c b/wolfcrypt/src/pwdbased.c index c2ed5c042f5..4b9502a8ca6 100644 --- a/wolfcrypt/src/pwdbased.c +++ b/wolfcrypt/src/pwdbased.c @@ -76,6 +76,9 @@ int wc_PBKDF1_ex(byte* key, int keyLen, byte* iv, int ivLen, return BAD_FUNC_ARG; } + if (keyLen > INT_MAX - ivLen) + return BAD_FUNC_ARG; + if (iterations <= 0) iterations = 1; From 807214dc559c86e9cacce564fb0dbc7586ffece2 Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Fri, 17 Apr 2026 19:32:23 +0200 Subject: [PATCH 2/3] Avoid unneeded temporary stack buffer; remove redundant check --- src/ocsp.c | 2 +- wolfcrypt/src/evp.c | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ocsp.c b/src/ocsp.c index 9de23cb9ea4..8c779c62476 100644 --- a/src/ocsp.c +++ b/src/ocsp.c @@ -759,7 +759,7 @@ int wolfSSL_OCSP_resp_find_status(WOLFSSL_OCSP_BASICRESP *bs, single = single->next; } - if (single == NULL || single->status == NULL) + if (single == NULL) return WOLFSSL_FAILURE; if (status != NULL) diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index eeb9aec4da9..6f73386ce88 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -4317,11 +4317,6 @@ int wolfSSL_EVP_SignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sigret, #ifndef NO_DSA case WC_EVP_PKEY_DSA: { int bytes; - unsigned char tmpSig[DSA_MAX_SIG_SIZE]; - ret = wolfSSL_DSA_do_sign(md, tmpSig, pkey->dsa); - /* wolfSSL_DSA_do_sign() can return WOLFSSL_FATAL_ERROR */ - if (ret != WOLFSSL_SUCCESS) - return ret; bytes = wolfSSL_BN_num_bytes(pkey->dsa->q); if (bytes == WC_NO_ERR_TRACE(WOLFSSL_FAILURE) || bytes > DSA_MAX_HALF_SIZE || @@ -4329,7 +4324,10 @@ int wolfSSL_EVP_SignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sigret, { return WOLFSSL_FAILURE; } - XMEMCPY(sigret, tmpSig, bytes * 2); + ret = wolfSSL_DSA_do_sign(md, sigret, pkey->dsa); + /* wolfSSL_DSA_do_sign() can return WOLFSSL_FATAL_ERROR */ + if (ret != WOLFSSL_SUCCESS) + return ret; *siglen = (unsigned int)(bytes * 2); return WOLFSSL_SUCCESS; } From 7bf63e91ffe35e0d307213c08116b0b5b60b115a Mon Sep 17 00:00:00 2001 From: Mattia Moffa Date: Fri, 17 Apr 2026 20:05:08 +0200 Subject: [PATCH 3/3] Remove now useless check --- wolfcrypt/src/evp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 6f73386ce88..ce885955f6f 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -4319,7 +4319,6 @@ int wolfSSL_EVP_SignFinal(WOLFSSL_EVP_MD_CTX *ctx, unsigned char *sigret, int bytes; bytes = wolfSSL_BN_num_bytes(pkey->dsa->q); if (bytes == WC_NO_ERR_TRACE(WOLFSSL_FAILURE) || - bytes > DSA_MAX_HALF_SIZE || bytes * 2 > (int)*siglen) { return WOLFSSL_FAILURE;