Merge pull request #10289 from julek-wolfssl/zd/21652

TLS 1.3: gate 0-RTT on a cache-backed resumption ticket
This commit is contained in:
David Garske
2026-05-05 12:46:26 -07:00
committed by GitHub
11 changed files with 354 additions and 30 deletions
+1
View File
@@ -754,6 +754,7 @@ WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS
WOLFSSL_DTLS_RESEND_ONLY_TIMEOUT
WOLFSSL_DUMP_MEMIO_STREAM
WOLFSSL_DUP_CERTPOL
WOLFSSL_EARLY_DATA_NO_ANTI_REPLAY
WOLFSSL_ECC_BLIND_K
WOLFSSL_ECC_GEN_REJECT_SAMPLING
WOLFSSL_ECC_NO_SMALL_STACK
+4
View File
@@ -14454,6 +14454,10 @@ wolfSSL_accept_TLSv13(WOLFSSL* ssl);
A server value of zero indicates no early data is to be sent by client using
session tickets. A client value of zero indicates that the client will
not send any early data.
The default value is zero: per RFC 8446 Appendix E.5, TLS implementations
"MUST NOT enable 0-RTT (either sending or accepting) unless specifically
requested by the application." Servers must call this function (or the
per-SSL equivalent) with a non-zero value to opt in.
It is recommended that the number of early data bytes be kept as low as
practically possible in the application.
+8 -6
View File
@@ -602,21 +602,23 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519,
#endif /* WOLFSSL_TLS13 && HAVE_SUPPORTED_CURVES */
#ifdef WOLFSSL_EARLY_DATA
static void EarlyData(WOLFSSL_CTX* ctx, WOLFSSL* ssl, const char* msg,
int msgSz, char* buffer)
static int EarlyData(WOLFSSL_CTX* ctx, WOLFSSL* ssl, const char* msg,
int msgSz, char* buffer)
{
int err;
int ret;
(void)ctx;
(void)buffer;
WOLFSSL_ASYNC_WHILE_PENDING(ret = wolfSSL_write_early_data(ssl, msg, msgSz, &msgSz),
ret <= 0);
if (ret != msgSz) {
err = wolfSSL_get_error(ssl, ret);
LOG_ERROR("SSL_write_early_data msg error %d, %s\n", err,
wolfSSL_ERR_error_string((unsigned long)err, buffer));
wolfSSL_free(ssl); ssl = NULL;
wolfSSL_CTX_free(ctx); ctx = NULL;
err_sys("SSL_write_early_data failed");
wolfSSL_ERR_error_string((unsigned long)err, buffer));
return -1;
}
return 0;
}
#endif
+4
View File
@@ -2848,6 +2848,10 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
err_sys_ex(catastrophic, "can't set minimum downgrade version");
}
#ifdef WOLFSSL_EARLY_DATA
if (earlyData)
wolfSSL_CTX_set_max_early_data(ctx, 4096);
#endif
#ifdef OPENSSL_COMPATIBLE_DEFAULTS
/* Restore wolfSSL verify defaults */
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_DEFAULT, NULL);
+3 -1
View File
@@ -2842,7 +2842,9 @@ int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap)
#endif
#ifdef WOLFSSL_EARLY_DATA
ctx->maxEarlyDataSz = MAX_EARLY_DATA_SZ;
/* RFC 8446 section E.5: 0-RTT off by default; opt in via
* wolfSSL_CTX_set_max_early_data(). */
ctx->maxEarlyDataSz = 0;
#endif
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
+9 -5
View File
@@ -3255,8 +3255,12 @@ static void SESSION_ex_data_cache_update(WOLFSSL_SESSION* session, int idx,
#endif
#ifndef NO_SESSION_CACHE
/* OpenSSL-compatible return: 1 if the session was found and removed from the
* internal cache, or if the external remove callback (rem_sess_cb) was
* invoked. 0 if neither applied (not present, or null arguments). */
int wolfSSL_SSL_CTX_remove_session(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *s)
{
int found = 0;
#if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA)
int rem_called = FALSE;
#endif
@@ -3265,7 +3269,7 @@ int wolfSSL_SSL_CTX_remove_session(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *s)
s = ClientSessionToSession(s);
if (ctx == NULL || s == NULL)
return BAD_FUNC_ARG;
return 0;
#ifdef HAVE_EXT_CACHE
if (!ctx->internalCacheOff)
@@ -3282,6 +3286,7 @@ int wolfSSL_SSL_CTX_remove_session(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *s)
ret = TlsSessionCacheGetAndWrLock(id, &sess, &row, ctx->method->side);
if (ret == 0 && sess != NULL) {
found = 1;
#if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA)
if (sess->rem_sess_cb != NULL) {
rem_called = TRUE;
@@ -3320,13 +3325,12 @@ int wolfSSL_SSL_CTX_remove_session(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *s)
#if defined(HAVE_EXT_CACHE) || defined(HAVE_EX_DATA)
if (ctx->rem_sess_cb != NULL && !rem_called) {
ctx->rem_sess_cb(ctx, s);
/* Assume the external cache had the session. */
found = 1;
}
#endif
/* s cannot be resumed at this point */
s->timeout = 0;
return 0;
return found;
}
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) \
+30 -14
View File
@@ -61,6 +61,7 @@
*
* TLS 1.3 Session Tickets:
* WOLFSSL_TICKET_HAVE_ID: Session tickets include ID default: off
* Forced on when WOLFSSL_EARLY_DATA is set.
* WOLFSSL_TICKET_NONCE_MALLOC: Dynamically allocate ticket nonce default: off
*
* TLS 1.3 Key Exchange:
@@ -81,6 +82,14 @@
#if !defined(NO_TLS) && defined(WOLFSSL_TLS13)
/* 0-RTT anti-replay eviction needs the session cache. */
#if defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET) && \
defined(NO_SESSION_CACHE) && !defined(NO_WOLFSSL_SERVER) && \
!defined(WOLFSSL_EARLY_DATA_NO_ANTI_REPLAY)
#error "WOLFSSL_EARLY_DATA with tickets requires !NO_SESSION_CACHE, or " \
"define WOLFSSL_EARLY_DATA_NO_ANTI_REPLAY to opt out."
#endif
#ifndef WOLFCRYPT_ONLY
#ifdef HAVE_ERRNO_H
@@ -5942,8 +5951,11 @@ static int DoTls13EncryptedExtensions(WOLFSSL* ssl, const byte* input,
#ifdef WOLFSSL_EARLY_DATA
if (ssl->earlyData != no_early_data) {
TLSX* ext = TLSX_Find(ssl->extensions, TLSX_EARLY_DATA);
if (ext == NULL || !ext->val)
if (ext == NULL || !ext->val) {
WOLFSSL_MSG("Early data rejected by server (no early_data "
"EncryptedExtensions response)");
ssl->earlyData = no_early_data;
}
}
if (ssl->earlyData == no_early_data) {
@@ -6427,18 +6439,6 @@ static int DoPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 inputSz,
/* This PSK works, no need to try any more. */
current->chosen = 1;
ext->resp = 1;
#if defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET) && \
!defined(NO_SESSION_CACHE)
/* RFC 8446 section 8: accept 0-RTT for a given handshake at most
* once. Evict the session from both the internal cache (under a
* write lock) and any external cache (via ctx->rem_sess_cb) so
* the same ClientHello cannot replay early data. Only when the
* client offered 0-RTT on a session that permits it. */
if (ssl->earlyData != no_early_data &&
ssl->session->maxEarlyDataSz != 0) {
(void)wolfSSL_SSL_CTX_remove_session(ssl->ctx, ssl->session);
}
#endif
break;
}
@@ -6599,8 +6599,16 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
* RFC 8773bis: early_data is not compatible with
* cert_with_extern_psk, so skip key derivation in that case. */
if (ssl->earlyData != no_early_data && first
&& ssl->options.maxEarlyDataSz > 0
#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK
&& !hasCertWithExternPsk
#endif
#if defined(HAVE_SESSION_TICKET) && !defined(NO_SESSION_CACHE)
/* RFC 8446 section 8: evict the session from the cache.
* Accept 0-RTT only when the eviction found the entry
* (single-use). */
&& wolfSSL_SSL_CTX_remove_session(ssl->ctx, ssl->session)
== 1
#endif
) {
extEarlyData->resp = 1;
@@ -6663,6 +6671,8 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
* combination in the ClientHello, but clear the response flag
* here as a defense-in-depth measure. */
if (extEarlyData != NULL) {
WOLFSSL_MSG("Rejecting early data: "
"cert_with_extern_psk is not 0-RTT compatible");
extEarlyData->resp = 0;
ssl->earlyData = no_early_data;
}
@@ -15493,7 +15503,8 @@ int wolfSSL_accept_TLSv13(WOLFSSL* ssl)
#ifdef HAVE_SESSION_TICKET
#ifdef WOLFSSL_TLS13_TICKET_BEFORE_FINISHED
if (!ssl->options.verifyPeer && !ssl->options.noTicketTls13 &&
ssl->ctx->ticketEncCb != NULL) {
ssl->ctx->ticketEncCb != NULL &&
ssl->options.maxTicketTls13 > 0) {
if ((ssl->error = SendTls13NewSessionTicket(ssl)) != 0) {
WOLFSSL_ERROR(ssl->error);
return WOLFSSL_FATAL_ERROR;
@@ -15634,6 +15645,11 @@ int wolfSSL_send_SessionTicket(WOLFSSL* ssl)
* A value of zero indicates no early data is to be sent by client using session
* tickets.
*
* The default value is zero: per RFC 8446 Appendix E.5, TLS implementations
* "MUST NOT enable 0-RTT (either sending or accepting) unless specifically
* requested by the application." Servers must explicitly opt in by calling
* this function (or the per-SSL equivalent) with a non-zero value.
*
* ctx The SSL/TLS CTX object.
* sz Maximum size of the early data.
* returns BAD_FUNC_ARG when ctx is NULL, SIDE_ERROR when not a server and
+6 -4
View File
@@ -19613,8 +19613,9 @@ static int test_wolfSSL_CTX_sess_set_remove_cb(void)
/* Force a cache update */
ExpectNotNull(SSL_SESSION_set_ex_data(clientSess, serverSessRemIdx - 1, 0));
/* This should set the timeout to 0 and call the remove callback from within
* the session cache. */
ExpectIntEQ(SSL_CTX_remove_session(clientSessCtx, clientSess), 0);
* the session cache. Returns 1 per OpenSSL semantics (session was
* present in the cache and removed). */
ExpectIntEQ(SSL_CTX_remove_session(clientSessCtx, clientSess), 1);
ExpectNull(SSL_SESSION_get_ex_data(clientSess, serverSessRemIdx));
ExpectIntEQ(clientSessRemCountFree, 1);
#endif
@@ -19626,8 +19627,9 @@ static int test_wolfSSL_CTX_sess_set_remove_cb(void)
/* Force a cache update */
ExpectNotNull(SSL_SESSION_set_ex_data(serverSess, serverSessRemIdx - 1, 0));
/* This should set the timeout to 0 and call the remove callback from within
* the session cache. */
ExpectIntEQ(SSL_CTX_remove_session(serverSessCtx, serverSess), 0);
* the session cache. Returns 1 per OpenSSL semantics (session was
* present in the cache and removed). */
ExpectIntEQ(SSL_CTX_remove_session(serverSessCtx, serverSess), 1);
ExpectNull(SSL_SESSION_get_ex_data(serverSess, serverSessRemIdx));
ExpectIntEQ(serverSessRemCountFree, 1);
/* Need to free the references that we kept */
+275
View File
@@ -2926,6 +2926,11 @@ int test_tls13_early_data(void)
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
&ssl_s, params[i].client_meth, params[i].server_meth), 0);
/* Opt the server into 0-RTT (off by default per RFC 8446 E.5). */
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ),
0);
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
if (params[i].isUdp) {
/* Early data is incompatible with HRR usage. Hence, we have to make
* sure a group is negotiated that does not cause a fragemented CH.
@@ -3298,6 +3303,276 @@ int test_tls13_early_data_0rtt_replay(void)
}
#endif
/* Verify that maxEarlyDataSz defaults to 0 (RFC 8446 E.5): a server that
* has not called wolfSSL_set_max_early_data must not advertise 0-RTT in its
* NewSessionTicket. Fails without the ctx->maxEarlyDataSz=0 default fix
* because the old default was MAX_EARLY_DATA_SZ (4096). */
int test_tls13_0rtt_default_off(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
defined(HAVE_SESSION_TICKET) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
char buf[64];
int written = 0;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
/* Step 1: handshake WITHOUT opting into 0-RTT on the server. */
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
0);
/* Deliberately do NOT call wolfSSL_set_max_early_data. */
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Consume NewSessionTicket. */
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -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: resume - early data write must fail because the ticket
* was issued without max_early_data_size. Without the default-to-0
* fix the old default (4096) would let this succeed. */
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_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_write_early_data(ssl_c, "test", 4, &written),
WOLFSSL_FATAL_ERROR);
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();
}
/* Verify that a stateless self-encrypted ticket can carry 0-RTT exactly
* once: the first resumption succeeds with early data, the second (replay)
* refuses it because wolfSSL_SSL_CTX_remove_session evicted the cache entry.
* Fails without the WOLFSSL_TICKET_HAVE_ID implication + the
* remove_session-based gate because the old code either never populated
* the cache for stateless tickets or never checked the return value. */
int test_tls13_0rtt_stateless_replay(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
!defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
char buf[64];
int round;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
/* Step 1: full handshake to get a stateless ticket with 0-RTT. */
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
0);
/* Do NOT set WOLFSSL_OP_NO_TICKET - keep stateless tickets. */
ExpectIntGE(wolfSSL_CTX_set_max_early_data(ctx_s, MAX_EARLY_DATA_SZ), 0);
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
ExpectNotNull(sess = wolfSSL_get1_session(ssl_c));
ExpectIntEQ(wolfSSL_SessionIsSetup(sess), 1);
wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
/* Suppress ticket reissuance on resume so the eviction from round 0
* is not undone by AddSession from a new NewSessionTicket. */
ExpectIntEQ(wolfSSL_CTX_set_num_tickets(ctx_s, 0), WOLFSSL_SUCCESS);
/* Step 2: resume twice. Round 0 = first use, round 1 = replay. */
for (round = 0; round < 2 && !EXPECT_FAIL(); round++) {
const char earlyMsg[] = "stateless-0rtt";
int written = 0;
int earlyRead = 0;
char earlyBuf[sizeof(earlyMsg)];
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
XMEMSET(earlyBuf, 0, sizeof(earlyBuf));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c,
&ssl_s, wolfTLSv1_3_client_method,
wolfTLSv1_3_server_method), 0);
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
ExpectIntEQ(wolfSSL_set_session(ssl_c, sess), WOLFSSL_SUCCESS);
ExpectIntEQ(test_tls13_early_data_write_until_write_ok(ssl_c,
earlyMsg, (int)sizeof(earlyMsg), &written),
sizeof(earlyMsg));
ExpectIntEQ(written, sizeof(earlyMsg));
(void)test_tls13_early_data_read_until_write_ok(ssl_s, earlyBuf,
sizeof(earlyBuf), &earlyRead);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
if (round == 0) {
/* First use: 0-RTT accepted. */
ExpectIntEQ(earlyRead, sizeof(earlyMsg));
ExpectStrEQ(earlyMsg, earlyBuf);
}
else {
/* Replay: 0-RTT refused, handshake still completes (1-RTT). */
ExpectIntEQ(earlyRead, 0);
}
wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
}
wolfSSL_SESSION_free(sess);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
/* Verify wolfSSL_SSL_CTX_remove_session returns OpenSSL-compatible values:
* 1 when the session was in the cache and removed, 0 otherwise.
* Fails without the return-value fix because the old code returned 0/
* BAD_FUNC_ARG. */
int test_tls13_remove_session_return(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \
defined(WOLFSSL_TICKET_HAVE_ID) && !defined(NO_SESSION_CACHE) && \
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
char buf[64];
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(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Consume NewSessionTicket so the cache is populated (AddSession fires
* because WOLFSSL_TICKET_HAVE_ID is defined). */
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
ExpectNotNull(sess = wolfSSL_get1_session(ssl_s));
/* Session is in the cache - first remove returns 1. */
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, sess), 1);
/* Already removed - second remove returns 0. */
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, sess), 0);
/* NULL args - returns 0 (not BAD_FUNC_ARG). */
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(NULL, sess), 0);
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, NULL), 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();
}
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE)
/* Thin external cache: only tracks rem_calls to verify that
* wolfSSL_SSL_CTX_remove_session counts the callback as "found". */
static int test_0rtt_ext_only_rem_calls;
static int test_0rtt_ext_only_new_cb(WOLFSSL* ssl, WOLFSSL_SESSION* s)
{
(void)ssl; (void)s;
return 0; /* don't retain */
}
static WOLFSSL_SESSION* test_0rtt_ext_only_get_cb(WOLFSSL* ssl,
const byte* id, int idLen, int* ref)
{
(void)ssl; (void)id; (void)idLen;
*ref = 0;
return NULL;
}
static void test_0rtt_ext_only_rem_cb(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* s)
{
(void)ctx; (void)s;
test_0rtt_ext_only_rem_calls++;
}
#endif
/* Verify that when the internal cache is off but an external cache callback
* is registered, wolfSSL_SSL_CTX_remove_session returns 1 (the ext callback
* fired, so we assume the session was present). Fails without the fix
* because the old code only set found=1 on an internal-cache hit. */
int test_tls13_0rtt_ext_cache_eviction(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(WOLFSSL_TLS13) && defined(WOLFSSL_EARLY_DATA) && \
defined(HAVE_SESSION_TICKET) && defined(WOLFSSL_TICKET_HAVE_ID) && \
!defined(NO_SESSION_CACHE) && defined(HAVE_EXT_CACHE) && \
!defined(WOLFSSL_NO_DEF_TICKET_ENC_CB)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
WOLFSSL_SESSION *sess = NULL;
char buf[64];
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
test_0rtt_ext_only_rem_calls = 0;
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method),
0);
/* Turn off internal cache; rely on external callbacks only. */
ExpectIntEQ(wolfSSL_CTX_set_session_cache_mode(ctx_s,
WOLFSSL_SESS_CACHE_NO_INTERNAL), WOLFSSL_SUCCESS);
wolfSSL_CTX_sess_set_new_cb(ctx_s, test_0rtt_ext_only_new_cb);
wolfSSL_CTX_sess_set_get_cb(ctx_s, test_0rtt_ext_only_get_cb);
wolfSSL_CTX_sess_set_remove_cb(ctx_s, test_0rtt_ext_only_rem_cb);
ExpectTrue(wolfSSL_set_options(ssl_s, WOLFSSL_OP_NO_TICKET) != 0);
ExpectIntGE(wolfSSL_set_max_early_data(ssl_s, MAX_EARLY_DATA_SZ), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* remove_session on an ext-cache-only server: rem_cb should fire and
* the function should return 1 (assumes the ext cache had it). */
ExpectNotNull(sess = wolfSSL_get1_session(ssl_s));
ExpectIntEQ(wolfSSL_SSL_CTX_remove_session(ctx_s, sess), 1);
ExpectIntGT(test_0rtt_ext_only_rem_calls, 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();
}
/* Check that the client won't send the same CH after a HRR. An HRR without
* a KeyShare or a Cookie extension will trigger the error. */
+8
View File
@@ -50,6 +50,10 @@ int test_tls13_pqc_hybrid_malformed_ecdh(void);
int test_tls13_empty_record_limit(void);
int test_tls13_short_session_ticket(void);
int test_tls13_early_data_0rtt_replay(void);
int test_tls13_0rtt_default_off(void);
int test_tls13_0rtt_stateless_replay(void);
int test_tls13_remove_session_return(void);
int test_tls13_0rtt_ext_cache_eviction(void);
int test_tls13_corrupted_finished(void);
int test_tls13_peerauth_failsafe(void);
int test_tls13_hrr_bad_cookie(void);
@@ -89,6 +93,10 @@ int test_tls13_cert_with_extern_psk_sh_confirms_resumption(void);
TEST_DECL_GROUP("tls13", test_tls13_empty_record_limit), \
TEST_DECL_GROUP("tls13", test_tls13_short_session_ticket), \
TEST_DECL_GROUP("tls13", test_tls13_early_data_0rtt_replay), \
TEST_DECL_GROUP("tls13", test_tls13_0rtt_default_off), \
TEST_DECL_GROUP("tls13", test_tls13_0rtt_stateless_replay), \
TEST_DECL_GROUP("tls13", test_tls13_remove_session_return), \
TEST_DECL_GROUP("tls13", test_tls13_0rtt_ext_cache_eviction), \
TEST_DECL_GROUP("tls13", test_tls13_unknown_ext_rejected), \
TEST_DECL_GROUP("tls13", test_tls13_corrupted_finished), \
TEST_DECL_GROUP("tls13", test_tls13_peerauth_failsafe), \
+6
View File
@@ -1288,6 +1288,12 @@ enum {
#define MAX_EARLY_DATA_SZ 4096
#endif
/* Anti-replay eviction keys off the ticket's session ID. */
#if defined(WOLFSSL_EARLY_DATA) && defined(HAVE_SESSION_TICKET) && \
!defined(WOLFSSL_TICKET_HAVE_ID)
#define WOLFSSL_TICKET_HAVE_ID
#endif
#if !defined(NO_RSA) || !defined(NO_DH) || defined(HAVE_ECC)
/* MySQL wants to be able to use 8192-bit numbers. */