Commit ef27df1343 for openssl.org
commit ef27df134327a62ff40985c6f0c0438cbc802de2
Author: Daniel Kubec <kubec@openssl.foundation>
Date: Thu Oct 8 13:56:29 2026 +0200
TLS: Generate the server random before processing ClientHello extensions
The server random was only generated after all of the ClientHello extensions had
been processed. In (D)TLSv1.3 the PSK callbacks are invoked while the
pre_shared_key extension is being parsed, so a call to SSL_get_server_random()
from within them returned all zeros. The TLSv1.2 psk_server_callback runs during
ClientKeyExchange processing and so already observed the real value, making the
behaviour inconsistent between protocol versions.
Move the generation of the server random to immediately after the protocol
version has been selected and before any of the remaining ClientHello
processing. As a result the server random is also visible from the other
callbacks invoked during ClientHello processing, such as the session ticket and
servername callbacks.
Fixes #26404
Assisted-by: Claude:claude-fable-5-1
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
Merge-date: Wed Oct 21 16:45:34 2026
Merged-from: https://github.com/openssl/openssl/pull/33102
diff --git a/doc/man3/SSL_CTX_set_client_hello_cb.pod b/doc/man3/SSL_CTX_set_client_hello_cb.pod
index 820c0261ce..99942f0858 100644
--- a/doc/man3/SSL_CTX_set_client_hello_cb.pod
+++ b/doc/man3/SSL_CTX_set_client_hello_cb.pod
@@ -107,6 +107,14 @@ resumption and the historical servername callback.
The SSL_client_hello_* family of functions may only be called from code
executing within a ClientHello callback.
+The ClientHello callback runs before the protocol version has been selected,
+and may itself change the set of supported versions. As the server_random
+depends on the selected version, it has not been generated yet when the
+callback runs, so L<SSL_get_server_random(3)> does not return the value of the
+current handshake from within it. Callbacks invoked later in ClientHello
+processing, such as the session ticket, PSK and servername callbacks, can
+observe it.
+
The SSL_client_hello_get0_*() functions return raw ClientHello data, whereas
SSL_client_hello_get1_extensions_present() returns only recognized extensions
(so unknown/GREASE-extensions are not included).
diff --git a/doc/man3/SSL_get_client_random.pod b/doc/man3/SSL_get_client_random.pod
index 6e92a51824..99bd7f62ca 100644
--- a/doc/man3/SSL_get_client_random.pod
+++ b/doc/man3/SSL_get_client_random.pod
@@ -74,6 +74,52 @@ Finally, though the "client_random" and "server_random" values are called
"random", many TLS implementations will generate four bytes of those
values based on their view of the current time.
+On the server side the server_random value is generated during ClientHello
+processing, after the protocol version has been selected and the ClientHello
+has passed its initial checks (such as DTLS cookie verification and cipher
+list validation), but before the session is looked up. It is therefore
+available to callbacks invoked from that point on, such as the session ticket
+callbacks, the servername callback and the PSK callbacks (see
+L<SSL_CTX_set_psk_find_session_callback(3)> and
+L<SSL_CTX_set_psk_server_callback(3)>), including in (D)TLSv1.3. It is not yet
+available to the ClientHello callback (see L<SSL_CTX_set_client_hello_cb(3)>)
+or to the DTLS cookie verification callback (see
+L<SSL_CTX_set_cookie_verify_cb(3)>). The final eight bytes of the
+server_random carry the downgrade protection sentinel (RFC 8446 section 4.1.3)
+whose value depends on the selected protocol version, and the ClientHello
+callback is permitted to change the set of supported versions, so the version
+cannot be selected before that callback has run. The cookie verification
+callback runs before any per-connection resources are committed to a
+ClientHello whose source address has not yet been validated, so the
+server_random is deliberately not generated until it has succeeded. From
+within these two callbacks SSL_get_server_random() does not return the value
+of the current handshake but whatever the B<SSL> object currently holds: all
+zeros if no server_random has been generated on it yet, the value of the
+previous handshake on this B<SSL> object, or, while processing the second
+ClientHello after a HelloRetryRequest, the value generated for the first
+ClientHello. A fresh server_random is generated for each ClientHello that
+reaches the session lookup, so after a HelloRetryRequest only the one
+generated for the final ClientHello is sent to the client. A DTLS ClientHello
+that is answered with a HelloVerifyRequest does not reach that point, so no
+server_random is generated for it. When Encrypted ClientHello (ECH) is
+accepted, the final eight bytes
+of the server_random are replaced with the ECH acceptance confirmation while
+the ServerHello is being constructed, so a value observed from a callback
+before that point will differ in those bytes from the value sent to the
+client. On the client side the value is available once the ServerHello has
+been received.
+
+These functions also work on QUIC connection SSL objects, where they return
+the values from the underlying TLS handshake, including from within TLS
+callbacks such as the server side PSK callbacks. On QUIC stream SSL objects
+they return 0. Like other functions reporting TLS handshake state, on a QUIC
+connection they read the handshake layer without taking the QUIC connection
+lock. In thread assisted mode (see L<OSSL_QUIC_client_thread_method(3)>) the
+background thread may be progressing the handshake concurrently, so an
+application must only call these functions either once the handshake has
+completed, for example after L<SSL_connect(3)> or L<SSL_do_handshake(3)> has
+returned 1, or from within a callback invoked by the handshake itself. In both
+cases the values can no longer change concurrently.
=head1 RETURN VALUES
diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c
index 30bb20a5f2..288ebde573 100644
--- a/ssl/statem/statem_srvr.c
+++ b/ssl/statem/statem_srvr.c
@@ -2084,6 +2084,7 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s)
SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(s);
SSL *ssl = SSL_CONNECTION_GET_SSL(s);
SSL *ussl = SSL_CONNECTION_GET_USER_SSL(s);
+ unsigned char *snonce = s->s3.server_random;
/* Finished parsing the ClientHello, now we can start processing it */
/* Give the ClientHello callback a crack at things */
@@ -2225,6 +2226,23 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s)
goto err;
}
+ /*
+ * Generate the server_random now. The protocol version (and hence any
+ * downgrade sentinel) is known, and the cheap structural checks on the
+ * ClientHello (cookie, record boundary, cipher list, SCSVs) have all
+ * passed, so we do not spend entropy on a ClientHello we are going to
+ * reject anyway. It must happen before the session lookup below, as the
+ * callbacks invoked from there on such as the session ticket callbacks,
+ * tls_session_secret_cb, the (D)TLSv1.3 PSK callbacks and the servername
+ * callback can observe the value via SSL_get_server_random(). Note that
+ * in the TLSv1.2 flow tls_session_secret_cb in particular needs it for
+ * SessionTicket key derivation.
+ */
+ if (ssl_fill_hello_random(s, 1, snonce, SSL3_RANDOM_SIZE, dgrd) <= 0) {
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+
/*
* We don't allow resumption in a backwards compatible ClientHello.
* In TLS1.1+, session_id MUST be empty.
@@ -2329,21 +2347,6 @@ static int tls_early_post_process_client_hello(SSL_CONNECTION *s)
goto err;
}
- /*
- * Check if we want to use external pre-shared secret for this handshake
- * for not reused session only. We need to generate server_random before
- * calling tls_session_secret_cb in order to allow SessionTicket
- * processing to use it in key derivation.
- */
- {
- unsigned char *pos;
- pos = s->s3.server_random;
- if (ssl_fill_hello_random(s, 1, pos, SSL3_RANDOM_SIZE, dgrd) <= 0) {
- SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- goto err;
- }
- }
-
if (!s->hit && !tls1_set_server_sigalgs(s)) {
/* SSLfatal() already called */
goto err;
@@ -2808,7 +2811,7 @@ CON_FUNC_RETURN tls_construct_server_hello(SSL_CONNECTION *s, WPACKET *pkt)
if (!WPACKET_put_bytes_u16(pkt, version)
/*
* Random stuff. Filling of the server_random takes place in
- * tls_process_client_hello()
+ * tls_early_post_process_client_hello()
*/
|| !WPACKET_memcpy(pkt,
s->hello_retry_request == SSL_HRR_PENDING
diff --git a/test/ech_test.c b/test/ech_test.c
index a4de0c9a10..e00c618b05 100644
--- a/test/ech_test.c
+++ b/test/ech_test.c
@@ -2410,6 +2410,108 @@ end:
return res;
}
+static unsigned char sni_cb_random[SSL3_RANDOM_SIZE];
+static int sni_cb_random_cnt = 0;
+
+static int sni_cb_server_random(SSL *s, int *al, void *arg)
+{
+ sni_cb_random_cnt++;
+ if (SSL_get_server_random(s, sni_cb_random, sizeof(sni_cb_random))
+ != SSL3_RANDOM_SIZE) {
+ *al = SSL_AD_INTERNAL_ERROR;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ return SSL_TLSEXT_ERR_OK;
+}
+
+/*
+ * The server random is available to the callbacks invoked while the
+ * ClientHello is processed, but when ECH is accepted its final 8 bytes are
+ * replaced with the acceptance confirmation only while the ServerHello is
+ * constructed. So a value observed from such a callback must match the one
+ * finally sent in all but those bytes.
+ * idx 0: ECH accepted, idx 1: ECH accepted after a HRR, idx 2: no ECH
+ */
+static int test_ech_cb_server_random(int idx)
+{
+ int res = 0, clientstatus, serverstatus;
+ OSSL_ECHSTORE *es = NULL;
+ OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
+ SSL_CTX *c = NULL, *s = NULL;
+ SSL *cssl = NULL, *sssl = NULL;
+ char *cinner = NULL, *couter = NULL, *sinner = NULL, *souter = NULL;
+ unsigned char srv_rnd[SSL3_RANDOM_SIZE], clnt_rnd[SSL3_RANDOM_SIZE];
+ const size_t siglen = 8; /* length of the ECH acceptance confirmation */
+ const size_t prefix = SSL3_RANDOM_SIZE - siglen;
+
+ if (!TEST_ptr(es = OSSL_ECHSTORE_new(libctx, propq))
+ || !TEST_true(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
+ 0, "example.com", hpke_suite))
+ || !TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
+ TLS_client_method(),
+ TLS1_3_VERSION, TLS1_3_VERSION,
+ &s, &c, cert, privkey))
+ || !TEST_true(SSL_CTX_set_tlsext_servername_callback(s,
+ sni_cb_server_random)))
+ goto end;
+ if (idx != 2
+ && (!TEST_true(SSL_CTX_set1_echstore(s, es))
+ || !TEST_true(SSL_CTX_set1_echstore(c, es))))
+ goto end;
+ if (!TEST_true(create_ssl_objects(s, c, &sssl, &cssl, NULL, NULL))
+ || !TEST_true(SSL_set_tlsext_host_name(cssl, "server.example")))
+ goto end;
+ /* force a HRR for the 2nd iteration */
+ if (idx == 1 && !TEST_true(SSL_set1_groups_list(sssl, "P-384")))
+ goto end;
+ sni_cb_random_cnt = 0;
+ if (!TEST_true(create_ssl_connection(sssl, cssl, SSL_ERROR_NONE)))
+ goto end;
+ /* override cert verification */
+ SSL_set_verify_result(cssl, X509_V_OK);
+ clientstatus = SSL_ech_get1_status(cssl, &cinner, &couter);
+ serverstatus = SSL_ech_get1_status(sssl, &sinner, &souter);
+ if (idx != 2
+ && (!TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)
+ || !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS)))
+ goto end;
+ if (idx == 2
+ && (!TEST_int_eq(clientstatus, SSL_ECH_STATUS_NOT_CONFIGURED)
+ || !TEST_int_eq(serverstatus, SSL_ECH_STATUS_NOT_CONFIGURED)))
+ goto end;
+ /* with a HRR the callback runs once per ClientHello */
+ if (!TEST_int_eq(sni_cb_random_cnt, idx == 1 ? 2 : 1)
+ || !TEST_size_t_eq(SSL_get_server_random(sssl, srv_rnd, sizeof(srv_rnd)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_size_t_eq(SSL_get_server_random(cssl, clnt_rnd, sizeof(clnt_rnd)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_mem_eq(srv_rnd, sizeof(srv_rnd), clnt_rnd, sizeof(clnt_rnd))
+ || !TEST_mem_eq(sni_cb_random, prefix, srv_rnd, prefix))
+ goto end;
+ if (idx != 2) {
+ /* the ECH acceptance confirmation replaced the final bytes */
+ if (!TEST_mem_ne(sni_cb_random + prefix, siglen,
+ srv_rnd + prefix, siglen))
+ goto end;
+ } else {
+ if (!TEST_mem_eq(sni_cb_random, SSL3_RANDOM_SIZE,
+ srv_rnd, sizeof(srv_rnd)))
+ goto end;
+ }
+ res = 1;
+end:
+ OPENSSL_free(sinner);
+ OPENSSL_free(souter);
+ OPENSSL_free(cinner);
+ OPENSSL_free(couter);
+ OSSL_ECHSTORE_free(es);
+ SSL_free(cssl);
+ SSL_free(sssl);
+ SSL_CTX_free(c);
+ SSL_CTX_free(s);
+ return res;
+}
+
#endif
int setup_tests(void)
@@ -2461,6 +2563,7 @@ int setup_tests(void)
ADD_ALL_TESTS(test_ech_no_inner, suite_combos);
ADD_ALL_TESTS(test_ech_keylog_random, 4);
ADD_TEST(ech_unauth_client_status_test);
+ ADD_ALL_TESTS(test_ech_cb_server_random, 3);
return 1;
err:
return 0;
diff --git a/test/quicapitest.c b/test/quicapitest.c
index 941336147e..9a2e84646a 100644
--- a/test/quicapitest.c
+++ b/test/quicapitest.c
@@ -1336,11 +1336,24 @@ static int use_session_cb(SSL *ssl, const EVP_MD *md, const unsigned char **id,
return 1;
}
+/*
+ * The server random as observed from within find_session_cb(). Prior to the
+ * fix for issue #26404 the server random was generated only after the
+ * ClientHello extensions had been processed, so a PSK callback observed an
+ * all-zero value.
+ */
+static unsigned char cb_server_random[SSL3_RANDOM_SIZE];
+static size_t cb_server_random_len;
+
static int find_session_cb(SSL *ssl, const unsigned char *identity,
size_t identity_len, SSL_SESSION **sess)
{
find_session_cb_cnt++;
+ memset(cb_server_random, 0, sizeof(cb_server_random));
+ cb_server_random_len = SSL_get_server_random(ssl, cb_server_random,
+ sizeof(cb_server_random));
+
if (serverpsk == NULL || !SSL_SESSION_up_ref(serverpsk))
return 0;
@@ -1359,38 +1372,65 @@ static int find_session_cb(SSL *ssl, const unsigned char *identity,
static int test_quic_psk(void)
{
SSL_CTX *cctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_client_method());
- SSL *clientquic = NULL;
+ SSL *c = NULL;
QUIC_TSERVER *qtserv = NULL;
+ SSL *stream = NULL;
+ unsigned char zeros[SSL3_RANDOM_SIZE] = { 0 };
+ unsigned char snonce[SSL3_RANDOM_SIZE];
+ unsigned char cnonce[SSL3_RANDOM_SIZE];
int testresult = 0;
if (!TEST_ptr(cctx)
/* No cert or private key for the server, i.e. PSK only */
|| !TEST_true(qtest_create_quic_objects(libctx, cctx, NULL, NULL,
- NULL, 0, &qtserv,
- &clientquic, NULL, NULL)))
+ NULL, 0, &qtserv, &c, NULL, NULL)))
goto end;
- SSL_set_psk_use_session_callback(clientquic, use_session_cb);
+ SSL_set_psk_use_session_callback(c, use_session_cb);
ossl_quic_tserver_set_psk_find_session_cb(qtserv, find_session_cb);
use_session_cb_cnt = 0;
find_session_cb_cnt = 0;
- clientpsk = serverpsk = create_a_psk(clientquic, SHA384_DIGEST_LENGTH);
+ clientpsk = serverpsk = create_a_psk(c, SHA384_DIGEST_LENGTH);
/* We already had one ref. Add another one */
if (!TEST_ptr(clientpsk) || !TEST_true(SSL_SESSION_up_ref(clientpsk)))
goto end;
- if (!TEST_true(qtest_create_quic_connection(qtserv, clientquic))
+ if (!TEST_true(qtest_create_quic_connection(qtserv, c))
|| !TEST_int_eq(1, find_session_cb_cnt)
|| !TEST_int_eq(1, use_session_cb_cnt)
/* Check that we actually used the PSK */
- || !TEST_true(SSL_session_reused(clientquic)))
+ || !TEST_true(SSL_session_reused(c)))
+ goto end;
+
+ /*
+ * The server PSK callback must have observed the server random actually
+ * used in the handshake. The client side SSL object is a QUIC connection
+ * object, on which SSL_get_server_random() and SSL_get_client_random()
+ * report the values from the underlying TLS handshake.
+ */
+ if (!TEST_size_t_eq(cb_server_random_len, SSL3_RANDOM_SIZE)
+ || !TEST_mem_ne(cb_server_random, sizeof(cb_server_random),
+ zeros, sizeof(zeros))
+ || !TEST_size_t_eq(SSL_get_server_random(c, snonce, sizeof(snonce)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_mem_eq(cb_server_random, sizeof(cb_server_random),
+ snonce, sizeof(snonce))
+ || !TEST_size_t_eq(SSL_get_client_random(c, cnonce, sizeof(cnonce)),
+ SSL3_RANDOM_SIZE))
+ goto end;
+
+ /* On a QUIC stream object the getters are not applicable and return 0 */
+ if (!TEST_ptr(stream = SSL_new_stream(c, 0))
+ || !TEST_size_t_eq(SSL_get_server_random(stream, snonce, sizeof(snonce)), 0)
+ || !TEST_size_t_eq(SSL_get_client_random(stream, cnonce, sizeof(cnonce)), 0))
goto end;
testresult = 1;
end:
- SSL_free(clientquic);
+ SSL_free(stream);
+ SSL_free(c);
ossl_quic_tserver_free(qtserv);
SSL_CTX_free(cctx);
SSL_SESSION_free(clientpsk);
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 75b2a9a825..701a707991 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -8993,6 +8993,239 @@ end:
return testresult;
}
+/*
+ * Server side PSK callbacks must be able to observe the server random via
+ * SSL_get_server_random(). Prior to the fix for issue #26404 the server
+ * random was only generated after the ClientHello extensions had been
+ * processed, so in (D)TLSv1.3 (where the PSK callbacks run during extension
+ * processing) the callbacks observed an all-zero value - or, after a
+ * HelloRetryRequest, the stale value generated for the first ClientHello.
+ */
+/* Enough slots for a HelloRetryRequest handshake (two ClientHellos) */
+#define CB_SERVER_RANDOM_MAX 2
+static unsigned char cb_server_random[CB_SERVER_RANDOM_MAX][SSL3_RANDOM_SIZE];
+static size_t cb_server_random_len = 0;
+static int cb_server_random_cnt = 0;
+
+static int record_cb_server_random(SSL *ssl)
+{
+ unsigned char *slot;
+
+ if (cb_server_random_cnt >= CB_SERVER_RANDOM_MAX)
+ return 0;
+ slot = cb_server_random[cb_server_random_cnt++];
+ memset(slot, 0, SSL3_RANDOM_SIZE);
+ cb_server_random_len = SSL_get_server_random(ssl, slot, SSL3_RANDOM_SIZE);
+ return cb_server_random_len == SSL3_RANDOM_SIZE;
+}
+
+static int find_session_cb_server_random(SSL *ssl,
+ const unsigned char *identity,
+ size_t identity_len,
+ SSL_SESSION **sess)
+{
+ if (!record_cb_server_random(ssl))
+ return 0;
+ return find_session_cb(ssl, identity, identity_len, sess);
+}
+
+#ifndef OPENSSL_NO_PSK
+static unsigned int psk_server_cb_server_random(SSL *ssl,
+ const char *identity,
+ unsigned char *psk,
+ unsigned int max_psk_len)
+{
+ if (!record_cb_server_random(ssl))
+ return 0;
+ return psk_server_cb(ssl, identity, psk, max_psk_len);
+}
+#endif
+
+/*
+ * Test 0: (D)TLSv1.3, new style callback (psk_find_session_cb())
+ * Test 1: (D)TLSv1.3, old style callback (psk_server_callback())
+ * Test 2: (D)TLSv1.2, old style callback (psk_server_callback())
+ * Test 3: (D)TLSv1.3, new style callback, with a HelloRetryRequest
+ *
+ * |pskver| is the protocol version to record in the PSK session and |expver|
+ * the version expected to be negotiated; |vermax| caps the handshake to
+ * (D)TLSv1.2 for test 2.
+ */
+static int psk_cb_server_random(const SSL_METHOD *smeth,
+ const SSL_METHOD *cmeth,
+ int vermin, int vermax,
+ int pskver, int expver, int idx)
+{
+ SSL_CTX *sctx = NULL, *cctx = NULL;
+ SSL *s = NULL, *c = NULL;
+ const SSL_CIPHER *cipher = NULL;
+ const unsigned char key[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+ };
+ unsigned char zeros[SSL3_RANDOM_SIZE] = { 0 };
+ unsigned char snonce[SSL3_RANDOM_SIZE];
+ unsigned char cnonce[SSL3_RANDOM_SIZE];
+ const unsigned char *final;
+ int oldstyle = (idx == 1 || idx == 2);
+ int v12 = (idx == 2);
+ int hrr = (idx == 3);
+ /* With a HelloRetryRequest the callback runs once per ClientHello */
+ int expcnt = hrr ? 2 : 1;
+ int testresult = 0;
+
+#ifdef OPENSSL_NO_PSK
+ if (oldstyle)
+ return TEST_skip("PSK not supported");
+#endif
+#ifdef OPENSSL_NO_EC
+ if (hrr)
+ return TEST_skip("No EC groups available to force a HelloRetryRequest");
+#endif
+
+ if (!TEST_true(create_ssl_ctx_pair(libctx, smeth, cmeth, vermin, vermax,
+ &sctx, &cctx, cert, privkey)))
+ goto end;
+
+ cb_server_random_cnt = 0;
+ cb_server_random_len = 0;
+ srvid = pskid;
+ use_session_cb_cnt = find_session_cb_cnt = 0;
+ psk_client_cb_cnt = psk_server_cb_cnt = 0;
+
+ if (!oldstyle) {
+ SSL_CTX_set_psk_use_session_callback(cctx, use_session_cb);
+ SSL_CTX_set_psk_find_session_callback(sctx,
+ find_session_cb_server_random);
+ } else {
+#ifndef OPENSSL_NO_PSK
+ SSL_CTX_set_psk_client_callback(cctx, psk_client_cb);
+ SSL_CTX_set_psk_server_callback(sctx, psk_server_cb_server_random);
+#endif
+ }
+
+ if (v12) {
+ /* Make sure a (D)TLSv1.2 PSK ciphersuite gets selected */
+ if (!TEST_true(SSL_CTX_set_cipher_list(cctx, "PSK-AES128-GCM-SHA256")))
+ goto end;
+ } else {
+ /* Old style callbacks always default to SHA256 */
+ if (!TEST_true(SSL_CTX_set_ciphersuites(cctx, "TLS_AES_128_GCM_SHA256")))
+ goto end;
+ }
+
+ if (!TEST_true(create_ssl_objects(sctx, cctx, &s, &c, NULL, NULL)))
+ goto end;
+
+ if (hrr) {
+ /* Client sends a P-256 key_share; server forces a retry to P-384 */
+ if (!TEST_true(SSL_set1_groups_list(c, "P-256:P-384"))
+ || !TEST_true(SSL_set1_groups_list(s, "P-384")))
+ goto end;
+ }
+
+ /* Create the PSK */
+ cipher = SSL_CIPHER_find(c, TLS13_AES_128_GCM_SHA256_BYTES);
+ clientpsk = SSL_SESSION_new();
+ if (!TEST_ptr(clientpsk)
+ || !TEST_ptr(cipher)
+ || !TEST_true(SSL_SESSION_set1_master_key(clientpsk, key, sizeof(key)))
+ || !TEST_true(SSL_SESSION_set_cipher(clientpsk, cipher))
+ || !TEST_true(SSL_SESSION_set_protocol_version(clientpsk, pskver))
+ || !TEST_true(SSL_SESSION_up_ref(clientpsk)))
+ goto end;
+ serverpsk = clientpsk;
+
+ final = cb_server_random[expcnt - 1];
+ if (!TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE))
+ || !TEST_int_eq(SSL_version(s), expver)
+ || !TEST_int_eq(cb_server_random_cnt, expcnt)
+ || !TEST_size_t_eq(cb_server_random_len, SSL3_RANDOM_SIZE)
+ /* The value seen by the callback must be populated ... */
+ || !TEST_mem_ne(final, SSL3_RANDOM_SIZE, zeros, sizeof(zeros))
+ /* ... and must be the value actually used in the handshake */
+ || !TEST_size_t_eq(SSL_get_server_random(s, snonce, sizeof(snonce)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_size_t_eq(SSL_get_server_random(c, cnonce, sizeof(cnonce)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_mem_eq(final, SSL3_RANDOM_SIZE, snonce, sizeof(snonce))
+ || !TEST_mem_eq(final, SSL3_RANDOM_SIZE, cnonce, sizeof(cnonce)))
+ goto end;
+
+ if (hrr) {
+ /*
+ * A fresh server_random is generated for the second ClientHello, so the
+ * value seen while processing the first one must have been populated
+ * too but differs from the one finally sent.
+ */
+ if (!TEST_mem_ne(cb_server_random[0], SSL3_RANDOM_SIZE,
+ zeros, sizeof(zeros))
+ || !TEST_mem_ne(cb_server_random[0], SSL3_RANDOM_SIZE,
+ cb_server_random[1], SSL3_RANDOM_SIZE))
+ goto end;
+ }
+
+ if (!oldstyle) {
+ /* The PSK must actually have been accepted via the wrapped callback */
+ if (!TEST_true(SSL_session_reused(s))
+ || !TEST_int_eq(find_session_cb_cnt, expcnt)
+ || !TEST_int_eq(psk_server_cb_cnt, 0))
+ goto end;
+ } else {
+ if (!TEST_int_eq(find_session_cb_cnt, 0)
+ || !TEST_int_eq(psk_server_cb_cnt, 1))
+ goto end;
+ }
+
+ testresult = 1;
+
+end:
+ SSL_SESSION_free(clientpsk);
+ SSL_SESSION_free(serverpsk);
+ clientpsk = serverpsk = NULL;
+ SSL_free(s);
+ SSL_free(c);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+ return testresult;
+}
+
+static int test_tls_psk_cb_server_random(int idx)
+{
+ int v12 = (idx == 2);
+
+#ifdef OPENSSL_NO_TLS1_2
+ if (v12)
+ return TEST_skip("No TLSv1.2");
+#endif
+ return psk_cb_server_random(TLS_server_method(), TLS_client_method(),
+ TLS1_VERSION, v12 ? TLS1_2_VERSION : 0,
+ TLS1_3_VERSION,
+ v12 ? TLS1_2_VERSION : TLS1_3_VERSION, idx);
+}
+
+#ifndef OPENSSL_NO_DTLS
+static int test_dtls_psk_cb_server_random(int idx)
+{
+ int v12 = (idx == 2);
+
+#ifdef OPENSSL_NO_DTLS1_2
+ if (v12)
+ return TEST_skip("No DTLSv1.2");
+#endif
+#ifdef OSSL_NO_USABLE_DTLS1_3
+ if (!v12)
+ return TEST_skip("No usable DTLSv1.3");
+#endif
+ return psk_cb_server_random(DTLS_server_method(), DTLS_client_method(),
+ DTLS1_VERSION, v12 ? DTLS1_2_VERSION : 0,
+ DTLS1_3_VERSION,
+ v12 ? DTLS1_2_VERSION : DTLS1_3_VERSION, idx);
+}
+#endif /* OPENSSL_NO_DTLS */
+
/*
* A client with its own session ID context configured must still be able
* to resume a TLS 1.3 external PSK obtained via the legacy
@@ -12257,6 +12490,193 @@ end:
return testresult;
}
+/*
+ * The other server side callbacks invoked while the ClientHello is being
+ * processed must be able to observe the server random too: the servername
+ * callback, the session ticket decrypt callback and the ticket key callback
+ * (in the decrypt direction). Prior to the fix for issue #26404 all of them
+ * observed an all-zero value in every protocol version, since the server
+ * random was only generated once the extensions had been processed.
+ */
+static unsigned char sni_cb_random[SSL3_RANDOM_SIZE];
+static unsigned char dec_tick_cb_random[SSL3_RANDOM_SIZE];
+static unsigned char tick_key_cb_random[SSL3_RANDOM_SIZE];
+static int sni_cb_random_cnt, dec_tick_cb_random_cnt, tick_key_cb_random_cnt;
+
+static int sni_cb_server_random(SSL *s, int *al, void *arg)
+{
+ sni_cb_random_cnt++;
+ if (SSL_get_server_random(s, sni_cb_random, sizeof(sni_cb_random))
+ != SSL3_RANDOM_SIZE) {
+ *al = SSL_AD_INTERNAL_ERROR;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ return SSL_TLSEXT_ERR_OK;
+}
+
+static SSL_TICKET_RETURN dec_tick_cb_server_random(SSL *s, SSL_SESSION *ss,
+ const unsigned char *keyname,
+ size_t keyname_length,
+ SSL_TICKET_STATUS status,
+ void *arg)
+{
+ dec_tick_cb_random_cnt++;
+ if (SSL_get_server_random(s, dec_tick_cb_random,
+ sizeof(dec_tick_cb_random))
+ != SSL3_RANDOM_SIZE)
+ return SSL_TICKET_RETURN_ABORT;
+
+ switch (status) {
+ case SSL_TICKET_SUCCESS:
+ return SSL_TICKET_RETURN_USE;
+ case SSL_TICKET_SUCCESS_RENEW:
+ return SSL_TICKET_RETURN_USE_RENEW;
+ default:
+ /* No (usable) ticket: make sure one gets issued */
+ return SSL_TICKET_RETURN_IGNORE_RENEW;
+ }
+}
+
+static int tick_key_evp_cb_server_random(SSL *s, unsigned char key_name[16],
+ unsigned char iv[EVP_MAX_IV_LENGTH],
+ EVP_CIPHER_CTX *ctx, EVP_MAC_CTX *hctx,
+ int enc)
+{
+ /* Only the decrypt direction runs while the ClientHello is processed */
+ if (!enc) {
+ tick_key_cb_random_cnt++;
+ if (SSL_get_server_random(s, tick_key_cb_random,
+ sizeof(tick_key_cb_random))
+ != SSL3_RANDOM_SIZE)
+ return -1;
+ }
+ return tick_key_evp_cb(s, key_name, iv, ctx, hctx, enc);
+}
+
+/*
+ * Do a full handshake followed by a ticket based resumption and check that
+ * the value seen by each callback is the server random actually used in that
+ * handshake.
+ */
+static int clienthello_cb_server_random(const SSL_METHOD *smeth,
+ const SSL_METHOD *cmeth,
+ int vermin, int vermax, int expver)
+{
+ SSL_CTX *sctx = NULL, *cctx = NULL;
+ SSL *s = NULL, *c = NULL;
+ SSL_SESSION *sess = NULL;
+ unsigned char zeros[SSL3_RANDOM_SIZE] = { 0 };
+ unsigned char snonce[SSL3_RANDOM_SIZE];
+ int testresult = 0;
+
+ if (!TEST_true(create_ssl_ctx_pair(libctx, smeth, cmeth, vermin, vermax,
+ &sctx, &cctx, cert, privkey)))
+ goto end;
+
+ /* Resume from the ticket only, not from the session cache */
+ if (!TEST_true(SSL_CTX_set_session_cache_mode(sctx, SSL_SESS_CACHE_OFF))
+ || !TEST_true(SSL_CTX_set_tlsext_servername_callback(sctx,
+ sni_cb_server_random))
+ || !TEST_true(SSL_CTX_set_session_ticket_cb(sctx, NULL,
+ dec_tick_cb_server_random, NULL))
+ || !TEST_true(SSL_CTX_set_tlsext_ticket_key_evp_cb(sctx,
+ tick_key_evp_cb_server_random)))
+ goto end;
+ tick_key_renew = 0;
+ tick_key_cb_called = 0;
+
+ sni_cb_random_cnt = dec_tick_cb_random_cnt = tick_key_cb_random_cnt = 0;
+ if (!TEST_true(create_ssl_objects(sctx, cctx, &s, &c, NULL, NULL))
+ || !TEST_true(SSL_set_tlsext_host_name(c, "server.example"))
+ || !TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE))
+ || !TEST_int_eq(SSL_version(s), expver)
+ || !TEST_int_eq(sni_cb_random_cnt, 1)
+ || !TEST_mem_ne(sni_cb_random, SSL3_RANDOM_SIZE, zeros, sizeof(zeros))
+ || !TEST_size_t_eq(SSL_get_server_random(s, snonce, sizeof(snonce)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_mem_eq(sni_cb_random, SSL3_RANDOM_SIZE, snonce, sizeof(snonce)))
+ goto end;
+
+ /*
+ * In TLSv1.2 the decrypt callback also runs during the initial handshake
+ * (with SSL_TICKET_EMPTY) and must have seen the same value.
+ */
+ if (dec_tick_cb_random_cnt != 0
+ && !TEST_mem_eq(dec_tick_cb_random, SSL3_RANDOM_SIZE,
+ snonce, sizeof(snonce)))
+ goto end;
+
+ if (!TEST_ptr(sess = SSL_get1_session(c)))
+ goto end;
+ shutdown_ssl_connection(s, c);
+ s = c = NULL;
+
+ sni_cb_random_cnt = dec_tick_cb_random_cnt = tick_key_cb_random_cnt = 0;
+ if (!TEST_true(create_ssl_objects(sctx, cctx, &s, &c, NULL, NULL))
+ || !TEST_true(SSL_set_tlsext_host_name(c, "server.example"))
+ || !TEST_true(SSL_set_session(c, sess))
+ || !TEST_true(create_ssl_connection(s, c, SSL_ERROR_NONE))
+ || !TEST_true(SSL_session_reused(s))
+ || !TEST_int_eq(sni_cb_random_cnt, 1)
+ || !TEST_int_eq(dec_tick_cb_random_cnt, 1)
+ || !TEST_int_eq(tick_key_cb_random_cnt, 1)
+ || !TEST_size_t_eq(SSL_get_server_random(s, snonce, sizeof(snonce)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_mem_ne(snonce, sizeof(snonce), zeros, sizeof(zeros))
+ || !TEST_mem_eq(sni_cb_random, SSL3_RANDOM_SIZE, snonce, sizeof(snonce))
+ || !TEST_mem_eq(dec_tick_cb_random, SSL3_RANDOM_SIZE,
+ snonce, sizeof(snonce))
+ || !TEST_mem_eq(tick_key_cb_random, SSL3_RANDOM_SIZE,
+ snonce, sizeof(snonce)))
+ goto end;
+
+ testresult = 1;
+
+end:
+ SSL_SESSION_free(sess);
+ SSL_free(s);
+ SSL_free(c);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(cctx);
+ return testresult;
+}
+
+/* Test 0: TLSv1.3, Test 1: TLSv1.2 */
+static int test_tls_clienthello_cb_server_random(int idx)
+{
+#ifdef OSSL_NO_USABLE_TLS1_3
+ if (idx == 0)
+ return TEST_skip("No usable TLSv1.3");
+#endif
+#ifdef OPENSSL_NO_TLS1_2
+ if (idx == 1)
+ return TEST_skip("No TLSv1.2");
+#endif
+ return clienthello_cb_server_random(TLS_server_method(),
+ TLS_client_method(), TLS1_VERSION,
+ idx == 1 ? TLS1_2_VERSION : 0,
+ idx == 1 ? TLS1_2_VERSION : TLS1_3_VERSION);
+}
+
+#ifndef OPENSSL_NO_DTLS
+/* Test 0: DTLSv1.3, Test 1: DTLSv1.2 */
+static int test_dtls_clienthello_cb_server_random(int idx)
+{
+#ifdef OSSL_NO_USABLE_DTLS1_3
+ if (idx == 0)
+ return TEST_skip("No usable DTLSv1.3");
+#endif
+#ifdef OPENSSL_NO_DTLS1_2
+ if (idx == 1)
+ return TEST_skip("No DTLSv1.2");
+#endif
+ return clienthello_cb_server_random(DTLS_server_method(),
+ DTLS_client_method(), DTLS1_VERSION,
+ idx == 1 ? DTLS1_2_VERSION : 0,
+ idx == 1 ? DTLS1_2_VERSION : DTLS1_3_VERSION);
+}
+#endif /* OPENSSL_NO_DTLS */
+
/*
* Callback that always returns ABORT for successfully decrypted tickets.
* Used by test_ticket_abort_session_leak to exercise the error path in
@@ -14949,6 +15369,14 @@ static int secret_cb(SSL *s, void *secretin, int *secret_len,
{
int i;
unsigned char *secret = secretin;
+ unsigned char zeros[SSL3_RANDOM_SIZE] = { 0 };
+ unsigned char snonce[SSL3_RANDOM_SIZE];
+
+ /* The server random must already be available on both sides */
+ if (!TEST_size_t_eq(SSL_get_server_random(s, snonce, sizeof(snonce)),
+ SSL3_RANDOM_SIZE)
+ || !TEST_mem_ne(snonce, sizeof(snonce), zeros, sizeof(zeros)))
+ return 0;
/* Just use a fixed master secret */
for (i = 0; i < *secret_len; i++)
@@ -19014,6 +19442,10 @@ int setup_tests(void)
ADD_ALL_TESTS(test_tls13_psk_client_sid_ctx, 2);
#endif /* OPENSSL_NO_PSK */
ADD_TEST(test_tls13_psk_verify_peer_no_sid_ctx);
+ ADD_ALL_TESTS(test_tls_psk_cb_server_random, 4);
+#ifndef OPENSSL_NO_DTLS
+ ADD_ALL_TESTS(test_dtls_psk_cb_server_random, 4);
+#endif
ADD_TEST(test_tls13_psk_verify_peer_no_ticket);
#endif /* !defined(OSSL_NO_USABLE_TLS1_3) */
#if !defined(OSSL_NO_USABLE_TLS1_3)
@@ -19061,6 +19493,10 @@ int setup_tests(void)
ADD_ALL_TESTS(test_ssl_pending, 2);
ADD_ALL_TESTS(test_ssl_get_shared_ciphers, OSSL_NELEM(shared_ciphers_data));
ADD_ALL_TESTS(test_ticket_callbacks, 20);
+ ADD_ALL_TESTS(test_tls_clienthello_cb_server_random, 2);
+#ifndef OPENSSL_NO_DTLS
+ ADD_ALL_TESTS(test_dtls_clienthello_cb_server_random, 2);
+#endif
ADD_TEST(test_ticket_abort_session_leak);
ADD_ALL_TESTS(test_shutdown, 7);
ADD_TEST(test_async_shutdown);