xref: /freebsd/crypto/openssl/test/tls13tickettest.c (revision 1523ccfd9c8c254f7928143d31c305384b05fd11)
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 
22 static int stats_init(struct stats *stats)
23 {
24     memset(stats, 0, sizeof(*stats));
25     return 1;
26 }
27 
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 
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 
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 
62 static int set_callbacks(SSL *ssl)
63 {
64     SSL_set_info_callback(ssl, info_cb);
65     return 1;
66 }
67 
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 
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  */
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 
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