Commit 2e214d3a3b for openssl.org
commit 2e214d3a3b1fbe4a7ac18bafd4ed8a7bcf0d05e6
Author: Viktor Dukhovni <viktor@openssl.org>
Date: Wed Jul 22 14:00:25 2026 +1000
Polish psk callback error path cleanup
- Avoid SSL_SESSION leak
- Always cleanse the returned PSK secret
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Merge-date: Fri Oct 9 13:56:04 2026
Merged-from: https://github.com/openssl/openssl/pull/31925
diff --git a/CHANGES.md b/CHANGES.md
index 543bb999ba..d11c34fe82 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,6 +33,21 @@ OpenSSL 4.2
### Changes between 4.1 and 4.2 [xx XXX xxxx]
+ * Changed TLS 1.3 clients to offer a PSK, and to attempt 0-RTT early data,
+ only when it can actually be used. A resumption ticket or external PSK is
+ offered only if some offered ciphersuite shares its handshake digest, and
+ 0-RTT is attempted only if the first offered PSK's exact ciphersuite and,
+ where recorded, its ALPN protocol are offered and, for a ticket, it has not
+ aged out. When 0-RTT is not viable the client keeps the PSK for a 1-RTT
+ handshake. In particular a client-detected ALPN inconsistency now suppresses
+ 0-RTT rather than failing with `SSL_R_INCONSISTENT_EARLY_DATA_ALPN`.
+
+ Fixed TLS 1.3 clients to reject a `psk_use_session` callback PSK whose master
+ key is empty, with `SSL_R_BAD_PSK`, instead of failing later in binder
+ computation.
+
+ *Viktor Dukhovni*
+
* `EVP_SKEY_get0_raw_key()` now accepts a NULL key pointer to retrieve only
the key length. The length is obtained from the `OSSL_SKEY_PARAM_KEY_LENGTH`
key parameter without exporting the key, so it is also available for keys
diff --git a/ssl/statem/extensions_clnt.c b/ssl/statem/extensions_clnt.c
index 2b7be830a2..478b4ddce6 100644
--- a/ssl/statem/extensions_clnt.c
+++ b/ssl/statem/extensions_clnt.c
@@ -1284,14 +1284,17 @@ EXT_RETURN tls_construct_ctos_early_data(SSL_CONNECTION *s, WPACKET *pkt,
psk, sizeof(psk));
if (psklen > PSK_MAX_PSK_LEN) {
- SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, ERR_R_INTERNAL_ERROR);
+ OPENSSL_cleanse(psk, sizeof(psk));
+ SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
- } else if (psklen > 0) {
+ }
+ if (psklen > 0) {
const unsigned char tls13_aes128gcmsha256_id[] = { 0x13, 0x01 };
const SSL_CIPHER *cipher;
idlen = strlen(identity);
if (idlen > PSK_MAX_IDENTITY_LEN) {
+ OPENSSL_cleanse(psk, sizeof(psk));
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
@@ -1304,6 +1307,7 @@ EXT_RETURN tls_construct_ctos_early_data(SSL_CONNECTION *s, WPACKET *pkt,
cipher = SSL_CIPHER_find(SSL_CONNECTION_GET_SSL(s),
tls13_aes128gcmsha256_id);
if (cipher == NULL) {
+ OPENSSL_cleanse(psk, sizeof(psk));
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return EXT_RETURN_FAIL;
}
@@ -1314,8 +1318,9 @@ EXT_RETURN tls_construct_ctos_early_data(SSL_CONNECTION *s, WPACKET *pkt,
|| !SSL_SESSION_set1_master_key(psksess, psk, psklen)
|| !SSL_SESSION_set_cipher(psksess, cipher)
|| !SSL_SESSION_set_protocol_version(psksess, version1_3)) {
+ SSL_SESSION_free(psksess);
+ OPENSSL_cleanse(psk, sizeof(psk));
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
- OPENSSL_cleanse(psk, psklen);
return EXT_RETURN_FAIL;
}
OPENSSL_cleanse(psk, psklen);
diff --git a/ssl/statem/extensions_srvr.c b/ssl/statem/extensions_srvr.c
index 0e7a467cc5..64a46af4b9 100644
--- a/ssl/statem/extensions_srvr.c
+++ b/ssl/statem/extensions_srvr.c
@@ -1407,6 +1407,7 @@ int tls_parse_ctos_psk(SSL_CONNECTION *s, PACKET *pkt, unsigned int context,
sizeof(pskdata));
OPENSSL_free(pskid);
if (pskdatalen > PSK_MAX_PSK_LEN) {
+ OPENSSL_cleanse(pskdata, sizeof(pskdata));
SSLfatal(s, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
} else if (pskdatalen > 0) {