@@ -38173,6 +38173,32 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
3817338173 ssl->options.resuming = 0;
3817438174 return ret;
3817538175 }
38176+ #if defined(HAVE_SESSION_TICKET) && \
38177+ (defined(HAVE_SNI) || defined(HAVE_ALPN))
38178+ /* Do not resume session if sniHash/alpnHash do not match. */
38179+ if (!ssl->options.useTicket) {
38180+ byte curHash[TICKET_BINDING_HASH_SZ];
38181+ #ifdef HAVE_SNI
38182+ if (TicketSniHash(ssl, curHash) != 0 ||
38183+ XMEMCMP(curHash, session->sniHash,
38184+ TICKET_BINDING_HASH_SZ) != 0) {
38185+ WOLFSSL_MSG("Resumed session SNI mismatch, full handshake");
38186+ ssl->options.resuming = 0;
38187+ return ret;
38188+ }
38189+ #endif
38190+ #ifdef HAVE_ALPN
38191+ if (ssl->options.resuming &&
38192+ (TicketAlpnHash(ssl, curHash) != 0 ||
38193+ XMEMCMP(curHash, session->alpnHash,
38194+ TICKET_BINDING_HASH_SZ) != 0)) {
38195+ WOLFSSL_MSG("Resumed session ALPN mismatch, full handshake");
38196+ ssl->options.resuming = 0;
38197+ return ret;
38198+ }
38199+ #endif
38200+ }
38201+ #endif /* HAVE_SESSION_TICKET && (HAVE_SNI || HAVE_ALPN) */
3817638202#if !defined(WOLFSSL_NO_TICKET_EXPIRE) && !defined(NO_ASN_TIME)
3817738203 /* check if the ticket is valid */
3817838204 if (LowResTimer() > session->bornOn + ssl->timeout) {
@@ -38754,8 +38780,22 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
3875438780 #endif
3875538781 #if defined(HAVE_SESSION_TICKET) && \
3875638782 (defined(HAVE_SNI) || defined(HAVE_ALPN))
38757- if((ret=VerifyTicketBinding(ssl)))
38758- goto out;
38783+ /* Only verify here for TLS 1.2 ticket-based resumption.
38784+ * For stateful (session-ID) resumption ssl->session is
38785+ * not loaded until HandleTlsResumption runs below, which
38786+ * performs its own binding check against the cached
38787+ * session. On mismatch decline the resumption (RFC 6066
38788+ * Section 3) but proceed with a full handshake; leave
38789+ * useTicket set so the server still issues a fresh
38790+ * ticket to the client. */
38791+ if (ssl->options.useTicket &&
38792+ VerifyTicketBinding(ssl) != 0) {
38793+ WOLFSSL_MSG("Ticket binding mismatch, "
38794+ "declining resumption and falling back "
38795+ "to full handshake");
38796+ ssl->options.resuming = 0;
38797+ ssl->options.peerAuthGood = 0;
38798+ }
3875938799 #endif
3876038800
3876138801 i += totalExtSz;
@@ -39537,7 +39577,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
3953739577
3953839578#ifdef HAVE_SNI
3953939579 /* Hash server-selected SNI; zeros dst when none. */
39540- static int TicketSniHash(WOLFSSL* ssl, byte* dst)
39580+ int TicketSniHash(WOLFSSL* ssl, byte* dst)
3954139581 {
3954239582 char* name = NULL;
3954339583 word16 nameLen;
@@ -39557,16 +39597,23 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
3955739597
3955839598#ifdef HAVE_ALPN
3955939599 /* Hash negotiated ALPN; zeros dst when none. */
39560- static int TicketAlpnHash(WOLFSSL* ssl, byte* dst)
39600+ int TicketAlpnHash(WOLFSSL* ssl, byte* dst)
3956139601 {
39562- char* proto = NULL ;
39563- word16 protoLen = 0 ;
39602+ TLSX* extension ;
39603+ ALPN* alpn ;
3956439604
39565- if (TLSX_ALPN_GetRequest(ssl->extensions, (void**)&proto,
39566- &protoLen) == WOLFSSL_SUCCESS &&
39567- proto != NULL && protoLen > 0) {
39568- return wc_Hash(TICKET_BINDING_HASH_TYPE, (const byte*)proto,
39569- protoLen, dst, TICKET_BINDING_HASH_SZ);
39605+ extension = TLSX_Find(ssl->extensions, TLSX_APPLICATION_LAYER_PROTOCOL);
39606+ if (extension != NULL) {
39607+ alpn = (ALPN*)extension->data;
39608+ if (alpn != NULL && alpn->negotiated == 1 &&
39609+ alpn->protocol_name != NULL) {
39610+ word32 protoLen = (word32)XSTRLEN(alpn->protocol_name);
39611+ if (protoLen > 0) {
39612+ return wc_Hash(TICKET_BINDING_HASH_TYPE,
39613+ (const byte*)alpn->protocol_name,
39614+ protoLen, dst, TICKET_BINDING_HASH_SZ);
39615+ }
39616+ }
3957039617 }
3957139618
3957239619 XMEMSET(dst, 0, TICKET_BINDING_HASH_SZ);
@@ -39575,15 +39622,30 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
3957539622#endif
3957639623
3957739624#if defined(HAVE_SNI) || defined(HAVE_ALPN)
39578- /* Server-side: verify the SNI/ALPN bindings carried on a resumed
39579- * session match what was negotiated for the current connection.
39580- * Must be called after extension parsing and ALPN_Select.
39581- * Returns 0 on match, WOLFSSL_FATAL_ERROR on mismatch. */
39625+ /* Server-side TLS 1.2 ticket-resumption binding check. Confirms the
39626+ * SNI/ALPN bound to the resumed session matches what was negotiated
39627+ * for the current connection. Must be called after extension
39628+ * parsing and ALPN_Select so the negotiated values are available,
39629+ * and only once DoClientTicketFinalize has populated
39630+ * ssl->session->sniHash/alpnHash from the decrypted ticket.
39631+ *
39632+ * Other resumption paths handle the same check themselves and do
39633+ * not use this function:
39634+ * - TLS 1.2 session-ID (stateful): HandleTlsResumption compares
39635+ * against the cached session at lookup time.
39636+ * - TLS 1.3 PSK: DoPreSharedKeys compares against each candidate
39637+ * ticket's bound hashes before committing, allowing the server
39638+ * to skip mismatching PSKs and pick the next one.
39639+ *
39640+ * Returns 0 on match, WOLFSSL_FATAL_ERROR on mismatch. The caller
39641+ * is responsible for the policy on mismatch -- RFC 6066 Section 3
39642+ * mandates declining the resumption and proceeding with a full
39643+ * handshake rather than aborting. */
3958239644 int VerifyTicketBinding(WOLFSSL* ssl)
3958339645 {
3958439646 byte curHash[TICKET_BINDING_HASH_SZ];
3958539647
39586- if (!ssl->options.resuming || !ssl->options.useTicket )
39648+ if (!ssl->options.resuming)
3958739649 return 0;
3958839650
3958939651#ifdef HAVE_SNI
@@ -40092,8 +40154,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
4009240154 ssl->sessionCtxSz) != 0))
4009340155 return WOLFSSL_FATAL_ERROR;
4009440156#endif
40095- /* SNI/ALPN binding is verified after ALPN_Select via
40096- * VerifyTicketBinding(). */
40157+ /* SNI/ALPN binding is checked by the per-PSK loop in
40158+ * DoPreSharedKeys, not here, so that mismatching PSKs can be
40159+ * skipped in favor of the next candidate. */
4009740160 return 0;
4009840161 }
4009940162#endif /* WOLFSSL_SLT13 */
@@ -40189,8 +40252,13 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
4018940252 }
4019040253 }
4019140254#endif
40192- /* Carry the ticket bindings on the session for the deferred
40193- * VerifyTicketBinding() check. */
40255+ /* Carry the ticket bindings on the session. TLS 1.2 uses these
40256+ * for the deferred VerifyTicketBinding() check in DoClientHello
40257+ * (SNI/ALPN aren't known when DoClientTicket runs during
40258+ * extension parsing). TLS 1.3 checks bindings per-PSK before
40259+ * reaching this point, but still copies them so a subsequent
40260+ * SetupSession on a resumed session preserves them in the cache
40261+ * for future resumptions. */
4019440262#ifdef HAVE_SNI
4019540263 XMEMCPY(ssl->session->sniHash, it->sniHash, TICKET_BINDING_HASH_SZ);
4019640264#endif
@@ -40556,8 +40624,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
4055640624 goto cleanup;
4055740625 }
4055840626
40559- /* SNI/ALPN binding is verified after ALPN_Select via
40560- * VerifyTicketBinding(). */
40627+ /* SNI/ALPN binding is verified later in DoClientHello via
40628+ * VerifyTicketBinding(), once extension parsing and ALPN_Select
40629+ * have run and the negotiated values are available. */
4056140630 DoClientTicketFinalize(ssl, it, NULL);
4056240631
4056340632cleanup:
0 commit comments