Skip to content

TLS 1.3 server fails a client's 256th consecutive ticket resumption with SESSION_TICKET_NONCE_OVERFLOW #11632

Description

@neilcohen

Version

5.9.4-stable and master (checked 2026-10-02). The abort was added by #9884, so 5.9.0 and later are affected.

Description

A TLS 1.3 server with session tickets fails the handshake of a client's 256th consecutive resumption with SESSION_TICKET_NONCE_OVERFLOW (-517). This assumes the client always resumes with the newest ticket it received.

When a connection resumes from a ticket, DoClientTicketFinalize (src/internal.c) restores that ticket's nonce into ssl->session->ticketNonce, because the resumption PSK is derived from it. SendTls13NewSessionTicket (src/tls13.c) then increments that inherited value for the connection's new ticket, rather than starting at 0. Each resumption in a chain therefore adds one. Since #9884, the server aborts once the value reaches 255.

RFC 8446 §4.6.1 only requires the nonce to be "unique across all tickets issued on this connection". Each connection's tickets are derived from that connection's own resumption_master_secret, so restarting the nonce on each connection would not repeat a PSK. Before #9884 the counter wrapped silently, so this limit is new in 5.9.0.

What the client sees: the handshake completes as a resumption, then the server closes the connection without an alert. An OpenSSL client keeps the ticket after this failure, so every retry fails the same way until the ticket expires.

We hit this with a service whose clients reconnect often.

Reproduction

Stock 5.9.4-stable, macOS arm64:

./configure --enable-session-ticket --disable-shared --enable-static && make
./examples/server/server -v 4 -d -i -x -g -p 11443

Chain resumptions with OpenSSL 3.6, always resuming from the newest session:

req() { printf 'GET / HTTP/1.1\r\nHost: localhost\r\n\r\n'; sleep 0.2; }
req | openssl s_client -connect localhost:11443 -tls1_3 -ign_eof -sess_out s0.sess > c0.txt 2>&1
prev=s0.sess
for ((i=1; i<=260; i++)); do
  req | openssl s_client -connect localhost:11443 -tls1_3 -ign_eof -sess_in $prev -sess_out s$i.sess > c$i.txt 2>&1
  [ -s s$i.sess ] && prev=s$i.sess
done

Connections 1 to 255 report Reused, TLSv1.3 and receive the HTML page. Connection 256 reports Reused, TLSv1.3 and receives nothing, and the server prints:

SSL_accept error -517, Session ticket nonce overflow

Every later connection fails the same way, because no new ticket was issued.

Expected

A client can keep resuming indefinitely. Each connection's ticket nonces start at 0, or at least the nonce restored from the resumed ticket is not used as the starting point of the counter.

Possible fix

Derive the nonce for tickets sent on a connection from a per-connection counter (for example ssl->options.ticketsSent), rather than from ssl->session->ticketNonce as restored from the resumed ticket. Alternatively, reset ssl->session->ticketNonce on the server once the resumption PSK has been derived.

Workaround

wolfSSL_CTX_no_ticket_TLSv13() on the server context. TLS 1.3 clients then do a full handshake on every connection.

Activity

  1. dgarske commented on Oct 2, 2026

    @dgarske
    Member

    Hi @neilcohen , thank you for this excellent report! Can you tell us more about how you found this and a bit about your use of wolfSSL? I've assigned this to @julek-wolfssl to investigate and fix.
    Thanks, David Garske, wolfSSL

  2. julek-wolfssl commented on Oct 8, 2026

    @julek-wolfssl
    Member

    Hi @neilcohen
    the fix is posted here: #11692
    Juliusz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions