Skip to content

Harden TLS 1.3 NewSessionTicket handling per RFC 9846 - #11178

Draft
julek-wolfssl wants to merge 5 commits into
wolfSSL:masterfrom
julek-wolfssl:fix/rfc9846-session-ticket-compliance
Draft

Harden TLS 1.3 NewSessionTicket handling per RFC 9846#11178
julek-wolfssl wants to merge 5 commits into
wolfSSL:masterfrom
julek-wolfssl:fix/rfc9846-session-ticket-compliance

Conversation

@julek-wolfssl

Copy link
Copy Markdown
Member
  • Only send a NewSessionTicket when the client advertised psk_key_exchange_modes, per RFC 9846 §4.3.9/§4.7.1. The advertised modes are now recorded in Options since the extension object is freed with the rest of the handshake state; the automatic path skips the ticket, and the explicit API returns MISSING_HANDSHAKE_DATA or PSK_KEY_ERROR. Old behaviour is available via WOLFSSL_TLS13_TICKET_NO_PSK_MODES. Fixes wolfSSL sends TLS 1.3 NewSessionTicket without client PSK mode advertisement #11126.
  • Made this PSK-mode check opt-in behind WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES (including the Options fields that record the advertised modes), since withholding a ticket from clients that omit psk_key_exchange_modes but still expect one is a behaviour change.
  • Validate NewSessionTicket.extensions framing in DoTls13NewSessionTicket() even when WOLFSSL_EARLY_DATA is not defined, so malformed framing is rejected with a decode_error alert regardless of build config. Fixes RFC9846 wolfSSL issue: NewSessionTicket extension TLV framing is skipped without early-data support #11127.
  • Fixed wolfSSL_GetSessionClient() to compare the full cached server ID length instead of only the requested number of bytes, preventing a short server ID from matching an unrelated cached entry in the same cache row and crossing wolfSSL_SetServerID() partitions, per RFC 9846 Appendix C.4. Fixes TLS session ticket partition key prefix aliasing in wolfSSL client cache #11132.
  • Added tests asserting that unrecognized NewSessionTicket extensions (empty vector, one unknown type, two unknown types) are ignored per RFC 9846 §4.7.1, with the connection staying up and application data flowing both ways with no alert sent, both with and without the framing check.

RFC 9846 Section 4.3.9 says psk_key_exchange_modes restricts the PSKs the
server may supply through NewSessionTicket, and Section 4.7.1 makes sending
a ticket conditional on the ClientHello carrying a suitable extension. The
send paths only checked that tickets were enabled.

Record the modes from the ClientHello in Options - the extension object is
freed with the rest of the handshake state, so wolfSSL_send_SessionTicket()
cannot look it up. The automatic path skips the ticket; the explicit API
returns MISSING_HANDSHAKE_DATA or PSK_KEY_ERROR.

Define WOLFSSL_TLS13_TICKET_NO_PSK_MODES for the old behaviour.

Fixes wolfSSL#11126
RFC 9846 Section 4.7.1 defines NewSessionTicket.extensions as a list of
Section 4.3 Extension TLVs, and Section 6 requires a decode_error alert for
a message that cannot be parsed. DoTls13NewSessionTicket() only validated
the outer vector length unless WOLFSSL_EARLY_DATA was defined, so a client
built without early data accepted malformed framing.

Fixes wolfSSL#11127
wolfSSL_GetSessionClient() compared only the requested number of bytes, so a
short server ID could match a cached entry that merely started with the same
bytes and landed in the same cache row. Applications partitioning the cache
with wolfSSL_SetServerID() could then offer a ticket across partitions,
against RFC 9846 Appendix C.4.

Fixes wolfSSL#11132
Withholding a ticket from a client that omits psk_key_exchange_modes is a
behaviour change for peers that omit the extension but still expect a
ticket. Keep the old behaviour by default and gate the RFC 9846 check
behind WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES, including the Options fields
that record the advertised modes.
RFC 9846 Section 4.7.1 requires clients to ignore unrecognized extensions,
so cover an empty vector, one unknown type and two unknown types, and check
that application data still flows both ways afterwards with no alert sent.
These cases pass with or without the framing check and pin down what it must
not reject.
Copilot AI lite review requested due to automatic review settings August 14, 2026 19:54
@julek-wolfssl julek-wolfssl self-assigned this Aug 14, 2026

Copilot AI left a comment

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.

Pull request overview

This PR hardens wolfSSL’s TLS 1.3 NewSessionTicket handling for RFC 9846 compliance by (a) gating ticket sending on the client’s advertised psk_key_exchange_modes (opt-in) and (b) enforcing extension framing validation during NewSessionTicket parsing regardless of early-data support. It also fixes a client session-cache lookup flaw where a short server ID could prefix-match longer cached IDs, allowing cross-partition resumption.

Changes:

  • Add an opt-in PSK-mode precondition (WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES) for automatic and explicit TLS 1.3 ticket sending, persisting advertised modes in Options.
  • Always parse/validate NewSessionTicket.extensions framing via TLSX_Parse() to reject malformed TLVs with decode_error even without WOLFSSL_EARLY_DATA.
  • Fix client cache serverID matching to require both byte-equality and length-equality; add regression tests for the above behaviors.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
wolfssl/internal.h Adds Options fields to persist client-advertised PSK modes across handshake-state cleanup when the opt-in macro is enabled.
src/tls13.c Enforces NST extension TLV framing validation and adds the PSK-mode gate used by both automatic NST loop and wolfSSL_send_SessionTicket().
src/tls.c Records psk_key_exchange_modes presence/modes into Options for later ticket-send decisions.
src/ssl_sess.c Fixes session cache lookup to prevent serverID prefix aliasing by requiring equal cached/requested ID lengths.
tests/api/test_tls13.h Registers new TLS 1.3 ticket/framing tests.
tests/api/test_tls13.c Adds tests for PSK-mode ticket gating and for accepting unknown NST extensions while rejecting malformed framing.
tests/api/test_session.h Registers new client-cache prefix regression test.
tests/api/test_session.c Adds regression test ensuring short server IDs don’t match longer cached IDs.
.wolfssl_known_macro_extras Adds the new opt-in macro to the known-macros list.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/tls.c
Comment on lines +12917 to +12921
/* Keep the advertised modes for the NewSessionTicket decision. The
* extension object is dropped with the rest of the handshake state
* once the handshake is done. */
ssl->options.pskKeModes = modes;
ssl->options.pskKeModesRecvd = 1;
Comment thread tests/api/test_tls13.c
WOLFSSL* ssl_s = NULL;
struct test_memio_ctx test_ctx;

/* A ClientHello without psk_key_exchange_modes gets no ticket. */
Comment thread src/tls13.c
* WOLFSSL_TICKET_HAVE_ID: Session tickets include ID default: off
* Forced on when WOLFSSL_EARLY_DATA is set.
* WOLFSSL_TICKET_NONCE_MALLOC: Dynamically allocate ticket nonce default: off
* WOLFSSL_TLS13_TICKET_CHECK_PSK_MODES: Withhold NewSessionTicket default: off
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants