Manual session reuse does not enforce single-use ticket
Summary
- Root Cause Key:
manual-session-reuse-does-not-enforce-single-use-ticket
This report replaces the earlier over-merged conclusion. After re-checking
RFC 8446, the wolfSSL source, and focused runtime behavior, the supported
issue is narrower:
- If an application keeps the same
WOLFSSL_SESSION* and passes it to
wolfSSL_set_session() more than once, wolfSSL does not force one-time
consumption of the TLS 1.3 ticket/session state.
- This maps to RFC 8446 Appendix C.4 client tracking prevention.
- The broader claims in the earlier report were not confirmed:
fresh ticket on every connection is implemented, and
unauthenticated operation without explicit configuration was not
reproduced.
The correct classification is therefore confirmed_partial: an
API-side support only privacy hardening gap, not the broader standards
violation claimed in the original report.
Standard Requirement
Official standard link: https://www.rfc-editor.org/rfc/rfc8446.html#appendix-C.4
Section: Appendix C.4, Client Tracking Prevention
Clients SHOULD NOT reuse a ticket for multiple connections. Reuse of
a ticket allows passive observers to correlate different connections.
Servers that issue tickets SHOULD offer at least as many tickets as
the number of connections that a client might use; for example, a web
browser using HTTP/1.1 [RFC7230] might open six connections to a
server. Servers SHOULD issue new tickets with every connection.
For the current issue, the first sentence is the relevant one. The standard
advises clients not to reuse a ticket across multiple connections because
that enables linkability. This is a SHOULD NOT, so the strongest supported
reading is a privacy-policy expectation rather than a mandatory handshake
legality check that every TLS library must hard-enforce internally.
Relevant Source Code
src/ssl_sess.c:1519-1609
int wolfSSL_SetSession(WOLFSSL* ssl, WOLFSSL_SESSION* session)
{
SessionRow* sessRow = NULL;
int ret = WOLFSSL_SUCCESS;
session = ClientSessionToSession(session);
if (ssl == NULL || session == NULL || !session->isSetup) {
WOLFSSL_MSG("ssl or session NULL or not set up");
return WOLFSSL_FAILURE;
}
if (ret == WOLFSSL_SUCCESS) {
if (ssl->session == session) {
WOLFSSL_MSG("ssl->session and session same");
}
else if (session->type != WOLFSSL_SESSION_TYPE_CACHE) {
if (wolfSSL_SESSION_up_ref(session) == WOLFSSL_SUCCESS) {
wolfSSL_FreeSession(ssl->ctx, ssl->session);
ssl->session = session;
}
else
ret = WOLFSSL_FAILURE;
}
else {
ret = wolfSSL_DupSession(session, ssl->session, 0);
if (ret != WOLFSSL_SUCCESS)
WOLFSSL_MSG("Session duplicate failed");
}
}
if (ret != WOLFSSL_SUCCESS)
return ret;
ssl->options.resuming = 1;
ssl->options.haveEMS = (ssl->session->haveEMS) ? 1 : 0;
wolfSSL_SetSession() accepts a caller-supplied WOLFSSL_SESSION, attaches
it to the current ssl, and marks the connection as resuming. There is no
"consume ticket once" or "reject second use" logic in this path.
tests/api.c:34552-34592
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
ExpectIntEQ(wolfSSL_set_session(ssl_c2, sess), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_session(ssl_c3, sess), WOLFSSL_SUCCESS);
/* Exchange initial flights for the second connection */
ExpectIntEQ(wolfSSL_connect(ssl_c2), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
ExpectIntEQ(wolfSSL_get_error(ssl_c2, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
WOLFSSL_ERROR_WANT_READ);
ExpectIntEQ(wolfSSL_accept(ssl_s2), WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
ExpectIntEQ(wolfSSL_get_error(ssl_s2, WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR)),
WOLFSSL_ERROR_WANT_READ);
/* Complete third connection so that new tickets are exchanged */
ExpectIntEQ(test_memio_do_handshake(ssl_c3, ssl_s3, 10, NULL), 0);
/* Complete second connection */
ExpectIntEQ(test_memio_do_handshake(ssl_c2, ssl_s2, 10, NULL), 0);
ExpectIntEQ(wolfSSL_session_reused(ssl_c2), 1);
ExpectIntEQ(wolfSSL_session_reused(ssl_c3), 1);
This test explicitly attaches the same sess to both ssl_c2 and ssl_c3.
The second resumed connection starts first, the third connection then
completes and obtains fresh tickets, and the second connection later
completes as well. Both resumed connections are expected to succeed from the
same original session/ticket state.
src/tls13.c:13037-13219 and src/internal.c:41735-41833
static int SendTls13NewSessionTicket(WOLFSSL* ssl)
{
...
if (ssl->session->ticketNonce.len == 0) {
ssl->session->ticketNonce.len = DEF_TICKET_NONCE_SZ;
ssl->session->ticketNonce.data[0] = 0;
}
else {
...
ssl->session->ticketNonce.data[0]++;
}
...
}
void DoClientTicketFinalize(WOLFSSL* ssl, InternalTicket* it,
const WOLFSSL_SESSION* sess)
{
...
XMEMCPY(ssl->session->ticketNonce.data, it->ticketNonce,
it->ticketNonceLen);
ssl->session->ticketNonce.len = it->ticketNonceLen;
...
}
These paths show that wolfSSL does implement fresh-ticket generation and
ticket-state refresh. The confirmed issue is not "no fresh ticket support";
it is specifically that manual wolfSSL_set_session() reuse does not stop an
application from reusing an older saved session.
Implementation Behavior
The re-checked implementation behavior is:
wolfSSL_get1_session() and wolfSSL_set_session() form an explicit
application-controlled resumption API.
- On that manual resumption path, wolfSSL does not mark a TLS 1.3
session/ticket as consumed after first use.
- Existing wolfSSL tests treat repeated reuse of the same saved session as
allowed behavior.
- The two broader subclaims from the earlier report do not hold:
src/tls13.c:13037-13219 and src/internal.c:41735-41833 cover fresh
ticket issuance and refresh, and examples/client/client.c:3701-3790
shows that peer verification is enabled by default unless the caller
explicitly disables it.
The actual supported issue is therefore narrower than the original merged
report: a client-side privacy policy gap exposed through the public session
resumption API.
Inconsistency Reason
RFC 8446 Appendix C.4 says that clients SHOULD NOT reuse a ticket for
multiple connections because reuse enables correlation by passive observers.
wolfSSL does not enforce that policy on the wolfSSL_set_session() API path.
If an application keeps and reuses the same WOLFSSL_SESSION*, resumption can
proceed multiple times.
However, this is only a partial mismatch:
- The standard text is
SHOULD NOT, not MUST NOT.
- The requirement is about client privacy policy, not basic parser or
transcript correctness.
- wolfSSL exposes the resumption mechanism but leaves the final reuse decision
to the application.
The correct conclusion is therefore confirmed_partial / API-side support only, not the broader standards-bug framing used in the original report.
Runtime Evidence
Focused unit test
The test ran only test_session_ticket_hs_update against the audited build. It completed two connections using the same retained WOLFSSL_SESSION* and checked whether wolfSSL rejected the second manual reuse.
Observed output:
starting unit tests...
Begin API Tests
1763: test_session_ticket_hs_update : passed ( 0.04310)
End API Tests
Failed/Skipped/Passed/All: 0/0/1/1
unit_test: Success for all configured tests.
This test confirms the issue because it succeeds while reusing the same saved
session on two resumed connections:
- The test extracts
sess from the first connection.
- It calls
wolfSSL_set_session(ssl_c2, sess) and
wolfSSL_set_session(ssl_c3, sess).
ssl_c2 begins a resumed handshake.
ssl_c3 completes and receives fresh tickets.
ssl_c2 then also completes, and both connections are asserted to have
reused a session.
That behavior shows that wolfSSL does not enforce one-time consumption of the
original session/ticket state on the manual resumption path.
Impact
The impact is primarily privacy and policy control, not direct authentication
bypass or handshake failure:
- An application that assumes the library will automatically prevent repeated
ticket reuse may unintentionally permit linkability across connections.
- Callers that expect one-time ticket consumption by default will observe a
behavior gap.
- This does not mean wolfSSL fails to issue fresh tickets, and it does not
mean unauthenticated operation is enabled without explicit configuration.
Overall, this is best described as a low-to-moderate privacy hardening gap,
with the actual risk depending on whether the application already enforces its
own session/ticket reuse policy.
Fix Direction
Reasonable fixes or mitigations include:
- Add an optional library-side
single-use resumption policy.
For example, track a consumed bit for TLS 1.3 session state and reject a
second wolfSSL_set_session() use once resumption succeeds, or enable that
behavior behind an explicit strict-mode option.
- Strengthen API documentation and examples.
Document that wolfSSL_set_session() does not automatically prevent
repeated reuse of the same WOLFSSL_SESSION, and that applications should
destroy, rotate, or replace saved sessions according to their privacy
policy.
- Add clearer tests around policy behavior.
Keep the current compatibility test, but also add a strict-policy test that
verifies repeated reuse is rejected when the new option is enabled.
This report now supersedes the earlier broader claim. The previously merged
subclaims about missing fresh tickets and unauthenticated operation without
explicit configuration were not confirmed during re-check.
Manual session reuse does not enforce single-use ticket
Summary
manual-session-reuse-does-not-enforce-single-use-ticketThis report replaces the earlier over-merged conclusion. After re-checking
RFC 8446, the wolfSSL source, and focused runtime behavior, the supported
issue is narrower:
WOLFSSL_SESSION*and passes it towolfSSL_set_session()more than once, wolfSSL does not force one-timeconsumption of the TLS 1.3 ticket/session state.
fresh ticket on every connectionis implemented, andunauthenticated operation without explicit configurationwas notreproduced.
The correct classification is therefore
confirmed_partial: anAPI-side support onlyprivacy hardening gap, not the broader standardsviolation claimed in the original report.
Standard Requirement
Official standard link: https://www.rfc-editor.org/rfc/rfc8446.html#appendix-C.4
Section: Appendix C.4,
Client Tracking PreventionFor the current issue, the first sentence is the relevant one. The standard
advises clients not to reuse a ticket across multiple connections because
that enables linkability. This is a
SHOULD NOT, so the strongest supportedreading is a privacy-policy expectation rather than a mandatory handshake
legality check that every TLS library must hard-enforce internally.
Relevant Source Code
src/ssl_sess.c:1519-1609wolfSSL_SetSession()accepts a caller-suppliedWOLFSSL_SESSION, attachesit to the current
ssl, and marks the connection as resuming. There is no"consume ticket once" or "reject second use" logic in this path.
tests/api.c:34552-34592This test explicitly attaches the same
sessto bothssl_c2andssl_c3.The second resumed connection starts first, the third connection then
completes and obtains fresh tickets, and the second connection later
completes as well. Both resumed connections are expected to succeed from the
same original session/ticket state.
src/tls13.c:13037-13219andsrc/internal.c:41735-41833These paths show that wolfSSL does implement fresh-ticket generation and
ticket-state refresh. The confirmed issue is not "no fresh ticket support";
it is specifically that manual
wolfSSL_set_session()reuse does not stop anapplication from reusing an older saved session.
Implementation Behavior
The re-checked implementation behavior is:
wolfSSL_get1_session()andwolfSSL_set_session()form an explicitapplication-controlled resumption API.
session/ticket as consumed after first use.
allowed behavior.
src/tls13.c:13037-13219andsrc/internal.c:41735-41833cover freshticket issuance and refresh, and
examples/client/client.c:3701-3790shows that peer verification is enabled by default unless the caller
explicitly disables it.
The actual supported issue is therefore narrower than the original merged
report: a client-side privacy policy gap exposed through the public session
resumption API.
Inconsistency Reason
RFC 8446 Appendix C.4 says that clients
SHOULD NOTreuse a ticket formultiple connections because reuse enables correlation by passive observers.
wolfSSL does not enforce that policy on the
wolfSSL_set_session()API path.If an application keeps and reuses the same
WOLFSSL_SESSION*, resumption canproceed multiple times.
However, this is only a partial mismatch:
SHOULD NOT, notMUST NOT.transcript correctness.
to the application.
The correct conclusion is therefore
confirmed_partial/API-side support only, not the broader standards-bug framing used in the original report.Runtime Evidence
Focused unit test
The test ran only
test_session_ticket_hs_updateagainst the audited build. It completed two connections using the same retainedWOLFSSL_SESSION*and checked whether wolfSSL rejected the second manual reuse.Observed output:
This test confirms the issue because it succeeds while reusing the same saved
session on two resumed connections:
sessfrom the first connection.wolfSSL_set_session(ssl_c2, sess)andwolfSSL_set_session(ssl_c3, sess).ssl_c2begins a resumed handshake.ssl_c3completes and receives fresh tickets.ssl_c2then also completes, and both connections are asserted to havereused a session.
That behavior shows that wolfSSL does not enforce one-time consumption of the
original session/ticket state on the manual resumption path.
Impact
The impact is primarily privacy and policy control, not direct authentication
bypass or handshake failure:
ticket reuse may unintentionally permit linkability across connections.
behavior gap.
mean unauthenticated operation is enabled without explicit configuration.
Overall, this is best described as a low-to-moderate privacy hardening gap,
with the actual risk depending on whether the application already enforces its
own session/ticket reuse policy.
Fix Direction
Reasonable fixes or mitigations include:
single-use resumptionpolicy.For example, track a
consumedbit for TLS 1.3 session state and reject asecond
wolfSSL_set_session()use once resumption succeeds, or enable thatbehavior behind an explicit strict-mode option.
Document that
wolfSSL_set_session()does not automatically preventrepeated reuse of the same
WOLFSSL_SESSION, and that applications shoulddestroy, rotate, or replace saved sessions according to their privacy
policy.
Keep the current compatibility test, but also add a strict-policy test that
verifies repeated reuse is rejected when the new option is enabled.
This report now supersedes the earlier broader claim. The previously merged
subclaims about missing fresh tickets and unauthenticated operation without
explicit configuration were not confirmed during re-check.