✓ProveIt
Examples / Report #3153497 Saved analysis

CVE-2025-5025: No QUIC certificate pinning with wolfSSL

curl · 2025-05-199.4s analysis · $0.010 in model calls

What the report claims

The report claims that curl with wolfSSL backend fails to invoke wssl_verify_pinned() for HTTP/3, causing certificate pinning to be bypassed.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2025-5025 No QUIC certificate pinning with wolfSSL

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Why this finding?
  • wssl_verify_pinned() function exists and implements certificate pinning for wolfSSL (lib/vtls/wolfssl.c:1529-1588)
  • The report's core claim is that this function is not *called* for HTTP/3/QUIC connections, but the provided code does not include the QUIC connection setup, vtls callback registration, or wolfSSL QUIC integration points
  • Deterministic check only confirms symbol existence, not call sites
  • Reporter states pinning works for HTTP/1.1 and other TLS backends, suggesting a specific gap in HTTP/3 + wolfSSL
What still needs checking
  • Call sites of wssl_verify_pinned() in the codebase (grep for wssl_verify_pinned)
  • QUIC/HTTP3 connection establishment code for wolfSSL backend
  • vtls filter/callback registration for wolfSSL in QUIC context
How this analysis ran 3 claims · 3 citations verified

Checked against curl master on 2025-05-19 (414ec13840fd). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 2 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
3 claims · 1284 tokens · $0.0001
3 · Evidence
1 code snippets
real curl source at 414ec13840fd · 3 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
6650 tokens · 7.4s · $0.0100
5 · Receipts
3 of 3 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the report. The wssl_verify_pinned() function exists in wolfSSL backend (lib/vtls/wolfssl.c:1529), but we need to check whether the HTTP/3/QUIC code path invokes it. Could you confirm which curl version you tested (8.13.0 or master), and if possible, point to where the QUIC connection setup calls the TLS backend's verify_pinned callback? This will help us locate the missing hook.

Inspect all claims and source code 3 claims · 1 code excerpts

Report claims

C1flaw not verifiable

wssl_verify_pinned() has not been implemented for HTTP/3

Read the reasoning

The provided code shows wssl_verify_pinned() is implemented (lines 1529-1588), but does not show where it is called from. The claim that it 'has not been implemented for HTTP/3' actually means it's not *invoked* for HTTP/3, which requires checking call sites in the QUIC/HTTP3 code path. Those files are not provided.

C2flaw not verifiable

certificate pinning does not work when using HTTP/3 with wolfSSL

Read the reasoning

The pinning verification logic exists in wssl_verify_pinned(). Whether it works for HTTP/3 depends on whether the HTTP/3/QUIC code path calls this function (or an equivalent). The provided source does not include the QUIC connection setup or the vtls callback hooks for wolfSSL.

C3flaw not verifiable

curl should invoke wssl_verify_pinned() but it has not been implemented

Read the reasoning

The claim that curl 'should invoke wssl_verify_pinned() but it has not been implemented' conflates implementation with invocation. The function is implemented; the question is whether the HTTP/3 code path invokes it. The provided code does not contain the relevant call sites.

What the report says about other projects

not addressed wolfSSL certificate manager verifyCallback is called for each cert loaded

Read the original report

Full report on HackerOne ↗

## Summary: When using wolfSSL as the TLS backend, certificate pinning does not work when using HTTP/3. The code should invoke `wssl_verify_pinned()`, but it has not been implemented. ## Affected version ``` # curl -V WARNING: this libcurl is Debug-enabled, do not use in production curl 8.13.0 (x86_64-pc-linux-gnu) libcurl/8.13.0 wolfSSL/5.8.0 zlib/1.3.1 libidn2/2.3.8 libpsl/0.21.2 ngtcp2/1.13.0-DEV nghttp3/1.1 Release-Date: 2025-04-02 Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt pop3 pop3s rtsp smtp smtps telnet tftp ws wss Features: alt-svc AsynchDNS Debug HSTS HTTP3 HTTPS-proxy IDN IPv6 Largefile libz PSL SSL threadsafe TrackMemory UnixSockets ``` ## Steps To Reproduce: I will explain using a connection to google.com as an example. 1. Prepare curl with WolfSSL backend. 1. curl --http3 https://google.com --pinnedpubkey sha256//ffff It should result in an error because the specified public key and the certificate's public key are different, but no error occurs. An error occurs when using HTTP/1.1. An error occurs when the TLS backend is OpenSSL or GnuTLS. ## Impact Bypassing Certificate Pinning.

Source code

lib/vtls/wolfssl.clines 1529–1588 · definition of wssl_verify_pinned
1529static CURLcode wssl_verify_pinned(struct Curl_cfilter *cf,
C1: wssl_verify_pinned() function definition exists (lines 1529-1588), but call sites are not provided
C2: wssl_verify_pinned() implements pinning verification logic, but HTTP/3 call path not shown
C3: Function exists but invocation points for HTTP/3 not in provided code
1530 struct Curl_easy *data)
1531{
1532 struct ssl_connect_data *connssl = cf->ctx;
1533#ifndef CURL_DISABLE_PROXY
1534 const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ?
1535 data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
1536 data->set.str[STRING_SSL_PINNEDPUBLICKEY];
1537#else
1538 const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
1539#endif
1540
1541 if(pinnedpubkey) {
1542#ifdef KEEP_PEER_CERT
1543 struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
1544 WOLFSSL_X509 *x509;
1545 const char *x509_der;
1546 int x509_der_len;
1547 struct Curl_X509certificate x509_parsed;
1548 struct Curl_asn1Element *pubkey;
1549 CURLcode result;
1550
1551 x509 = wolfSSL_get_peer_certificate(wssl->ssl);
1552 if(!x509) {
1553 failf(data, "SSL: failed retrieving server certificate");
1554 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
1555 }
1556
1557 x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
1558 if(!x509_der) {
1559 failf(data, "SSL: failed retrieving ASN.1 server certificate");
1560 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
1561 }
1562
1563 memset(&x509_parsed, 0, sizeof(x509_parsed));
1564 if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
1565 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
1566
1567 pubkey = &x509_parsed.subjectPublicKeyInfo;
1568 if(!pubkey->header || pubkey->end <= pubkey->header) {
1569 failf(data, "SSL: failed retrieving public key from server certificate");
1570 return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
1571 }
1572
1573 result = Curl_pin_peer_pubkey(data,
1574 pinnedpubkey,
1575 (const unsigned char *)pubkey->header,
1576 (size_t)(pubkey->end - pubkey->header));
1577 wolfSSL_FreeX509(x509);
1578 if(result) {
1579 failf(data, "SSL: public key does not match pinned public key");
1580 return result;
1581 }
1582#else
1583 failf(data, "Library lacks pinning support built-in");
1584 return CURLE_NOT_BUILT_IN;
1585#endif
1586 }
1587 return CURLE_OK;
1588}