Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions tests/api/test_dtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -6276,3 +6276,122 @@ int test_wolfSSL_set_secret(void)
return EXPECT_RESULT();
}

/* Control for test_dtls13_server_ctx_reuse_ticket: identical, except the server
* CTX has TLS1.3 session tickets disabled (wolfSSL_CTX_no_ticket_TLSv13). Both
* handshakes then succeed — proving the failure below is caused specifically by
* server-issued session tickets tainting the reused CTX. */
int test_dtls13_server_ctx_reuse_no_ticket_ok(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
&& defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM) \
&& defined(WOLFSSL_PQC_HYBRIDS) && defined(HAVE_SESSION_TICKET)
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
WOLFSSL *ssl_s = NULL;
struct test_memio_ctx test_ctx;
int group = WOLFSSL_X25519MLKEM768;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
/* The only difference from the failing test: suppress server tickets. */
ExpectIntEQ(wolfSSL_CTX_no_ticket_TLSv13(ctx_s), 0);
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, group), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_dtls13_allow_ch_frag(ssl_s, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
wolfSSL_free(ssl_c);
ssl_c = NULL;
wolfSSL_free(ssl_s);
ssl_s = NULL;

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, group), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_dtls13_allow_ch_frag(ssl_s, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}

/* Repro: a single DTLS 1.3 server WOLFSSL_CTX reused across two successive
* handshakes (each with a large hybrid PQ key share, so the ClientHello
* fragments). Handshake #1 succeeds and the server issues a TLS1.3
* NewSessionTicket; handshake #2 on the SAME server CTX is then rejected by the
* server at its Finished step. Both handshakes must succeed. */
int test_dtls13_server_ctx_reuse_ticket(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
&& defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM) \
&& defined(WOLFSSL_PQC_HYBRIDS) && defined(HAVE_SESSION_TICKET)
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
WOLFSSL *ssl_s = NULL;
struct test_memio_ctx test_ctx;
int group = WOLFSSL_X25519MLKEM768;
const char *msg = "test";
int msgSz = (int)XSTRLEN(msg) + 1;
byte buf[64];

/* Handshake #1 — fresh contexts. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
/* Set hint > WOLFSSL_TICKET_KEY_LIFETIME / 2 to trigger the bug in
* TicketEncCbCtx_ChooseKey: both keys become alive but neither covers
* the hint window, returning BAD_STATE_E on the 2nd handshake. */
ExpectIntEQ(wolfSSL_CTX_set_TicketHint(ctx_s,
WOLFSSL_TICKET_KEY_LIFETIME + 1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, group), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_dtls13_allow_ch_frag(ssl_s, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Exchange app data so the server flushes its post-handshake
* NewSessionTicket and the client consumes it. */
ExpectIntEQ(wolfSSL_write(ssl_c, msg, msgSz), msgSz);
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), msgSz);
ExpectIntEQ(wolfSSL_write(ssl_s, msg, msgSz), msgSz);
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), msgSz);

wolfSSL_free(ssl_c);
ssl_c = NULL;
wolfSSL_free(ssl_s);
ssl_s = NULL;

/* Handshake #2 — REUSE the same server (and client) CTX, fresh transport. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectNotNull(ssl_c = wolfSSL_new(ctx_c));
ExpectNotNull(ssl_s = wolfSSL_new(ctx_s));
wolfSSL_SetIOWriteCtx(ssl_c, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_c, &test_ctx);
wolfSSL_SetIOWriteCtx(ssl_s, &test_ctx);
wolfSSL_SetIOReadCtx(ssl_s, &test_ctx);
ExpectIntEQ(wolfSSL_set_groups(ssl_c, &group, 1), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseKeyShare(ssl_c, group), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_dtls13_allow_ch_frag(ssl_s, 1), WOLFSSL_SUCCESS);
/* The bug: this returns != 0 because the server rejects its own
* handshake at the Finished step on the reused, ticket-tainted CTX. */
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
4 changes: 4 additions & 0 deletions tests/api/test_dtls.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ int test_dtls12_record_length_mismatch(void);
int test_dtls12_short_read(void);
int test_dtls13_longer_length(void);
int test_dtls13_short_read(void);
int test_dtls13_server_ctx_reuse_no_ticket_ok(void);
int test_dtls13_server_ctx_reuse_ticket(void);
int test_records_span_network_boundaries(void);
int test_dtls_record_cross_boundaries(void);
int test_dtls_rtx_across_epoch_change(void);
Expand Down Expand Up @@ -161,6 +163,8 @@ int test_WOLFSSL_dtls_version_alert(void);
TEST_DECL_GROUP("dtls", test_dtls13_min_rtx_interval), \
TEST_DECL_GROUP("dtls", test_dtls13_no_session_id_echo), \
TEST_DECL_GROUP("dtls", test_dtls13_oversized_cert_chain), \
TEST_DECL_GROUP("dtls", test_dtls13_server_ctx_reuse_no_ticket_ok), \
TEST_DECL_GROUP("dtls", test_dtls13_server_ctx_reuse_ticket), \
TEST_DECL_GROUP("dtls", test_dtls_set_session_min_downgrade), \
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_create_free_peer), \
TEST_DECL_GROUP("dtls", test_wolfSSL_dtls_get0_peer), \
Expand Down