Commit e5a18924e2 for openssl.org
commit e5a18924e261cffd7cec0fd7d131f892d196f144
Author: Daniel Kubec <kubec@openssl.foundation>
Date: Mon Mar 30 13:43:41 2026 +0200
TLS1.3: Disable tickets when SSL_OP_NO_TICKET and SSL_SESS_CACHE_OFF are set.
Do not issue TLS 1.3 session tickets if the server has explicitly disabled
them via SSL_OP_NO_TICKET and also disabled the session cache with
SSL_SESS_CACHE_OFF. Together, these settings clearly indicate an intent to
suppress session resumption; sending NewSessionTicket messages in this case
would be wasteful and misleading.
From the server’s perspective, a client that does not advertise
psk_key_exchange_modes in TLS 1.3, or that sends it with RFC 9149 parameters
such as new_session_count = 0 or resumption_count = 0, is effectively
signaling no interest in session tickets or resumption.
RFC 8446 section 4.2.9: Servers MUST NOT select a key exchange mode that is
not listed by the client. This extension also restricts the modes for use
with PSK resumption. Servers SHOULD NOT send NewSessionTicket with tickets
that are not compatible with the advertised modes; however, if a server does
so, the impact will just be that the client's attempts at resumption fail.
Fixes #8077
Signed-off-by: Daniel Kubec <kubec@openssl.foundation>
Reviewed-by: Matt Caswell <matt@openssl.foundation>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Wed May 20 15:42:00 2026
(Merged from https://github.com/openssl/openssl/pull/30639)
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index f061a47f4b..46e418676e 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -688,10 +688,41 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL_CONNECTION *s)
st->hand_state = TLS_ST_OK;
return WRITE_TRAN_CONTINUE;
}
- if (s->num_tickets > s->sent_tickets)
+ /*
+ * Do not issue TLS 1.3 session tickets if the server has explicitly
+ * disabled them via SSL_OP_NO_TICKET and also disabled the session
+ * cache with SSL_SESS_CACHE_OFF. Together, these settings clearly
+ * indicate an intent to suppress session resumption; sending
+ * NewSessionTicket messages in this case would be wasteful and
+ * misleading.
+ *
+ * From the server’s perspective, a client that does not advertise
+ * psk_key_exchange_modes in TLS 1.3, or that sends it with RFC 9149
+ * parameters such as new_session_count = 0 or resumption_count = 0, is
+ * effectively signaling no interest in session tickets or resumption.
+ *
+ * RFC 8446 section 4.2.9: Servers MUST NOT select a key exchange mode
+ * that is not listed by the client. This extension also restricts the
+ * modes for use with PSK resumption. Servers SHOULD NOT send
+ * NewSessionTicket with tickets that are not compatible with the
+ * advertised modes; however, if a server does so, the impact will just
+ * be that the client's attempts at resumption fail.
+ *
+ * Note: Although RFC 9149 allows clients to signal no interest in
+ * session tickets or resumption (e.g. new_session_count = 0 or
+ * resumption_count = 0), this implementation does not currently
+ * interpret or enforce those parameters.
+ */
+ if (((s->options & SSL_OP_NO_TICKET) != 0
+ && (SSL_CONNECTION_GET_CTX(s)->session_cache_mode & SSL_SESS_CACHE_SERVER)
+ == 0)
+ || s->ext.psk_kex_mode == TLSEXT_KEX_MODE_FLAG_NONE) {
+ st->hand_state = TLS_ST_OK;
+ } else if (s->num_tickets > s->sent_tickets) {
st->hand_state = TLS_ST_SW_SESSION_TICKET;
- else
+ } else {
st->hand_state = TLS_ST_OK;
+ }
return WRITE_TRAN_CONTINUE;
case TLS_ST_SR_KEY_UPDATE:
diff --git a/test/build.info b/test/build.info
index 955a0ace58..fdc182352e 100644
--- a/test/build.info
+++ b/test/build.info
@@ -969,7 +969,7 @@ IF[{- !$disabled{tests} -}]
INCLUDE[dsa_no_digest_size_test]=../include ../apps/include
DEPEND[dsa_no_digest_size_test]=../libcrypto.a libtestutil.a
- IF[{- !$disabled{ecx} && !$disabled{tls1_3} -}]
+ IF[{- !$disabled{ecx} && !$disabled{tls1_2} && !$disabled{tls1_3} -}]
PROGRAMS{noinst}=tls13ticket_test
SOURCE[tls13ticket_test]=tls13tickettest.c helpers/ssltestlib.c
INCLUDE[tls13ticket_test]=../include ../apps/include
diff --git a/test/recipes/70-test_tls13ticket.t b/test/recipes/70-test_tls13ticket.t
index 0fb782bd0d..5bd745d6dd 100644
--- a/test/recipes/70-test_tls13ticket.t
+++ b/test/recipes/70-test_tls13ticket.t
@@ -15,10 +15,12 @@ setup("test_tls13ticket");
plan skip_all => "needs TLSv1.3 enabled"
if disabled("tls1_3");
+plan skip_all => "needs TLSv1.2 enabled"
+ if disabled("tls1_2");
+
plan skip_all => "needs ECX enabled"
if disabled("ecx");
-
plan tests => 1;
ok(run(test(["tls13ticket_test", srctop_file("apps", "server.pem"),
diff --git a/test/tls13tickettest.c b/test/tls13tickettest.c
index 9470f41696..cbbab1c8b1 100644
--- a/test/tls13tickettest.c
+++ b/test/tls13tickettest.c
@@ -8,23 +8,69 @@
*/
#include <openssl/ssl.h>
+#include <openssl/ssl3.h>
+#include <openssl/tls1.h>
+#include "internal/packet.h"
#include "helpers/ssltestlib.h"
#include "testutil.h"
+/*
+ * Do not issue TLS 1.3 session tickets if the server has explicitly disabled
+ * them via SSL_OP_NO_TICKET and also disabled the session cache with
+ * SSL_SESS_CACHE_OFF. Together, these settings clearly indicate an intent to
+ * suppress session resumption; sending NewSessionTicket messages in this case
+ * would be wasteful and misleading.
+ *
+ * From the server’s perspective, a client that does not advertise
+ * psk_key_exchange_modes in TLS 1.3, or that sends it with RFC 9149 parameters
+ * such as new_session_count = 0 or resumption_count = 0, is effectively
+ * signaling no interest in session tickets or resumption.
+ *
+ * RFC 8446 section 4.2.9: Servers MUST NOT select a key exchange mode that is
+ * not listed by the client. This extension also restricts the modes for use
+ * with PSK resumption. Servers SHOULD NOT send NewSessionTicket with tickets
+ * that are not compatible with the advertised modes; however, if a server does
+ * so, the impact will just be that the client's attempts at resumption fail.
+ *
+ * In other words, if psk_key_exchange_modes is missing or the server doesn't
+ * recognize any of the client's advertised modes, this effectively disables
+ * both resumption and ticket issuance, since the server has no valid mode the
+ * client understands. In TLS 1.3 terms, omitting this extension is essentially
+ * a signal that the client has no interest in tickets and resumption.
+ */
+
+#ifndef CLIENT_VERSION_LEN
+/*
+ * This is the legacy version length, i.e. len(0x0303). The same
+ * label is used in e.g. test/sslapitest.c and elsewhere but not
+ * defined in a header file I could find.
+ */
+#define CLIENT_VERSION_LEN 2
+#endif
+
struct stats {
unsigned int tickets;
+ unsigned int nst_msgs;
+ unsigned int ch_has_psk;
+ unsigned int ch_has_psk_kex_modes;
+ unsigned int ch_has_session_ticket;
+ unsigned int sh_has_psk;
+ unsigned int sh_has_supported_versions;
+};
+
+struct tls13_endpoint {
+ SSL *ssl;
+ struct stats stats;
+};
+
+struct tls13_channel {
+ struct tls13_endpoint c, s;
};
static char *cert = NULL;
static char *pkey = NULL;
static int stats_idx = -1;
-static int stats_init(struct stats *stats)
-{
- memset(stats, 0, sizeof(*stats));
- return 1;
-}
-
static int sess_new_cb(SSL *ssl, SSL_SESSION *session)
{
struct stats *stats = SSL_get_ex_data(ssl, stats_idx);
@@ -59,34 +105,235 @@ static void info_cb(const SSL *ssl, int type, int val)
handshake_finished(ssl);
}
-static int set_callbacks(SSL *ssl)
+static void parse_ch_exts(const unsigned char *buf, size_t len, struct stats *x)
{
- SSL_set_info_callback(ssl, info_cb);
- return 1;
+ PACKET pkt, e, ex;
+ unsigned int v;
+
+ if (!PACKET_buf_init(&pkt, buf, len)
+ || !PACKET_forward(&pkt, 4 + 2 + 32)
+ || !PACKET_get_1(&pkt, &v)
+ || !PACKET_forward(&pkt, v)
+ || !PACKET_get_net_2(&pkt, &v)
+ || !PACKET_forward(&pkt, v)
+ || !PACKET_get_1(&pkt, &v)
+ || !PACKET_forward(&pkt, v)
+ || !PACKET_as_length_prefixed_2(&pkt, &e))
+ return;
+
+ while (PACKET_remaining(&e) > 0) {
+ if (!PACKET_get_net_2(&e, &v) || !PACKET_get_length_prefixed_2(&e, &ex))
+ return;
+ switch (v) {
+ case TLSEXT_TYPE_psk:
+ x->ch_has_psk = 1;
+ break;
+ case TLSEXT_TYPE_psk_kex_modes:
+ x->ch_has_psk_kex_modes = 1;
+ break;
+ case TLSEXT_TYPE_session_ticket:
+ x->ch_has_session_ticket = 1;
+ break;
+ }
+ }
+ TEST_info("ch extensions: psk=%d psk_kex_modes=%d session_ticket=%d",
+ x->ch_has_psk, x->ch_has_psk_kex_modes, x->ch_has_session_ticket);
}
-static int set_shutdown(SSL *c, SSL *s)
+static void parse_sh_exts(const unsigned char *buf, size_t len, struct stats *x)
{
- SSL_set_shutdown(c, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
- SSL_set_shutdown(s, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
- return 1;
+ PACKET pkt, e, ex;
+ unsigned int v;
+
+ if (!PACKET_buf_init(&pkt, buf, len)
+ || !PACKET_forward(&pkt, 4 + 2 + 32)
+ || !PACKET_get_1(&pkt, &v)
+ || !PACKET_forward(&pkt, v + 2 + 1)
+ || !PACKET_as_length_prefixed_2(&pkt, &e))
+ return;
+
+ while (PACKET_remaining(&e) > 0) {
+ if (!PACKET_get_net_2(&e, &v) || !PACKET_get_length_prefixed_2(&e, &ex))
+ return;
+ switch (v) {
+ case TLSEXT_TYPE_psk:
+ x->sh_has_psk = 1;
+ break;
+ case TLSEXT_TYPE_supported_versions:
+ x->sh_has_supported_versions = 1;
+ break;
+ }
+ }
+ TEST_info("sh extensions: psk=%d supported_versions=%d",
+ x->sh_has_psk, x->sh_has_supported_versions);
}
-static int enable_tickets(SSL_CTX *s, SSL_CTX *c)
+static void msg_cb(int write_p, int version, int content_type,
+ const void *buf, size_t len, SSL *ssl, void *arg)
{
- unsigned int cf = SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE;
- unsigned int sf = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL_STORE;
+ struct stats *stats = SSL_get_ex_data(ssl, stats_idx);
- SSL_CTX_set_session_cache_mode(s, sf);
- SSL_CTX_set_session_cache_mode(c, cf);
- SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL);
+ if (content_type == SSL3_RT_HANDSHAKE && len > 0) {
+ unsigned char mt = ((const unsigned char *)buf)[0];
+
+ if (mt == SSL3_MT_NEWSESSION_TICKET && stats != NULL)
+ stats->nst_msgs++;
+ if (mt == SSL3_MT_CLIENT_HELLO && stats != NULL)
+ parse_ch_exts(buf, len, stats);
+ if (mt == SSL3_MT_SERVER_HELLO && stats != NULL)
+ parse_sh_exts(buf, len, stats);
+ }
+}
+static int set_ctx_callbacks(SSL_CTX *c, SSL_CTX *s)
+{
SSL_CTX_sess_set_new_cb(s, sess_new_cb);
SSL_CTX_sess_set_new_cb(c, sess_new_cb);
+ SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL);
+ return 1;
+}
+
+static int tls_channel_init(SSL_CTX *c_ctx, SSL_CTX *s_ctx, struct tls13_channel *ch)
+{
+ SSL *c = NULL, *s = NULL;
+ int test;
+
+ memset(ch, 0, sizeof(*ch));
+
+ test = TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL))
+ && TEST_true(SSL_set_ex_data(c, stats_idx, &ch->c.stats))
+ && TEST_true(SSL_set_ex_data(s, stats_idx, &ch->s.stats));
+
+ if (test != 0) {
+ SSL_set_info_callback(c, info_cb);
+ SSL_set_msg_callback(c, msg_cb);
+ SSL_set_info_callback(s, info_cb);
+ SSL_set_msg_callback(s, msg_cb);
+ ch->c.ssl = c;
+ ch->s.ssl = s;
+ }
+ return test;
+}
+
+static void tls_channel_fini(struct tls13_channel *ch)
+{
+ SSL_free(ch->c.ssl);
+ SSL_free(ch->s.ssl);
+}
+
+static int tls_shutdown(struct tls13_channel *ch)
+{
+ SSL_set_shutdown(ch->c.ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+ SSL_set_shutdown(ch->s.ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
+ return 1;
+}
+
+static int ticket_enable(SSL_CTX *ctx)
+{
+ unsigned flags = SSL_SESS_CACHE_NO_INTERNAL_STORE;
+ if (SSL_CTX_is_server(ctx))
+ flags |= SSL_SESS_CACHE_SERVER;
+ else
+ flags |= SSL_SESS_CACHE_CLIENT;
+ SSL_CTX_set_session_cache_mode(ctx, flags);
+ return 1;
+}
+
+static int ticket_disable(SSL_CTX *ctx)
+{
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
return 1;
}
+/*
+ * RFC 5077 3.1: The server sends an empty SessionTicket extension to indicate
+ * that it will send a new session ticket using the NewSessionTicket handshake
+ * message.
+ */
+
+static int test_tls12_ticket_enable(void)
+{
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ struct tls13_channel resumed = { .c.ssl = NULL, .s.ssl = NULL };
+ SSL_SESSION *sess = NULL;
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_2_VERSION, TLS1_2_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(ticket_enable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_uint_eq(initial.s.stats.nst_msgs, 1)
+ && TEST_uint_eq(initial.c.stats.nst_msgs, 1)
+ && TEST_uint_eq(initial.c.stats.tickets, 1)
+ && TEST_uint_eq(initial.s.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.ch_has_psk_kex_modes, 0)
+ && TEST_uint_eq(initial.c.stats.ch_has_psk_kex_modes, 0)
+ && TEST_uint_eq(initial.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.sh_has_supported_versions, 0)
+ && TEST_uint_eq(initial.s.stats.sh_has_supported_versions, 0)
+ && TEST_ptr(sess = SSL_get1_session(initial.c.ssl))
+ && TEST_true(tls_channel_init(c, s, &resumed))
+ && TEST_true(SSL_set_session(resumed.c.ssl, sess))
+ && TEST_true(create_ssl_connection(resumed.s.ssl, resumed.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(SSL_session_reused(resumed.c.ssl))
+ && TEST_uint_eq(resumed.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.ch_has_psk_kex_modes, 0)
+ && TEST_uint_eq(resumed.c.stats.ch_has_psk_kex_modes, 0)
+ && TEST_uint_eq(resumed.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(resumed.c.stats.sh_has_supported_versions, 0)
+ && TEST_uint_eq(resumed.s.stats.sh_has_supported_versions, 0);
+
+ SSL_SESSION_free(sess);
+ tls_channel_fini(&initial);
+ tls_channel_fini(&resumed);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return test;
+}
+
+static int test_tls12_ticket_disable_server(void)
+{
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_2_VERSION, TLS1_2_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(ticket_disable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_uint_eq(initial.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(initial.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(initial.c.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.s.stats.ch_has_psk_kex_modes, 0)
+ && TEST_uint_eq(initial.c.stats.ch_has_psk_kex_modes, 0)
+ && TEST_uint_eq(initial.c.stats.sh_has_supported_versions, 0)
+ && TEST_uint_eq(initial.s.stats.sh_has_supported_versions, 0);
+
+ tls_channel_fini(&initial);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return test;
+}
+
/*
* Verify ticket regeneration after fallback to a full handshake. If session
* resumption fails due to a ciphersuite mismatch, it falls back to a full
@@ -95,45 +342,248 @@ static int enable_tickets(SSL_CTX *s, SSL_CTX *c)
*/
static int test_tls13_ticket_ciphersuite_mismatch(void)
{
- struct stats stats1, stats2;
- SSL_CTX *s_ctx = NULL, *c_ctx = NULL;
- SSL *s_ssl = NULL, *c_ssl = NULL, *s = NULL, *c = NULL;
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ struct tls13_channel resumed = { .c.ssl = NULL, .s.ssl = NULL };
+ SSL_SESSION *sess = NULL;
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(ticket_enable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(SSL_CTX_set_ciphersuites(s, "TLS_AES_128_GCM_SHA256"))
+ && TEST_true(SSL_CTX_set_ciphersuites(c, "TLS_AES_128_GCM_SHA256"))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_uint_ge(initial.c.stats.tickets, 1)
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_ptr(sess = SSL_get1_session(initial.c.ssl))
+ && TEST_true(SSL_CTX_set_ciphersuites(s, "TLS_AES_256_GCM_SHA384"))
+ && TEST_true(SSL_CTX_set_ciphersuites(c, "TLS_AES_256_GCM_SHA384"))
+ && TEST_true(tls_channel_init(c, s, &resumed))
+ && TEST_true(SSL_set_session(resumed.c.ssl, sess))
+ && TEST_true(create_ssl_connection(resumed.s.ssl, resumed.c.ssl, SSL_ERROR_NONE))
+ && TEST_false(SSL_session_reused(resumed.c.ssl))
+ && TEST_uint_eq(resumed.s.stats.tickets, 2)
+ && TEST_uint_eq(resumed.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_psk_kex_modes, 1);
+
+ SSL_SESSION_free(sess);
+ tls_channel_fini(&initial);
+ tls_channel_fini(&resumed);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return test;
+}
+
+/*
+ * The session_ticket extension (#35) is still present in the ClientHello for
+ * channels where both min and max protocol version are TLS 1.3. This is
+ * unexpected given that session_ticket (#35) is defined as
+ * TLS1_2_AND_BELOW_ONLY in OpenSSL, and therefore should not appear in a
+ * strictly TLS 1.3 handshake.
+ */
+
+static int test_tls13_ticket_enable(void)
+{
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ struct tls13_channel resumed = { .c.ssl = NULL, .s.ssl = NULL };
+ SSL_SESSION *sess = NULL;
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(ticket_enable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_uint_eq(initial.s.stats.nst_msgs, 2)
+ && TEST_uint_eq(initial.c.stats.nst_msgs, 2)
+ && TEST_uint_eq(initial.c.stats.tickets, 2)
+ && TEST_uint_eq(initial.s.stats.tickets, 2)
+ && TEST_uint_eq(initial.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_session_ticket, 1)
+ && TEST_ptr(sess = SSL_get1_session(initial.c.ssl))
+ && TEST_true(tls_channel_init(c, s, &resumed))
+ && TEST_true(SSL_set_session(resumed.c.ssl, sess))
+ && TEST_true(create_ssl_connection(resumed.s.ssl, resumed.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(SSL_session_reused(resumed.c.ssl))
+ && TEST_uint_eq(resumed.s.stats.nst_msgs, 1)
+ && TEST_uint_eq(resumed.c.stats.nst_msgs, 1)
+ && TEST_uint_eq(resumed.c.stats.tickets, 1)
+ && TEST_uint_eq(resumed.s.stats.tickets, 1)
+ && TEST_uint_eq(resumed.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.sh_has_supported_versions, 1)
+ && TEST_uint_eq(resumed.s.stats.sh_has_supported_versions, 1);
+
+ SSL_SESSION_free(sess);
+ tls_channel_fini(&initial);
+ tls_channel_fini(&resumed);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return test;
+}
+
+/*
+ * If num_tickets is set to 0, then no tickets will be issued for either
+ * a full (initial) connection or a resumed session.
+ */
+static int test_tls13_ticket_initial_set_num_tickets_zero(void)
+{
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ struct tls13_channel resumed = { .c.ssl = NULL, .s.ssl = NULL };
+ SSL_SESSION *sess = NULL;
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(SSL_CTX_set_num_tickets(s, 0))
+ && TEST_true(ticket_enable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_uint_eq(initial.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(initial.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(initial.c.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_session_ticket, 1)
+ && TEST_ptr(sess = SSL_get1_session(initial.c.ssl))
+ && TEST_true(tls_channel_init(c, s, &resumed))
+ && TEST_true(SSL_set_session(resumed.c.ssl, sess))
+ && TEST_true(create_ssl_connection(resumed.s.ssl, resumed.c.ssl, SSL_ERROR_NONE))
+ && TEST_false(SSL_session_reused(resumed.c.ssl))
+ && TEST_uint_eq(resumed.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.sh_has_supported_versions, 1)
+ && TEST_uint_eq(resumed.s.stats.sh_has_supported_versions, 1);
+
+ SSL_SESSION_free(sess);
+ tls_channel_fini(&initial);
+ tls_channel_fini(&resumed);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return test;
+}
+
+static int test_tls13_ticket_resumed_set_num_tickets_zero(void)
+{
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ struct tls13_channel resumed = { .c.ssl = NULL, .s.ssl = NULL };
+ SSL_SESSION *sess = NULL;
+ int test;
+
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(ticket_enable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_uint_eq(initial.s.stats.nst_msgs, 2)
+ && TEST_uint_eq(initial.c.stats.nst_msgs, 2)
+ && TEST_uint_eq(initial.c.stats.tickets, 2)
+ && TEST_uint_eq(initial.s.stats.tickets, 2)
+ && TEST_uint_eq(initial.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_session_ticket, 1)
+ && TEST_ptr(sess = SSL_get1_session(initial.c.ssl))
+ && TEST_true(SSL_CTX_set_num_tickets(s, 0))
+ && TEST_true(tls_channel_init(c, s, &resumed))
+ && TEST_true(SSL_set_session(resumed.c.ssl, sess))
+ && TEST_true(create_ssl_connection(resumed.s.ssl, resumed.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(SSL_session_reused(resumed.c.ssl))
+ && TEST_uint_eq(resumed.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.sh_has_supported_versions, 1)
+ && TEST_uint_eq(resumed.s.stats.sh_has_supported_versions, 1);
+
+ SSL_SESSION_free(sess);
+ tls_channel_fini(&initial);
+ tls_channel_fini(&resumed);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return test;
+}
+
+/*
+ * Do not issue TLSv1.3 session tickets if the server has explicitly disabled
+ * them via SSL_OP_NO_TICKET and also turned off the session cache with
+ * SSL_SESS_CACHE_OFF. Both conditions together indicate a clear intent to
+ * suppress resumption, so sending NewSessionTicket messages would be
+ * wasteful and misleading.
+ */
+static int test_tls13_ticket_disable_server(void)
+{
+ SSL_CTX *c = NULL, *s = NULL;
+ struct tls13_channel initial = { .c.ssl = NULL, .s.ssl = NULL };
+ struct tls13_channel resumed = { .c.ssl = NULL, .s.ssl = NULL };
SSL_SESSION *sess = NULL;
int test;
- test = create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
- TLS1_3_VERSION, TLS1_3_VERSION, &s_ctx, &c_ctx, cert, pkey)
- && TEST_true(SSL_CTX_set_ciphersuites(s_ctx, "TLS_AES_128_GCM_SHA256"))
- && TEST_true(SSL_CTX_set_ciphersuites(c_ctx, "TLS_AES_128_GCM_SHA256"))
- && TEST_true(enable_tickets(s_ctx, c_ctx))
- && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL))
- && TEST_true(set_callbacks(c))
- && TEST_true(set_callbacks(s))
- && TEST_true(stats_init(&stats1))
- && TEST_true(SSL_set_ex_data(c, stats_idx, &stats1))
- && TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE))
- && TEST_uint_eq(stats1.tickets, 2)
- && TEST_true(set_shutdown(c, s))
- && TEST_ptr(sess = SSL_get1_session(c))
- && TEST_true(SSL_CTX_set_ciphersuites(s_ctx, "TLS_AES_256_GCM_SHA384"))
- && TEST_true(SSL_CTX_set_ciphersuites(c_ctx, "TLS_AES_256_GCM_SHA384"))
- && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s_ssl, &c_ssl, NULL, NULL))
- && TEST_true(SSL_set_session(c_ssl, sess))
- && TEST_true(set_callbacks(c_ssl))
- && TEST_true(set_callbacks(s_ssl))
- && TEST_true(stats_init(&stats2))
- && TEST_true(SSL_set_ex_data(c_ssl, stats_idx, &stats2))
- && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE))
- && TEST_false(SSL_session_reused(c_ssl))
- && TEST_uint_eq(stats2.tickets, 2);
+ test = TEST_true(create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
+ TLS1_3_VERSION, TLS1_3_VERSION, &s, &c, cert, pkey))
+ && TEST_true(set_ctx_callbacks(c, s))
+ && TEST_true(ticket_disable(s))
+ && TEST_true(ticket_enable(c))
+ && TEST_true(tls_channel_init(c, s, &initial))
+ && TEST_true(create_ssl_connection(initial.s.ssl, initial.c.ssl, SSL_ERROR_NONE))
+ && TEST_true(tls_shutdown(&initial))
+ && TEST_uint_eq(initial.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(initial.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(initial.c.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.tickets, 0)
+ && TEST_uint_eq(initial.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(initial.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(initial.c.stats.sh_has_supported_versions, 1)
+ && TEST_uint_eq(initial.s.stats.sh_has_supported_versions, 1)
+ && TEST_ptr(sess = SSL_get1_session(initial.c.ssl))
+ && TEST_true(tls_channel_init(c, s, &resumed))
+ && TEST_true(SSL_set_session(resumed.c.ssl, sess))
+ && TEST_true(create_ssl_connection(resumed.s.ssl, resumed.c.ssl, SSL_ERROR_NONE))
+ && TEST_false(SSL_session_reused(resumed.c.ssl))
+ && TEST_uint_eq(resumed.s.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.nst_msgs, 0)
+ && TEST_uint_eq(resumed.c.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.tickets, 0)
+ && TEST_uint_eq(resumed.s.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_session_ticket, 1)
+ && TEST_uint_eq(resumed.s.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.ch_has_psk_kex_modes, 1)
+ && TEST_uint_eq(resumed.c.stats.sh_has_supported_versions, 1)
+ && TEST_uint_eq(resumed.s.stats.sh_has_supported_versions, 1);
SSL_SESSION_free(sess);
- SSL_free(s_ssl);
- SSL_free(c_ssl);
- SSL_free(s);
- SSL_free(c);
- SSL_CTX_free(s_ctx);
- SSL_CTX_free(c_ctx);
+ tls_channel_fini(&initial);
+ tls_channel_fini(&resumed);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
return test;
}
@@ -151,7 +601,13 @@ int setup_tests(void)
return 0;
stats_idx = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ ADD_TEST(test_tls12_ticket_enable);
+ ADD_TEST(test_tls12_ticket_disable_server);
ADD_TEST(test_tls13_ticket_ciphersuite_mismatch);
+ ADD_TEST(test_tls13_ticket_enable);
+ ADD_TEST(test_tls13_ticket_initial_set_num_tickets_zero);
+ ADD_TEST(test_tls13_ticket_resumed_set_num_tickets_zero);
+ ADD_TEST(test_tls13_ticket_disable_server);
return 1;
}