1 /*
2 * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <openssl/ssl.h>
11 #include "helpers/ssltestlib.h"
12 #include "testutil.h"
13
14 struct stats {
15 unsigned int tickets;
16 };
17
18 static char *cert = NULL;
19 static char *pkey = NULL;
20 static int stats_idx = -1;
21
stats_init(struct stats * stats)22 static int stats_init(struct stats *stats)
23 {
24 memset(stats, 0, sizeof(*stats));
25 return 1;
26 }
27
sess_new_cb(SSL * ssl,SSL_SESSION * session)28 static int sess_new_cb(SSL *ssl, SSL_SESSION *session)
29 {
30 struct stats *stats = SSL_get_ex_data(ssl, stats_idx);
31 if (stats == NULL)
32 return 0;
33 if (SSL_is_init_finished(ssl) == 0)
34 stats->tickets++;
35 return 0;
36 }
37
handshake_finished(const SSL * ssl)38 static void handshake_finished(const SSL *ssl)
39 {
40 const char *endpoint = SSL_is_server(ssl) ? "server" : "client";
41 if (SSL_session_reused(ssl))
42 TEST_info("%s: Abbreviated handshake finished", endpoint);
43 else
44 TEST_info("%s: Full handshake finished", endpoint);
45 }
46
info_cb(const SSL * ssl,int type,int val)47 static void info_cb(const SSL *ssl, int type, int val)
48 {
49 const char *endpoint = SSL_is_server(ssl) ? "server" : "client";
50
51 if (type & SSL_CB_ALERT) {
52 const char *dir = (type & SSL_CB_READ) ? "read" : "write";
53
54 TEST_info("%s: alert %s: %s : %s", endpoint, dir,
55 SSL_alert_type_string_long(val),
56 SSL_alert_desc_string_long(val));
57 }
58 if (type & SSL_CB_HANDSHAKE_DONE)
59 handshake_finished(ssl);
60 }
61
set_callbacks(SSL * ssl)62 static int set_callbacks(SSL *ssl)
63 {
64 SSL_set_info_callback(ssl, info_cb);
65 return 1;
66 }
67
set_shutdown(SSL * c,SSL * s)68 static int set_shutdown(SSL *c, SSL *s)
69 {
70 SSL_set_shutdown(c, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
71 SSL_set_shutdown(s, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN);
72 return 1;
73 }
74
enable_tickets(SSL_CTX * s,SSL_CTX * c)75 static int enable_tickets(SSL_CTX *s, SSL_CTX *c)
76 {
77 unsigned int cf = SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL_STORE;
78 unsigned int sf = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL_STORE;
79
80 SSL_CTX_set_session_cache_mode(s, sf);
81 SSL_CTX_set_session_cache_mode(c, cf);
82 SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL);
83
84 SSL_CTX_sess_set_new_cb(s, sess_new_cb);
85 SSL_CTX_sess_set_new_cb(c, sess_new_cb);
86
87 return 1;
88 }
89
90 /*
91 * Verify ticket regeneration after fallback to a full handshake. If session
92 * resumption fails due to a ciphersuite mismatch, it falls back to a full
93 * handshake. In that case, ensure a new session ticket is issued reflecting the
94 * negotiated ciphersuite.
95 */
test_tls13_ticket_ciphersuite_mismatch(void)96 static int test_tls13_ticket_ciphersuite_mismatch(void)
97 {
98 struct stats stats1, stats2;
99 SSL_CTX *s_ctx = NULL, *c_ctx = NULL;
100 SSL *s_ssl = NULL, *c_ssl = NULL, *s = NULL, *c = NULL;
101 SSL_SESSION *sess = NULL;
102 int test;
103
104 test = create_ssl_ctx_pair(NULL, TLS_server_method(), TLS_client_method(),
105 TLS1_3_VERSION, TLS1_3_VERSION, &s_ctx, &c_ctx, cert, pkey)
106 && TEST_true(SSL_CTX_set_ciphersuites(s_ctx, "TLS_AES_128_GCM_SHA256"))
107 && TEST_true(SSL_CTX_set_ciphersuites(c_ctx, "TLS_AES_128_GCM_SHA256"))
108 && TEST_true(enable_tickets(s_ctx, c_ctx))
109 && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s, &c, NULL, NULL))
110 && TEST_true(set_callbacks(c))
111 && TEST_true(set_callbacks(s))
112 && TEST_true(stats_init(&stats1))
113 && TEST_true(SSL_set_ex_data(c, stats_idx, &stats1))
114 && TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE))
115 && TEST_uint_eq(stats1.tickets, 2)
116 && TEST_true(set_shutdown(c, s))
117 && TEST_ptr(sess = SSL_get1_session(c))
118 && TEST_true(SSL_CTX_set_ciphersuites(s_ctx, "TLS_AES_256_GCM_SHA384"))
119 && TEST_true(SSL_CTX_set_ciphersuites(c_ctx, "TLS_AES_256_GCM_SHA384"))
120 && TEST_true(create_ssl_objects(s_ctx, c_ctx, &s_ssl, &c_ssl, NULL, NULL))
121 && TEST_true(SSL_set_session(c_ssl, sess))
122 && TEST_true(set_callbacks(c_ssl))
123 && TEST_true(set_callbacks(s_ssl))
124 && TEST_true(stats_init(&stats2))
125 && TEST_true(SSL_set_ex_data(c_ssl, stats_idx, &stats2))
126 && TEST_true(create_ssl_connection(s_ssl, c_ssl, SSL_ERROR_NONE))
127 && TEST_false(SSL_session_reused(c_ssl))
128 && TEST_uint_eq(stats2.tickets, 2);
129
130 SSL_SESSION_free(sess);
131 SSL_free(s_ssl);
132 SSL_free(c_ssl);
133 SSL_free(s);
134 SSL_free(c);
135 SSL_CTX_free(s_ctx);
136 SSL_CTX_free(c_ctx);
137 return test;
138 }
139
140 OPT_TEST_DECLARE_USAGE("\n")
141
setup_tests(void)142 int setup_tests(void)
143 {
144 if (!test_skip_common_options()) {
145 TEST_error("Error parsing test options\n");
146 return 0;
147 }
148
149 if (!TEST_ptr(cert = test_get_argument(0))
150 || !TEST_ptr(pkey = test_get_argument(1)))
151 return 0;
152
153 stats_idx = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
154 ADD_TEST(test_tls13_ticket_ciphersuite_mismatch);
155
156 return 1;
157 }
158