Commit 35b0591491 for openssl.org
commit 35b059149143e3fbea33186c06cfebc7f582e383
Author: Viktor Dukhovni <viktor@openssl.org>
Date: Mon Jul 13 19:56:00 2026 +1000
PSK documentation polish
Reviewed-by: Mounir Idrassi <mounir.idrassi@idrix.fr>
Reviewed-by: Nikola Pajkovsky <nikolap@openssl.org>
Merge-date: Fri Oct 9 13:56:10 2026
Merged-from: https://github.com/openssl/openssl/pull/31925
diff --git a/doc/man3/SSL_CTX_set_psk_client_callback.pod b/doc/man3/SSL_CTX_set_psk_client_callback.pod
index 146e3d03a5..2cc9f9198f 100644
--- a/doc/man3/SSL_CTX_set_psk_client_callback.pod
+++ b/doc/man3/SSL_CTX_set_psk_client_callback.pod
@@ -65,6 +65,8 @@ the following fields set:
=item The master key
This can be set via a call to L<SSL_SESSION_set1_master_key(3)>.
+The master key must not be empty: a session whose master key has zero length is
+rejected with B<SSL_R_BAD_PSK>.
=item A ciphersuite
@@ -72,9 +74,18 @@ Only the handshake digest associated with the ciphersuite is relevant for the
PSK (the server may go on to negotiate any ciphersuite which is compatible with
the digest). The application can use any TLSv1.3 ciphersuite. If B<md> is
not NULL the handshake digest for the ciphersuite should be the same.
-The ciphersuite can be set via a call to <SSL_SESSION_set_cipher(3)>. The
-handshake digest of an SSL_CIPHER object can be checked using
-<SSL_CIPHER_get_handshake_digest(3)>.
+The ciphersuite can be set via a call to L<SSL_SESSION_set_cipher(3)>.
+The handshake digest of an SSL_CIPHER object can be checked using
+L<SSL_CIPHER_get_handshake_digest(3)>.
+
+While only the digest matters for the handshake, early data (0-RTT) is protected
+with the exact ciphersuite recorded on the session, and the associated ALPN
+protocol, if any.
+To send early data with this PSK the application must therefore offer that
+ciphersuite (not merely a digest-compatible one) and, where applicable, at
+least that ALPN protocol on the connection; otherwise the client completes the
+handshake without sending early data.
+See L<SSL_write_early_data(3)>.
=item The protocol version
@@ -85,7 +96,8 @@ be TLS1_3_VERSION.
Additionally the maximum early data value should be set via a call to
L<SSL_SESSION_set_max_early_data(3)> if the PSK will be used for sending early
-data.
+data; see L<SSL_write_early_data(3)> for the conditions under which early data is
+actually sent.
Alternatively an SSL_SESSION created from a previous non-PSK handshake may also
be used as the basis for a PSK.
@@ -168,7 +180,8 @@ failure. In the event of failure the connection setup fails.
L<ssl(7)>,
L<SSL_CTX_set_psk_find_session_callback(3)>,
-L<SSL_set_psk_find_session_callback(3)>
+L<SSL_set_psk_find_session_callback(3)>,
+L<SSL_write_early_data(3)>
=head1 HISTORY
diff --git a/doc/man3/SSL_CTX_use_psk_identity_hint.pod b/doc/man3/SSL_CTX_use_psk_identity_hint.pod
index 0c60a4cc5e..c138310291 100644
--- a/doc/man3/SSL_CTX_use_psk_identity_hint.pod
+++ b/doc/man3/SSL_CTX_use_psk_identity_hint.pod
@@ -50,6 +50,11 @@ in B<*sess>. The SSL_SESSION object should, as a minimum, set the master key,
the ciphersuite and the protocol version. See
L<SSL_CTX_set_psk_use_session_callback(3)> for details.
+The server accepts early data (0-RTT) on a resumed PSK only if it selects the
+PSK's ciphersuite and the negotiated ALPN protocol matches; otherwise the early
+data is skipped and an ordinary 1-RTT handshake proceeds.
+See L<SSL_read_early_data(3)>.
+
It is also possible for the callback to succeed but not supply a PSK. To do this
the callback should return successfully and ensure that B<*sess> is NULL. In
this case no PSK will be used and, if a certificate has also been configured,
@@ -143,7 +148,8 @@ TLS 1.3 and TLS 1.2."
L<ssl(7)>,
L<SSL_CTX_set_psk_use_session_callback(3)>,
-L<SSL_set_psk_use_session_callback(3)>
+L<SSL_set_psk_use_session_callback(3)>,
+L<SSL_read_early_data(3)>
=head1 HISTORY
diff --git a/doc/man3/SSL_SESSION_get0_cipher.pod b/doc/man3/SSL_SESSION_get0_cipher.pod
index 3883a6fa05..35a06c4ee0 100644
--- a/doc/man3/SSL_SESSION_get0_cipher.pod
+++ b/doc/man3/SSL_SESSION_get0_cipher.pod
@@ -24,6 +24,9 @@ should not be released.
SSL_SESSION_set_cipher() can be used to set the ciphersuite associated with the
SSL_SESSION B<s> to B<cipher>. For example, this could be used to set up a
session based PSK (see L<SSL_CTX_set_psk_use_session_callback(3)>).
+For a PSK the cipher pins the handshake digest and, when the PSK is used for
+early data (0-RTT), the ciphersuite that protects that early data; see
+L<SSL_read_early_data(3)>.
=head1 RETURN VALUES
@@ -39,7 +42,8 @@ L<d2i_SSL_SESSION(3)>,
L<SSL_SESSION_get_time(3)>,
L<SSL_SESSION_get0_hostname(3)>,
L<SSL_SESSION_free(3)>,
-L<SSL_CTX_set_psk_use_session_callback(3)>
+L<SSL_CTX_set_psk_use_session_callback(3)>,
+L<SSL_read_early_data(3)>
=head1 HISTORY
diff --git a/doc/man3/SSL_read_early_data.pod b/doc/man3/SSL_read_early_data.pod
index 6c2f138b8b..6e571bd76b 100644
--- a/doc/man3/SSL_read_early_data.pod
+++ b/doc/man3/SSL_read_early_data.pod
@@ -86,10 +86,11 @@ If the session cannot be used then this function will return 0. Otherwise it
will return the maximum number of early data bytes that can be sent.
The function SSL_SESSION_set_max_early_data() sets the maximum number of early
-data bytes that can be sent for a session. This would typically be used when
-creating a PSK session file (see L<SSL_CTX_set_psk_use_session_callback(3)>). If
-using a ticket based PSK then this is set automatically to the value provided by
-the server.
+data bytes that can be sent for a session.
+This would typically be used when creating a PSK session (see
+L<SSL_CTX_set_psk_use_session_callback(3)>).
+If using a ticket based PSK then this is set automatically to the value provided
+by the server.
A client uses the function SSL_write_early_data() to send early data. This
function is similar to the L<SSL_write_ex(3)> function, but with the following
@@ -203,7 +204,7 @@ connection attempt. By default the server does not accept early data; a
server may indicate support for early data by calling
SSL_CTX_set_max_early_data() or
SSL_set_max_early_data() to set it for the whole SSL_CTX or an individual SSL
-object respectively. The B<max_early_data> parameter specifies the maximum
+object respectively. The I<max_early_data> parameter specifies the maximum
amount of early data in bytes that is permitted to be sent on a single
connection. Similarly the SSL_CTX_get_max_early_data() and
SSL_get_max_early_data() functions can be used to obtain the current maximum
@@ -215,25 +216,32 @@ early data setting for a server is nonzero then replay protection is
automatically enabled (see L</REPLAY PROTECTION> below).
If the server rejects the early data sent by a client then it will skip over
-the data that is sent. The maximum amount of received early data that is skipped
-is controlled by the recv_max_early_data setting. If a client sends more than
-this then the connection will abort. This value can be set by calling
-SSL_CTX_set_recv_max_early_data() or SSL_set_recv_max_early_data(). The current
-value for this setting can be obtained by calling
-SSL_CTX_get_recv_max_early_data() or SSL_get_recv_max_early_data(). The default
-value for this setting is 16,384 bytes.
-
-The recv_max_early_data value also has an impact on early data that is accepted.
+the data that is sent.
+The maximum amount of received early data that is skipped is controlled by the
+I<recv_max_early_data> setting.
+This setting only affects a server; it has no effect on a client.
+If a client sends more than this then the connection will abort.
+This value can be set by calling SSL_CTX_set_recv_max_early_data() or
+SSL_set_recv_max_early_data().
+The current value for this setting can be obtained by calling
+SSL_CTX_get_recv_max_early_data() or SSL_get_recv_max_early_data().
+The default value for this setting is 16,384 bytes.
+
+The I<recv_max_early_data> value also has an impact on early data that is
+accepted.
The amount of data that is accepted will always be the lower of the
-max_early_data for the session and the recv_max_early_data setting for the
-server. If a client sends more data than this then the connection will abort.
-
-The configured value for max_early_data on a server may change over time as
-required. However, clients may have tickets containing the previously configured
-max_early_data value. The recv_max_early_data should always be equal to or
-higher than any recently configured max_early_data value in order to avoid
-aborted connections. The recv_max_early_data should never be set to less than
-the current configured max_early_data value.
+I<max_early_data> for the session and the I<recv_max_early_data> setting for the
+server.
+If a client sends more data than this then the connection will abort.
+
+The configured value for I<max_early_data> on a server may change over time as
+required.
+However, clients may have tickets containing the previously configured
+I<max_early_data> value.
+The I<recv_max_early_data> should always be equal to or higher than any recently
+configured I<max_early_data> value in order to avoid aborted connections.
+The I<recv_max_early_data> should never be set to less than the current
+configured I<max_early_data> value.
Some server applications may wish to have more control over whether early data
is accepted or not, for example to mitigate replay risks (see L</REPLAY PROTECTION>
@@ -281,6 +289,22 @@ Nagle's algorithm. If an application opts to disable Nagle's algorithm
consideration should be given to turning it back on again after the handshake is
complete if appropriate.
+On the client side, early data is sent only when the PSK that would key it can
+actually be used for 0-RTT.
+Early data is protected with the ciphersuite of the first PSK the client offers,
+so it is sent only if that PSK's exact ciphersuite is among those the client
+offers; if an ALPN protocol was recorded on the PSK's session then it too must
+be among those offered; and a resumption ticket is not offered at all once its
+age exceeds the ticket lifetime.
+When the PSK remains usable for the handshake but 0-RTT specifically is not
+viable, the client silently omits the early data and completes an ordinary
+1-RTT handshake; SSL_get_early_data_status() then reports
+SSL_EARLY_DATA_REJECTED.
+When more than one PSK is available, the maximum early data value that applies
+is that of this first-offered PSK.
+Note that even when the client does send early data the server may still reject
+it, for example if it selects a different ciphersuite or ALPN protocol.
+
In rare circumstances, it may be possible for a client to have a session that
reports a max early data value greater than 0, but where the server does not
support this. For example, this can occur if a server has had its configuration