Add unit tests that TLS resumption fails due to ALPN mismatch

Fix F-4141
This commit is contained in:
Josh Holtrop
2026-06-04 09:26:43 -04:00
parent 492603fbd4
commit b01382c13b
2 changed files with 157 additions and 0 deletions
+153
View File
@@ -1128,6 +1128,159 @@ int test_tls13_resumption_with_alpn(void)
return EXPECT_RESULT();
}
/* TLS 1.2 stateful (session-ID) resumption must fall back to a full
* handshake if the ALPN protocol negotiated for the resumed connection
* does not match the ALPN bound to the original session. Mirrors
* test_tls12_session_id_resumption_sni_mismatch but varies ALPN instead
* of SNI. */
int test_tls12_session_id_resumption_alpn_mismatch(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_ALPN) && \
defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
struct test_memio_ctx test_ctx;
const char alpnA[] = "h2";
const char alpnB[] = "http/1.1";
/* Step 1: full TLS 1.2 handshake negotiating ALPN=h2, with the
* session ticket path disabled so resumption can only happen via the
* server's session-ID cache. The negotiated ALPN is retained on
* ssl->extensions by ALPN_Select, so SetupSession binds its hash to
* the cached session. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Sanity: the first handshake was not a resumption. */
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
* session cache still holds the entry from step 1). The client offers
* the saved session but both sides now advertise a *different* ALPN
* (http/1.1), so the handshake negotiates http/1.1. The server's cache
* lookup matches by session ID, but the server MUST NOT resume because
* the negotiated ALPN differs from the one bound to the original
* session. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Expected behavior: server falls back to a full handshake because the
* negotiated ALPN does not match the ALPN bound to the cached session.
* Both sides should report no resumption. */
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
wolfSSL_SESSION_free(sess);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* TLS 1.3 PSK resumption must fall back to a full handshake if the ALPN
* protocol negotiated for the resumed connection does not match the ALPN
* bound to the original session. Mirrors
* test_tls13_session_resumption_sni_mismatch but varies ALPN instead of
* SNI. */
int test_tls13_session_resumption_alpn_mismatch(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
defined(HAVE_ALPN) && defined(HAVE_SESSION_TICKET) && \
!defined(NO_SESSION_CACHE)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
struct test_memio_ctx test_ctx;
const char alpnA[] = "h2";
const char alpnB[] = "http/1.1";
byte readBuf[16];
/* Step 1: full TLS 1.3 handshake negotiating ALPN=h2 to obtain a
* session ticket. The negotiated ALPN is retained on ssl->extensions
* by ALPN_Select and bound to the ticket. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Sanity: the first handshake was not a resumption. */
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
/* Drive the post-handshake NewSessionTicket through to the client so
* the saved session is a real resumption ticket. */
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
* ticket key still matches). The client offers the saved session but
* both sides now advertise a *different* ALPN (http/1.1). The server
* MUST NOT resume because the negotiated ALPN differs from the one
* bound to the original ticket. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB),
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Expected behavior: server falls back to a full handshake because the
* negotiated ALPN does not match the ALPN bound to the cached ticket.
* Both sides should report no resumption. */
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
wolfSSL_SESSION_free(sess);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
int test_tls_set_curves_list_ecc_fallback(void)
{
EXPECT_DECLS;
+4
View File
@@ -36,6 +36,8 @@ int test_tls_set_session_min_downgrade(void);
int test_tls12_session_id_resumption_sni_mismatch(void);
int test_tls13_session_resumption_sni_mismatch(void);
int test_tls13_resumption_with_alpn(void);
int test_tls12_session_id_resumption_alpn_mismatch(void);
int test_tls13_session_resumption_alpn_mismatch(void);
int test_tls_set_curves_list_ecc_fallback(void);
int test_tls12_corrupted_finished(void);
int test_tls12_peerauth_failsafe(void);
@@ -57,6 +59,8 @@ int test_wolfSSL_alert_desc_string(void);
TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_sni_mismatch), \
TEST_DECL_GROUP("tls", test_tls13_session_resumption_sni_mismatch), \
TEST_DECL_GROUP("tls", test_tls13_resumption_with_alpn), \
TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_alpn_mismatch),\
TEST_DECL_GROUP("tls", test_tls13_session_resumption_alpn_mismatch), \
TEST_DECL_GROUP("tls", test_tls_set_curves_list_ecc_fallback), \
TEST_DECL_GROUP("tls", test_tls12_corrupted_finished), \
TEST_DECL_GROUP("tls", test_tls12_peerauth_failsafe), \