CVE-2026-61544: libp2p-quic Remote Panic via Certificate Expiry Race
A remote attacker can crash any application running a libp2p QUIC listener by presenting a short-lived TLS certificate and deliberately delaying the final handshake packet until the certificate…

The problem
In libp2p-quic < 0.13.1, the post-handshake upgrade path in transports/quic/src/connection/connecting.rs (lines 65-66) calls libp2p_tls::certificate::parse() a second time after Quinn reports handshake completion, and wraps the result in .expect() with no error handling.
libp2p_tls::certificate::parse() re-runs full certificate verification on every invocation, including a wall-clock not_after validity check. If the certificate expires in the window between the first parse (during TLS handshake) and the second parse (post-handshake upgrade), the expect panics, crashing the entire process.
No authentication or malformed packets are required.
Proof of concept
A working proof-of-concept for CVE-2026-61544 in libp2p-quic, with the exact payload below.
# Attacker-side steps (no special tooling needed beyond a custom libp2p node):
#
# 1. Generate a libp2p identity keypair.
# 2. Issue a self-signed libp2p TLS certificate with a very short validity window,
# e.g. not_before = now, not_after = now + 2s.
# 3. Open a QUIC connection to the target libp2p listener and present this certificate.
# 4. Complete the TLS 1.3 handshake up to (but not including) the final client
# Finished fragment. Hold that packet.
# 5. Wait until the certificate's not_after timestamp has elapsed.
# 6. Send the withheld final Finished fragment.
#
# Result: the listener's TLS layer accepts the handshake (cert was valid at parse time),
# Quinn signals handshake completion, libp2p-quic re-parses the certificate in the
# post-handshake upgrade path, libp2p_tls::certificate::parse() now sees an expired cert,
# and the .expect("peer certificate is valid") call panics, crashing the process.
#
# Relevant panicking code (pre-patch, connecting.rs L65-66):
# let peer_id = certificate::parse(&peer_cert)
# .expect("peer certificate is valid");The root cause is a TOCTOU (CWE-362) race on certificate validity combined with an unconditional .expect() (CWE-617). libp2p_tls::certificate::parse() is not idempotent across time because it re-evaluates the wall-clock not_after field on every call. The post-handshake call in connecting.rs assumed the result could never differ from the handshake-time parse, which is false for short-lived certificates.
The patch (PR #6525, commits 212f3774 and e8f35e12) replaces the .expect() with proper Result propagation, so an expired certificate at post-handshake parse time closes the connection gracefully instead of panicking. No change to the certificate spec or handshake flow was needed; the fix is purely about handling the error path that was already possible.
The fix
Upgrade libp2p-quic to version **0.13.1** or later. The fix is in PR #6525 (commits 212f3774 and e8f35e12): the .expect() on the post-handshake certificate re-parse is replaced with a graceful error return, so an expired certificate causes a connection close rather than a process panic.
Related research
- high · 7.5mistral.rs: Unbounded Remote Media Fetch and Video Frame Expansion DoS
- high · 7.8CVE-2026-75911CVE-2026-75911: deepseek-tui (CodeWhale) Project Config allow_shell Override Enables Arbitrary Shell Execution
- high · 7.5CVE-2026-75859CVE-2026-75859: deepseek-tui (CodeWhale) Arbitrary File Read via Project Config instructions Override
- high · 9.3CVE-2026-75913CVE-2026-75913: CodeWhale Argument Injection in git_show Allows Arbitrary File Write