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) - st->hand_state = TLS_ST_SW_SESSION_TICKET; - else + /* + * 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 { + 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 +#include +#include +#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 = 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_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); - 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; +} + +/* + * 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 = 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); + 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; }