new file mode 100644
@@ -0,0 +1,750 @@
+From ed131747c1a3dc7f816d26724c44537ac8acba44 Mon Sep 17 00:00:00 2001
+From: Stefan Eissing <stefan@eissing.org>
+Date: Wed, 2 Sep 2026 12:25:50 +0200
+Subject: [PATCH] x509: stricter parsing
+
+Check that the "validity" ASN.1 field has a length matching the
+contained date elements and fail the parsing otherwise.
+
+This is a precaution against malformed cert data that should have been
+caught by the TLS backend already. A bit added defense.
+
+Closes #22799
+
+CVE: CVE-2026-87866
+Upstream-Status: Backport [https://github.com/curl/curl/commit/ed131747c1a3dc7f816d26724c44537ac8acba44]
+
+Backport Changes:
+- Apply the byte-pointer updates to curl 8.19.0's older ASN.1 helpers.
+ Retain its Boolean, bit-string, integer and OID conversion logic,
+ including OID2str()'s symbolic argument in its updated signature.
+- Keep getASN1Element_()'s older flat length branches while applying
+ the byte reads and bounds comparison.
+- Expose getASN1Element() with UNITTEST and remove the older
+ Curl_x509_getASN1Element() wrapper. Add the private test prototype
+ to x509asn1.h: the old generator extracts only single-line prototypes.
+ Guard the production forward declaration to avoid a duplicate in
+ unit-test builds; retain WARN_UNUSED_RESULT on both declarations.
+- Adapt test1657 from the old CURLcode wrapper to the parser's pointer
+ result with the same success/error expectations. Rename its
+ inactive-build message to match the parser name.
+- Omit the type-only updates to unit1666, unit1667 and unit1676. These
+ tests are absent in 8.19.0; [1], [2] and [3] introduced them for OID
+ conversion, ASN.1-to-string conversion and DH public-key extraction.
+
+[1] https://github.com/curl/curl/commit/673e14cd33ef720b8c2b7e3485084e6e41a2c9dd
+[2] https://github.com/curl/curl/commit/14782b36fea2a5f69029b2b898302b6ba5aef838
+[3] https://github.com/curl/curl/commit/61d59c9e39b451b30feb431b780a5cc325757921
+
+Signed-off-by: Darsh Kelaiya <dkelaiya@cisco.com>
+---
+ lib/vtls/gtls.c | 4 +-
+ lib/vtls/mbedtls.c | 5 +-
+ lib/vtls/rustls.c | 13 ++---
+ lib/vtls/schannel.c | 10 ++--
+ lib/vtls/wolfssl.c | 4 +-
+ lib/vtls/x509asn1.c | 124 +++++++++++++++++++++---------------------
+ lib/vtls/x509asn1.h | 22 ++++----
+ tests/unit/unit1651.c | 6 +-
+ tests/unit/unit1657.c | 79 ++++++++++++++++++++++++---
+ 9 files changed, 163 insertions(+), 104 deletions(-)
+
+diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
+index b87b27d864..02a8147520 100644
+--- a/lib/vtls/gtls.c
++++ b/lib/vtls/gtls.c
+@@ -1626,8 +1626,8 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf,
+ goto out;
+
+ for(i = 0; i < chain.num_certs; i++) {
+- const char *beg = (const char *)chain.certs[i].data;
+- const char *end = beg + chain.certs[i].size;
++ const uint8_t *beg = chain.certs[i].data;
++ const uint8_t *end = beg + chain.certs[i].size;
+
+ result = Curl_extract_certinfo(data, (int)i, beg, end);
+ if(result)
+diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
+index d2199df884..cf7ac29594 100644
+--- a/lib/vtls/mbedtls.c
++++ b/lib/vtls/mbedtls.c
+@@ -431,9 +431,8 @@ static void mbed_extract_certinfo(struct Curl_easy *data,
+ result = Curl_ssl_init_certinfo(data, cert_count);
+
+ for(i = 0, cur = crt; result == CURLE_OK && cur; ++i, cur = cur->next) {
+- const char *beg = (const char *)cur->raw.p;
+- const char *end = beg + cur->raw.len;
+- result = Curl_extract_certinfo(data, i, beg, end);
++ result = Curl_extract_certinfo(data, i, cur->raw.p,
++ cur->raw.p + cur->raw.len);
+ }
+ }
+
+diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
+index 89619432b8..017e7ddcd8 100644
+--- a/lib/vtls/rustls.c
++++ b/lib/vtls/rustls.c
+@@ -1225,15 +1225,10 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
+ (int)errorlen, errorbuf);
+ return map_error(rresult);
+ }
+- {
+- const char *beg;
+- const char *end;
+- beg = (const char *)der_data;
+- end = (const char *)(der_data + der_len);
+- result = Curl_extract_certinfo(data, (int)i, beg, end);
+- if(result)
+- return result;
+- }
++ result = Curl_extract_certinfo(data, (int)i,
++ der_data, der_data + der_len);
++ if(result)
++ return result;
+ }
+ }
+
+diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
+index a4f57c7c0a..643b6d510c 100644
+--- a/lib/vtls/schannel.c
++++ b/lib/vtls/schannel.c
+@@ -1108,7 +1108,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+
+ do {
+ SECURITY_STATUS sspi_status;
+- const char *x509_der;
++ uint8_t *x509_der;
+ DWORD x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+@@ -1129,7 +1129,7 @@ static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+ (pCertContextServer->cbCertEncoded > 0)))
+ break;
+
+- x509_der = (const char *)pCertContextServer->pbCertEncoded;
++ x509_der = (uint8_t *)pCertContextServer->pbCertEncoded;
+ x509_der_len = pCertContextServer->cbCertEncoded;
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+@@ -1543,12 +1543,12 @@ static bool add_cert_to_certinfo(const CERT_CONTEXT *ccert_context,
+ struct Adder_args *args = (struct Adder_args *)raw_arg;
+ args->result = CURLE_OK;
+ if(valid_cert_encoding(ccert_context)) {
+- const char *beg = (const char *)ccert_context->pbCertEncoded;
+- const char *end = beg + ccert_context->cbCertEncoded;
+ int insert_index = reverse_order ? (args->certs_count - 1) - args->idx :
+ args->idx;
+ args->result = Curl_extract_certinfo(args->data, insert_index,
+- beg, end);
++ ccert_context->pbCertEncoded,
++ ccert_context->pbCertEncoded +
++ ccert_context->cbCertEncoded);
+ args->idx++;
+ }
+ return args->result == CURLE_OK;
+diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
+index c901ebfe1e..5a2967d275 100644
+--- a/lib/vtls/wolfssl.c
++++ b/lib/vtls/wolfssl.c
+@@ -1545,7 +1545,7 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+
+ if(pinnedpubkey) {
+ #ifdef KEEP_PEER_CERT
+- const char *x509_der;
++ const uint8_t *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+@@ -1558,7 +1558,7 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+ goto end;
+ }
+
+- x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
++ x509_der = wolfSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ goto end;
+diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c
+index b687a60193..65f2d9229d 100644
+--- a/lib/vtls/x509asn1.c
++++ b/lib/vtls/x509asn1.c
+@@ -160,17 +160,19 @@ static const struct Curl_OID OIDtable[] = {
+ * Please note there is no pretension here to rewrite a full SSL library.
+ */
+
+-static const char *getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end)
++#ifndef UNITTESTS
++static const uint8_t *getASN1Element(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end)
+ WARN_UNUSED_RESULT;
++#endif
+
+ #define CURL_ASN1_MAX_RECURSIONS 16
+
+-static const char *getASN1Element_(struct Curl_asn1Element *elem,
+- const char *beg, const char *end,
++static const uint8_t *getASN1Element_(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end,
+ size_t lvl)
+ {
+- unsigned char b;
++ uint8_t b;
+ size_t len;
+ struct Curl_asn1Element lelem;
+
+@@ -185,7 +187,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+
+ /* Process header byte. */
+ elem->header = beg;
+- b = (unsigned char)*beg++;
++ b = *beg++;
+ elem->constructed = (b & 0x20) != 0;
+ elem->eclass = (b >> 6) & 3;
+ b &= 0x1F;
+@@ -196,7 +198,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ /* Process length. */
+ if(beg >= end)
+ return NULL;
+- b = (unsigned char)*beg++;
++ b = *beg++;
+ if(!(b & 0x80))
+ len = b;
+ else if(!(b &= 0x7F)) {
+@@ -215,7 +217,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ elem->end = beg;
+ return beg + 1;
+ }
+- else if((unsigned)b > (size_t)(end - beg))
++ else if(b > (size_t)(end - beg))
+ return NULL; /* Does not fit in source. */
+ else {
+ /* Get long length. */
+@@ -223,7 +225,7 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ do {
+ if(len & 0xFF000000L)
+ return NULL; /* Lengths > 32 bits are not supported. */
+- len = (len << 8) | (unsigned char) *beg++;
++ len = (len << 8) | *beg++;
+ } while(--b);
+ }
+ if(len > (size_t)(end - beg))
+@@ -233,8 +235,11 @@ static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ return elem->end;
+ }
+
+-static const char *getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end)
++/*
++ * @unittest 1657
++ */
++UNITTEST const uint8_t *getASN1Element(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end)
+ {
+ return getASN1Element_(elem, beg, end, 0);
+ }
+@@ -255,17 +260,6 @@ static const struct Curl_OID *searchOID(const char *oid)
+ return NULL;
+ }
+
+-#ifdef UNITTESTS
+-/* used by unit1657.c */
+-CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end)
+-{
+- if(getASN1Element(elem, beg, end))
+- return CURLE_OK;
+- return CURLE_BAD_FUNCTION_ARGUMENT;
+-}
+-#endif
+-
+ /*
+ * Convert an ASN.1 Boolean value into its string representation.
+ *
+@@ -273,7 +267,7 @@ CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ */
+
+ static CURLcode bool2str(struct dynbuf *store,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ if(end - beg != 1)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+@@ -286,17 +280,18 @@ static CURLcode bool2str(struct dynbuf *store,
+ * Return error code.
+ */
+ static CURLcode octet2str(struct dynbuf *store,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ CURLcode result = CURLE_OK;
+
+ while(!result && beg < end)
+- result = curlx_dyn_addf(store, "%02x:", (unsigned char)*beg++);
++ result = curlx_dyn_addf(store, "%02x:", *beg++);
+
+ return result;
+ }
+
+-static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end)
++static CURLcode bit2str(struct dynbuf *store,
++ const uint8_t *beg, const uint8_t *end)
+ {
+ /* Convert an ASN.1 bit string to a printable string. */
+
+@@ -310,7 +305,8 @@ static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end)
+ *
+ * Returns error.
+ */
+-static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
++static CURLcode int2str(struct dynbuf *store,
++ const uint8_t *beg, const uint8_t *end)
+ {
+ unsigned int val = 0;
+ size_t n = end - beg;
+@@ -326,7 +322,7 @@ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
+ val = ~val;
+
+ do
+- val = (val << 8) | *(const unsigned char *)beg++;
++ val = (val << 8) | *beg++;
+ while(beg < end);
+ return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
+ }
+@@ -339,8 +335,8 @@ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end)
+ *
+ * Returns error.
+ */
+-static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from,
+- const char *end)
++static CURLcode utf8asn1str(struct dynbuf *to, int type,
++ const uint8_t *from, const uint8_t *end)
+ {
+ size_t inlength = end - from;
+ int size = 1;
+@@ -381,14 +377,14 @@ static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from,
+
+ switch(size) {
+ case 4:
+- wc = (wc << 8) | *(const unsigned char *)from++;
+- wc = (wc << 8) | *(const unsigned char *)from++;
++ wc = (wc << 8) | *from++;
++ wc = (wc << 8) | *from++;
+ FALLTHROUGH();
+ case 2:
+- wc = (wc << 8) | *(const unsigned char *)from++;
++ wc = (wc << 8) | *from++;
+ FALLTHROUGH();
+ default: /* case 1: */
+- wc = (wc << 8) | *(const unsigned char *)from++;
++ wc = (wc << 8) | *from++;
+ }
+ if(wc >= 0x00000080) {
+ if(wc >= 0x00000800) {
+@@ -422,14 +418,14 @@ static CURLcode utf8asn1str(struct dynbuf *to, int type, const char *from,
+ * Return error code.
+ */
+ static CURLcode encodeOID(struct dynbuf *store,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ unsigned int x;
+ unsigned int y;
+ CURLcode result = CURLE_OK;
+
+ /* Process the first two numbers. */
+- y = *(const unsigned char *)beg++;
++ y = *beg++;
+ x = y / 40;
+ y -= x * 40;
+
+@@ -445,7 +441,7 @@ static CURLcode encodeOID(struct dynbuf *store,
+ return CURLE_OK;
+ else if(beg == end)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+- y = *(const unsigned char *)beg++;
++ y = *beg++;
+ x = (x << 7) | (y & 0x7F);
+ } while(y & 0x80);
+ result = curlx_dyn_addf(store, ".%u", x);
+@@ -460,7 +456,7 @@ static CURLcode encodeOID(struct dynbuf *store,
+ */
+
+ static CURLcode OID2str(struct dynbuf *store,
+- const char *beg, const char *end, bool symbolic)
++ const uint8_t *beg, const uint8_t *end, bool symbolic)
+ {
+ CURLcode result = CURLE_OK;
+ if(beg < end) {
+@@ -650,10 +646,12 @@ static CURLcode ASN1tostr(struct dynbuf *store,
+ result = OID2str(store, elem->beg, elem->end, TRUE);
+ break;
+ case CURL_ASN1_UTC_TIME:
+- result = UTime2str(store, elem->beg, elem->end);
++ result = UTime2str(store, (const char *)elem->beg,
++ (const char *)elem->end);
+ break;
+ case CURL_ASN1_GENERALIZED_TIME:
+- result = GTime2str(store, elem->beg, elem->end);
++ result = GTime2str(store, (const char *)elem->beg,
++ (const char *)elem->end);
+ break;
+ case CURL_ASN1_UTF8_STRING:
+ case CURL_ASN1_NUMERIC_STRING:
+@@ -681,10 +679,10 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
+ struct Curl_asn1Element atv;
+ struct Curl_asn1Element oid;
+ struct Curl_asn1Element value;
+- const char *p1;
+- const char *p2;
+- const char *p3;
+- const char *str;
++ const uint8_t *p1;
++ const uint8_t *p2;
++ const uint8_t *p3;
++ const uint8_t *str;
+ CURLcode result = CURLE_OK;
+ bool added = FALSE;
+ struct dynbuf temp;
+@@ -716,7 +714,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
+ if(result)
+ goto error;
+
+- str = curlx_dyn_ptr(&temp);
++ str = curlx_dyn_uptr(&temp);
+
+ if(!str) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+@@ -737,7 +735,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
+ }
+
+ /* Encode attribute name. */
+- result = curlx_dyn_add(store, str);
++ result = curlx_dyn_add(store, (const char *)str);
+ if(result)
+ goto error;
+
+@@ -769,13 +767,14 @@ error:
+ * See RFC 5280.
+ */
+ int Curl_parseX509(struct Curl_X509certificate *cert,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element tbsCertificate;
+- const char *ccp;
+- static const char defaultVersion = 0; /* v1. */
++ const uint8_t *ccp;
++ static const uint8_t defaultVersion = 0; /* v1. */
+
++ memset(cert, 0, sizeof(*cert));
+ cert->certificate.header = NULL;
+ cert->certificate.beg = beg;
+ cert->certificate.end = end;
+@@ -831,6 +830,9 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
+ return -1;
+ if(!getASN1Element(&cert->notAfter, ccp, elem.end))
+ return -1;
++ /* These two fields MUST consume all of the validity elem */
++ if(elem.end != cert->notAfter.end)
++ return -1;
+ /* Get subject. */
+ beg = getASN1Element(&cert->subject, beg, end);
+ if(!beg)
+@@ -851,10 +853,10 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
+ cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0;
+ cert->extensions.tag = elem.tag = 0;
+ cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL;
+- cert->issuerUniqueID.beg = cert->issuerUniqueID.end = "";
+- cert->subjectUniqueID.beg = cert->subjectUniqueID.end = "";
++ cert->issuerUniqueID.beg = cert->issuerUniqueID.end = beg;
++ cert->subjectUniqueID.beg = cert->subjectUniqueID.end = beg;
+ cert->extensions.header = NULL;
+- cert->extensions.beg = cert->extensions.end = "";
++ cert->extensions.beg = cert->extensions.end = beg;
+ if(beg < end) {
+ beg = getASN1Element(&elem, beg, end);
+ if(!beg)
+@@ -888,7 +890,7 @@ int Curl_parseX509(struct Curl_X509certificate *cert,
+
+ static CURLcode dumpAlgo(struct dynbuf *store,
+ struct Curl_asn1Element *param,
+- const char *beg, const char *end)
++ const uint8_t *beg, const uint8_t *end)
+ {
+ struct Curl_asn1Element oid;
+
+@@ -901,7 +903,7 @@ static CURLcode dumpAlgo(struct dynbuf *store,
+ param->tag = 0;
+ param->beg = param->end = end;
+ if(beg < end) {
+- const char *p = getASN1Element(param, beg, end);
++ const uint8_t *p = getASN1Element(param, beg, end);
+ if(!p)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+@@ -972,7 +974,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo,
+ {
+ struct Curl_asn1Element elem;
+ struct Curl_asn1Element pk;
+- const char *p;
++ const uint8_t *p;
+
+ /* Generate all information records for the public key. */
+
+@@ -1004,7 +1006,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo,
+ return 1;
+
+ if(curl_strequal(algo, "rsaEncryption")) {
+- const char *q;
++ const uint8_t *q;
+ size_t len;
+
+ p = getASN1Element(&elem, pk.beg, pk.end);
+@@ -1017,7 +1019,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo,
+ len = ((elem.end - q) * 8);
+ if(len) {
+ unsigned int i;
+- for(i = *(const unsigned char *)q; !(i & 0x80); i <<= 1)
++ for(i = *q; !(i & 0x80); i <<= 1)
+ len--;
+ }
+ if(len > 32)
+@@ -1083,8 +1085,8 @@ static CURLcode DNtostr(struct dynbuf *store, struct Curl_asn1Element *dn)
+
+ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ int certnum,
+- const char *beg,
+- const char *end)
++ const uint8_t *beg,
++ const uint8_t *end)
+ {
+ struct Curl_X509certificate cert;
+ struct Curl_asn1Element param;
+@@ -1093,7 +1095,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ struct dynbuf out;
+ CURLcode result = CURLE_OK;
+ unsigned int version;
+- const char *ptr;
++ const uint8_t *ptr;
+ int rc;
+
+ if(!data->set.ssl.certinfo)
+@@ -1132,7 +1134,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
+ /* Version (always fits in less than 32 bits). */
+ version = 0;
+ for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
+- version = (version << 8) | *(const unsigned char *)ptr;
++ version = (version << 8) | *ptr;
+ if(data->set.ssl.certinfo) {
+ result = curlx_dyn_addf(&out, "%x", version);
+ if(result)
+diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h
+index 829dfd884f..e5374108e5 100644
+--- a/lib/vtls/x509asn1.h
++++ b/lib/vtls/x509asn1.h
+@@ -38,11 +38,11 @@
+
+ /* ASN.1 parsed element. */
+ struct Curl_asn1Element {
+- const char *header; /* Pointer to header byte. */
+- const char *beg; /* Pointer to element data. */
+- const char *end; /* Pointer to 1st byte after element. */
+- unsigned char eclass; /* ASN.1 element class. */
+- unsigned char tag; /* ASN.1 element tag. */
++ const uint8_t *header; /* Pointer to header byte. */
++ const uint8_t *beg; /* Pointer to element data. */
++ const uint8_t *end; /* Pointer to 1st byte after element. */
++ uint8_t eclass; /* ASN.1 element class. */
++ uint8_t tag; /* ASN.1 element tag. */
+ BIT(constructed); /* Element is constructed. */
+ };
+
+@@ -70,22 +70,22 @@ struct Curl_X509certificate {
+ */
+
+ int Curl_parseX509(struct Curl_X509certificate *cert,
+- const char *beg, const char *end);
++ const uint8_t *beg, const uint8_t *end);
+ CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum,
+- const char *beg, const char *end);
++ const uint8_t *beg, const uint8_t *end);
+ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
+- const char *beg, const char *end);
++ const uint8_t *beg, const uint8_t *end);
+
+ #ifdef UNITTESTS
++const uint8_t *getASN1Element(struct Curl_asn1Element *elem,
++ const uint8_t *beg, const uint8_t *end)
++ WARN_UNUSED_RESULT;
+ #if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \
+ defined(USE_RUSTLS)
+
+ /* used by unit1656.c */
+ CURLcode Curl_x509_GTime2str(struct dynbuf *store,
+ const char *beg, const char *end);
+-/* used by unit1657.c */
+-CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+- const char *beg, const char *end);
+ #endif /* USE_GNUTLS || USE_SCHANNEL || USE_MBEDTLS || RUSTLS */
+ #endif /* UNITTESTS */
+
+diff --git a/tests/unit/unit1651.c b/tests/unit/unit1651.c
+index 10fc979724..ce8cbaedc5 100644
+--- a/tests/unit/unit1651.c
++++ b/tests/unit/unit1651.c
+@@ -33,7 +33,7 @@ static CURLcode test_unit1651(const char *arg)
+
+ /* cert captured from gdb when connecting to curl.se on October 26
+ 2018. */
+- static unsigned char cert[] = {
++ static uint8_t cert[] = {
+ 0x30, 0x82, 0x0F, 0x5B, 0x30, 0x82, 0x0E, 0x43, 0xA0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x0C, 0x08, 0x77, 0x99, 0x2C, 0x6B, 0x67, 0xE1, 0x18, 0xD6,
+ 0x66, 0x66, 0x9E, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+@@ -365,8 +365,8 @@ static CURLcode test_unit1651(const char *arg)
+ };
+
+ CURLcode result;
+- const char *beg = (const char *)&cert[0];
+- const char *end = (const char *)&cert[sizeof(cert)];
++ const uint8_t *beg = &cert[0];
++ const uint8_t *end = &cert[sizeof(cert)];
+ struct Curl_easy *data;
+ int i;
+ int byte;
+diff --git a/tests/unit/unit1657.c b/tests/unit/unit1657.c
+index 7c769e78da..716df1856c 100644
+--- a/tests/unit/unit1657.c
++++ b/tests/unit/unit1657.c
+@@ -23,7 +23,8 @@
+ ***************************************************************************/
+ #include "unitcheck.h"
+
+-#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS)
++#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
++ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
+
+ #include "vtls/x509asn1.h"
+
+@@ -38,8 +39,8 @@ static CURLcode make1657_nested(const struct test1657_spec *spec,
+ {
+ CURLcode result;
+ size_t i;
+- unsigned char open_undef[] = { 0x32, 0x80 };
+- unsigned char close_undef[] = { 0x00, 0x00 };
++ uint8_t open_undef[] = { 0x32, 0x80 };
++ uint8_t close_undef[] = { 0x00, 0x00 };
+
+ for(i = 0; i < spec->n; ++i) {
+ result = curlx_dyn_addn(buf, open_undef, sizeof(open_undef));
+@@ -66,7 +67,8 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i,
+ {
+ CURLcode result;
+ struct Curl_asn1Element elem;
+- const char *in;
++ const uint8_t *in;
++ const uint8_t *ptr;
+
+ memset(&elem, 0, sizeof(elem));
+ curlx_dyn_reset(buf);
+@@ -75,8 +77,9 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i,
+ curl_mfprintf(stderr, "test %zu: error setting buf %d\n", i, result);
+ return FALSE;
+ }
+- in = curlx_dyn_ptr(buf);
+- result = Curl_x509_getASN1Element(&elem, in, in + curlx_dyn_len(buf));
++ in = curlx_dyn_uptr(buf);
++ ptr = getASN1Element(&elem, in, in + curlx_dyn_len(buf));
++ result = ptr ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
+ if(result != spec->result_exp) {
+ curl_mfprintf(stderr, "test %zu: expect result %d, got %d\n",
+ i, spec->result_exp, result);
+@@ -85,6 +88,59 @@ static bool do_test1657(const struct test1657_spec *spec, size_t i,
+ return TRUE;
+ }
+
++struct test1657_cert_spec {
++ const char * const pem;
++ bool exp_success;
++};
++
++static const struct test1657_cert_spec test1657_certs[] = {
++ { /* a valid certificate */
++ "MIIBGTCBzKADAgECAhQ8Ssn8BaVsqlKKccv81NFuKY1vATAFBgMrZXAwADAeFw0y"
++ "NjA5MDIwNDU0MjZaFw0yNjEwMDIwNDU0MjZaMAAwKjAFBgMrZXADIQBeG0S343s9"
++ "5yRmIK8xZmGvUmiiYvRH5ZMcPq6rvHHKwqNYMFYwNQYDKgMEBC4wADAqMAUGAytl"
++ "cAMhAPP5TXSe2WHt/bebbP8eHPXrG3LNxYz8TnHNgpwTFsT7MB0GA1UdDgQWBBR6"
++ "aqL9LrZiIpgYCy0X4fNrhgFQsTAFBgMrZXADQQALU0OHGcIvfTwyin/+u9csJ9hZ"
++ "W0c+XdHJY5uDdCR1O92K4Vnozf/QCe7ITOJ8aUnCs7Jgf5xrDgyXZNdum04A",
++ TRUE
++ },
++ { /* a certificate with "validity" element that has too long length */
++ "MIIBGTCBzKADAgECAhQ8Ssn8BaVsqlKKccv81NFuKY1vATAFBgMrZXAwADBZFw0y"
++ "NjA5MDIwNDU0MjZaFw0yNjEwMDIwNDU0MjZaMAAwKjAFBgMrZXADIQBeG0S343s9"
++ "5yRmIK8xZmGvUmiiYvRH5ZMcPq6rvHHKwqNYMFYwNQYDKgMEBC4wADAqMAUGAytl"
++ "cAMhAPP5TXSe2WHt/bebbP8eHPXrG3LNxYz8TnHNgpwTFsT7MB0GA1UdDgQWBBR6"
++ "aqL9LrZiIpgYCy0X4fNrhgFQsTAFBgMrZXADQQALU0OHGcIvfTwyin/+u9csJ9hZ"
++ "W0c+XdHJY5uDdCR1O92K4Vnozf/QCe7ITOJ8aUnCs7Jgf5xrDgyXZNdum04A",
++ FALSE
++ }
++};
++
++static bool test1657_parse_cert(size_t i,
++ const struct test1657_cert_spec *spec)
++{
++ struct Curl_X509certificate cert;
++ CURLcode result;
++ uint8_t *cert_der;
++ size_t cert_der_len;
++ bool success;
++
++ result = curlx_base64_decode(spec->pem, &cert_der, &cert_der_len);
++ if(result) {
++ curl_mfprintf(stderr, "cert %zu: base64 decoding failed: %d\n",
++ i, (int)result);
++ return FALSE;
++ }
++
++ success = !Curl_parseX509(&cert, cert_der, cert_der + cert_der_len);
++ if(success != spec->exp_success) {
++ curl_mfprintf(stderr, "cert %zu: parsing DER %sfailed\n", i,
++ spec->exp_success ? "" : "should have ");
++ return FALSE;
++ }
++
++ curlx_free(cert_der);
++ return TRUE;
++}
++
+ static CURLcode test_unit1657(const char *arg)
+ {
+ UNITTEST_BEGIN_SIMPLE
+@@ -104,7 +160,14 @@ static CURLcode test_unit1657(const char *arg)
+ if(!do_test1657(&test1657_specs[i], i, &dbuf))
+ all_ok = FALSE;
+ }
+- fail_unless(all_ok, "some tests of Curl_x509_getASN1Element() fails");
++ fail_unless(all_ok, "some getASN1Element() tests failed");
++
++ all_ok = TRUE;
++ for(i = 0; i < CURL_ARRAYSIZE(test1657_certs); ++i) {
++ if(!test1657_parse_cert(i, &test1657_certs[i]))
++ all_ok = FALSE;
++ }
++ fail_unless(all_ok, "some certificate tests failed");
+
+ curlx_dyn_free(&dbuf);
+ curl_global_cleanup();
+@@ -117,7 +180,7 @@ static CURLcode test_unit1657(const char *arg)
+ static CURLcode test_unit1657(const char *arg)
+ {
+ UNITTEST_BEGIN_SIMPLE
+- puts("not tested since Curl_x509_getASN1Element() is not built in");
++ puts("not tested since getASN1Element() is not built in");
+ UNITTEST_END_SIMPLE
+ }
+
+--
+2.35.6
@@ -71,6 +71,7 @@ SRC_URI = "https://curl.se/download/${BP}.tar.xz \
file://CVE-2026-94266_p1.patch \
file://CVE-2026-94266_p2.patch \
file://CVE-2026-107929.patch \
+ file://CVE-2026-87866.patch \
"
SRC_URI:append:class-nativesdk = " \