Skip to content

Commit b01382c

Browse files
Add unit tests that TLS resumption fails due to ALPN mismatch
Fix F-4141
1 parent 492603f commit b01382c

2 files changed

Lines changed: 157 additions & 0 deletions

File tree

tests/api/test_tls.c

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,159 @@ int test_tls13_resumption_with_alpn(void)
11281128
return EXPECT_RESULT();
11291129
}
11301130

1131+
/* TLS 1.2 stateful (session-ID) resumption must fall back to a full
1132+
* handshake if the ALPN protocol negotiated for the resumed connection
1133+
* does not match the ALPN bound to the original session. Mirrors
1134+
* test_tls12_session_id_resumption_sni_mismatch but varies ALPN instead
1135+
* of SNI. */
1136+
int test_tls12_session_id_resumption_alpn_mismatch(void)
1137+
{
1138+
EXPECT_DECLS;
1139+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
1140+
!defined(WOLFSSL_NO_TLS12) && defined(HAVE_ALPN) && \
1141+
defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE)
1142+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
1143+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
1144+
WOLFSSL_SESSION *sess = NULL;
1145+
struct test_memio_ctx test_ctx;
1146+
const char alpnA[] = "h2";
1147+
const char alpnB[] = "http/1.1";
1148+
1149+
/* Step 1: full TLS 1.2 handshake negotiating ALPN=h2, with the
1150+
* session ticket path disabled so resumption can only happen via the
1151+
* server's session-ID cache. The negotiated ALPN is retained on
1152+
* ssl->extensions by ALPN_Select, so SetupSession binds its hash to
1153+
* the cached session. */
1154+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1155+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
1156+
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);
1157+
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
1158+
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
1159+
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA),
1160+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1161+
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA),
1162+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1163+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
1164+
/* Sanity: the first handshake was not a resumption. */
1165+
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
1166+
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
1167+
1168+
wolfSSL_free(ssl_c); ssl_c = NULL;
1169+
wolfSSL_free(ssl_s); ssl_s = NULL;
1170+
1171+
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
1172+
* session cache still holds the entry from step 1). The client offers
1173+
* the saved session but both sides now advertise a *different* ALPN
1174+
* (http/1.1), so the handshake negotiates http/1.1. The server's cache
1175+
* lookup matches by session ID, but the server MUST NOT resume because
1176+
* the negotiated ALPN differs from the one bound to the original
1177+
* session. */
1178+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1179+
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
1180+
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
1181+
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
1182+
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
1183+
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
1184+
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
1185+
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_c), WOLFSSL_SUCCESS);
1186+
ExpectIntEQ(wolfSSL_NoTicketTLSv12(ssl_s), WOLFSSL_SUCCESS);
1187+
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB),
1188+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1189+
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB),
1190+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1191+
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
1192+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
1193+
1194+
/* Expected behavior: server falls back to a full handshake because the
1195+
* negotiated ALPN does not match the ALPN bound to the cached session.
1196+
* Both sides should report no resumption. */
1197+
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
1198+
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
1199+
1200+
wolfSSL_SESSION_free(sess);
1201+
wolfSSL_free(ssl_c);
1202+
wolfSSL_free(ssl_s);
1203+
wolfSSL_CTX_free(ctx_c);
1204+
wolfSSL_CTX_free(ctx_s);
1205+
#endif
1206+
return EXPECT_RESULT();
1207+
}
1208+
1209+
/* TLS 1.3 PSK resumption must fall back to a full handshake if the ALPN
1210+
* protocol negotiated for the resumed connection does not match the ALPN
1211+
* bound to the original session. Mirrors
1212+
* test_tls13_session_resumption_sni_mismatch but varies ALPN instead of
1213+
* SNI. */
1214+
int test_tls13_session_resumption_alpn_mismatch(void)
1215+
{
1216+
EXPECT_DECLS;
1217+
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13) && \
1218+
defined(HAVE_ALPN) && defined(HAVE_SESSION_TICKET) && \
1219+
!defined(NO_SESSION_CACHE)
1220+
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
1221+
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
1222+
WOLFSSL_SESSION *sess = NULL;
1223+
struct test_memio_ctx test_ctx;
1224+
const char alpnA[] = "h2";
1225+
const char alpnB[] = "http/1.1";
1226+
byte readBuf[16];
1227+
1228+
/* Step 1: full TLS 1.3 handshake negotiating ALPN=h2 to obtain a
1229+
* session ticket. The negotiated ALPN is retained on ssl->extensions
1230+
* by ALPN_Select and bound to the ticket. */
1231+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1232+
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
1233+
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
1234+
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnA, (word32)XSTRLEN(alpnA),
1235+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1236+
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnA, (word32)XSTRLEN(alpnA),
1237+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1238+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
1239+
/* Sanity: the first handshake was not a resumption. */
1240+
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
1241+
/* Drive the post-handshake NewSessionTicket through to the client so
1242+
* the saved session is a real resumption ticket. */
1243+
ExpectIntEQ(wolfSSL_read(ssl_c, readBuf, sizeof(readBuf)), -1);
1244+
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
1245+
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
1246+
1247+
wolfSSL_free(ssl_c); ssl_c = NULL;
1248+
wolfSSL_free(ssl_s); ssl_s = NULL;
1249+
1250+
/* Step 2: new SSL objects on the SAME WOLFSSL_CTX (so the server's
1251+
* ticket key still matches). The client offers the saved session but
1252+
* both sides now advertise a *different* ALPN (http/1.1). The server
1253+
* MUST NOT resume because the negotiated ALPN differs from the one
1254+
* bound to the original ticket. */
1255+
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1256+
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
1257+
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
1258+
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
1259+
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
1260+
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
1261+
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
1262+
ExpectIntEQ(wolfSSL_UseALPN(ssl_c, (char*)alpnB, (word32)XSTRLEN(alpnB),
1263+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1264+
ExpectIntEQ(wolfSSL_UseALPN(ssl_s, (char*)alpnB, (word32)XSTRLEN(alpnB),
1265+
WOLFSSL_ALPN_FAILED_ON_MISMATCH), WOLFSSL_SUCCESS);
1266+
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
1267+
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
1268+
1269+
/* Expected behavior: server falls back to a full handshake because the
1270+
* negotiated ALPN does not match the ALPN bound to the cached ticket.
1271+
* Both sides should report no resumption. */
1272+
ExpectIntEQ(wolfSSL_session_reused(ssl_s), 0);
1273+
ExpectIntEQ(wolfSSL_session_reused(ssl_c), 0);
1274+
1275+
wolfSSL_SESSION_free(sess);
1276+
wolfSSL_free(ssl_c);
1277+
wolfSSL_free(ssl_s);
1278+
wolfSSL_CTX_free(ctx_c);
1279+
wolfSSL_CTX_free(ctx_s);
1280+
#endif
1281+
return EXPECT_RESULT();
1282+
}
1283+
11311284
int test_tls_set_curves_list_ecc_fallback(void)
11321285
{
11331286
EXPECT_DECLS;

tests/api/test_tls.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ int test_tls_set_session_min_downgrade(void);
3636
int test_tls12_session_id_resumption_sni_mismatch(void);
3737
int test_tls13_session_resumption_sni_mismatch(void);
3838
int test_tls13_resumption_with_alpn(void);
39+
int test_tls12_session_id_resumption_alpn_mismatch(void);
40+
int test_tls13_session_resumption_alpn_mismatch(void);
3941
int test_tls_set_curves_list_ecc_fallback(void);
4042
int test_tls12_corrupted_finished(void);
4143
int test_tls12_peerauth_failsafe(void);
@@ -57,6 +59,8 @@ int test_wolfSSL_alert_desc_string(void);
5759
TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_sni_mismatch), \
5860
TEST_DECL_GROUP("tls", test_tls13_session_resumption_sni_mismatch), \
5961
TEST_DECL_GROUP("tls", test_tls13_resumption_with_alpn), \
62+
TEST_DECL_GROUP("tls", test_tls12_session_id_resumption_alpn_mismatch),\
63+
TEST_DECL_GROUP("tls", test_tls13_session_resumption_alpn_mismatch), \
6064
TEST_DECL_GROUP("tls", test_tls_set_curves_list_ecc_fallback), \
6165
TEST_DECL_GROUP("tls", test_tls12_corrupted_finished), \
6266
TEST_DECL_GROUP("tls", test_tls12_peerauth_failsafe), \

0 commit comments

Comments
 (0)