Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
276 changes: 271 additions & 5 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,23 @@ Exceptions

A human readable string of the verification error.

.. exception:: SSLECHRejectedError

A subclass of :exc:`SSLError` raised when the handshake fails because the
server rejected :ref:`Encrypted Client Hello <ssl-encrypted-client-hello>`.
The connection cannot be used. If :attr:`retry_config` is not empty, the
client may retry once with a new connection using that configuration.

This exception is only raised when :data:`HAS_ECH` is ``True``.

.. attribute:: retry_config

The server's ECH retry configuration as an ECHConfigList,
or ``b""`` if the server did not provide one or the server
could not be authenticated under the outer server name.

.. versionadded:: next

.. exception:: CertificateError

An alias for :exc:`SSLCertVerificationError`.
Expand Down Expand Up @@ -869,6 +886,40 @@ Constants

.. versionadded:: 3.12

.. data:: OP_ECH_GREASE

Include a GREASE ECH extension in ClientHello messages when ECH is not
being attempted. This can elicit an ECH retry configuration from a server.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. data:: OP_ECH_TRIALDECRYPT

Attempt server-side ECH decryption with all available ECH keys, rather
than only those with a matching configuration identifier.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. data:: OP_ECH_IGNORE_CID

Use a random ECH configuration identifier in client connections.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. data:: OP_ECH_GREASE_RETRY_CONFIG

Include GREASE entries in the ECH retry configurations sent by a server.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. data:: HAS_ALPN

Whether the OpenSSL library has built-in support for the *Application-Layer
Expand Down Expand Up @@ -964,6 +1015,13 @@ Constants

.. versionadded:: 3.14

.. data:: HAS_ECH

Whether the OpenSSL library has built-in support for Encrypted Client
Hello (ECH). This requires OpenSSL 4.0 or later with ECH enabled.

.. versionadded:: next

.. data:: CHANNEL_BINDING_TYPES

List of supported TLS channel binding types. Strings in this list
Expand Down Expand Up @@ -1032,6 +1090,21 @@ Constants

.. versionadded:: 3.6

.. class:: ECHStatus

:class:`enum.IntEnum` collection of Encrypted Client Hello (ECH) statuses
returned by :meth:`SSLSocket.get_ech_status`.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. attribute:: ECHStatus.ECH_STATUS_GREASE_ECH

The client sent a GREASE ECH extension and received a retry configuration.

.. versionadded:: next

.. data:: Purpose.SERVER_AUTH

Option for :func:`create_default_context` and
Expand Down Expand Up @@ -1368,6 +1441,39 @@ SSL sockets also have the following additional methods and attributes:

.. versionadded:: 3.3

.. method:: SSLSocket.get_ech_retry_config()

Return the server's ECH retry configuration as :class:`bytes`, or ``b""``
if no retry configuration is available.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. method:: SSLSocket.get_ech_status()

Gets the status of Encrypted Client Hello (ECH) processing. Returns an
:class:`ECHStatus` instance.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. method:: SSLSocket.set_ech_config(ech_config)

Set the Encrypted Client Hello (ECH) configuration for this connection
only, replacing any configuration set on the context with
:meth:`SSLContext.set_ech_config`. The *ech_config* parameter accepts the
same values.

The configuration must be set before the handshake starts, so this is
mostly useful with ``do_handshake_on_connect=False`` or on a socket that
is not connected yet.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. method:: SSLSocket.selected_alpn_protocol()

Return the protocol that was selected during the TLS handshake. If
Expand Down Expand Up @@ -1440,6 +1546,16 @@ SSL sockets also have the following additional methods and attributes:

.. versionadded:: 3.2

.. attribute:: SSLSocket.outer_server_hostname

Hostname of the server name used in the outer ClientHello when Encrypted Client
Hello (ECH) is used, or ``None`` for server-side socket or if no outer
server name is available.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. attribute:: SSLSocket.server_side

A boolean which is ``True`` for server-side sockets and ``False`` for
Expand Down Expand Up @@ -1622,6 +1738,24 @@ to speed up repeated connections from the same clients.

.. versionadded:: 3.4

.. method:: SSLContext.load_ech_keys(keyfile, password=None, *, for_retry=True)

Load a server's Encrypted Client Hello (ECH) private key and configuration.
The *keyfile* must be the path to a PEM file containing a private key
followed by its matching ECHConfigList.

The *password* argument is used to decrypt encrypted private keys. It accepts
a string, bytes, bytearray, or a callable returning one of those types, as in
:meth:`load_cert_chain`.

Each call adds keys and configurations to those already loaded into the
context. If *for_retry* is true, the new configurations are included in
ECH retry configurations sent to clients.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. method:: SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None)

Load a set of "certification authority" (CA) certificates used to validate
Expand Down Expand Up @@ -1816,6 +1950,30 @@ to speed up repeated connections from the same clients.

.. versionadded:: 3.5

.. method:: SSLContext.set_ech_config(ech_config)

Sets an Encrypted Client Hello (ECH) configuration, which may be discovered from
an HTTPS resource record in DNS or from :meth:`SSLSocket.get_ech_retry_config`.

The configuration applies to every connection made from the context, and a
server that cannot decrypt it rejects the connection with
:exc:`SSLECHRejectedError`. Because ECH configurations are published per
host, a context with a configuration should only be used for connections to
the hosts that publish it. To use a configuration for a single connection,
pass it as the *ech_config* argument of :meth:`wrap_socket` or
:meth:`wrap_bio` instead.

If the input value provided contains no suitable value (such as if it only contains
ECH configuration versions that are not supported), an :class:`SSLError` will be
raised.

The *ech_config* parameter should be a :term:`bytes-like object` containing
a binary-encoded ECHConfigList.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. method:: SSLContext.set_npn_protocols(npn_protocols)

Specify which protocols the socket should advertise during the SSL/TLS
Expand All @@ -1835,6 +1993,17 @@ to speed up repeated connections from the same clients.

NPN has been superseded by ALPN

.. method:: SSLContext.set_outer_alpn_protocols(protocols)

Specify which protocols the socket should advertise during the TLS
handshake in the outer ClientHello when ECH is used. The *protocols*
argument accepts the same values as for
:meth:`~SSLContext.set_alpn_protocols`.

Only available when :data:`HAS_ECH` is ``True``.

.. versionadded:: next

.. attribute:: SSLContext.sni_callback

Register a callback function that will be called after the TLS Client Hello
Expand Down Expand Up @@ -1942,7 +2111,7 @@ to speed up repeated connections from the same clients.

.. method:: SSLContext.wrap_socket(sock, server_side=False, \
do_handshake_on_connect=True, suppress_ragged_eofs=True, \
server_hostname=None, session=None)
server_hostname=None, session=None, ech_config=None)

Wrap an existing Python socket *sock* and return an instance of
:attr:`SSLContext.sslsocket_class` (default :class:`SSLSocket`). The
Expand Down Expand Up @@ -1983,6 +2152,10 @@ to speed up repeated connections from the same clients.

*session*, see :attr:`~SSLSocket.session`.

*ech_config*, see :meth:`SSLSocket.set_ech_config`. Specifying
*ech_config* will raise a :exc:`ValueError` if *server_side* is true, and
a :exc:`NotImplementedError` if :data:`HAS_ECH` is ``False``.

To wrap an :class:`SSLSocket` in another :class:`SSLSocket`, use
:meth:`SSLContext.wrap_bio`.

Expand All @@ -1997,6 +2170,9 @@ to speed up repeated connections from the same clients.
The method returns an instance of :attr:`SSLContext.sslsocket_class`
instead of hard-coded :class:`SSLSocket`.

.. versionchanged:: next
*ech_config* argument was added.

.. attribute:: SSLContext.sslsocket_class

The return type of :meth:`SSLContext.wrap_socket`, defaults to
Expand All @@ -2007,16 +2183,17 @@ to speed up repeated connections from the same clients.
.. versionadded:: 3.7

.. method:: SSLContext.wrap_bio(incoming, outgoing, server_side=False, \
server_hostname=None, session=None)
server_hostname=None, session=None, \
ech_config=None)

Wrap the BIO objects *incoming* and *outgoing* and return an instance of
:attr:`SSLContext.sslobject_class` (default :class:`SSLObject`). The SSL
routines will read input data from the incoming BIO and write data to the
outgoing BIO.

The *server_side*, *server_hostname* and *session* parameters have the
same meaning as in :meth:`SSLContext.wrap_socket`, and are validated in
the same way: in particular a :exc:`ValueError` is raised when
The *server_side*, *server_hostname*, *session* and *ech_config*
parameters have the same meaning as in :meth:`SSLContext.wrap_socket`,
and are validated in the same way: in particular a :exc:`ValueError` is raised when
:attr:`~SSLContext.check_hostname` is enabled but no *server_hostname* is
given, since there would be no name to match the peer's certificate
against.
Expand All @@ -2035,6 +2212,9 @@ to speed up repeated connections from the same clients.
*server_hostname* was accepted, and verified the certificate chain but
never the peer's identity.

.. versionchanged:: next
*ech_config* argument was added.

.. attribute:: SSLContext.sslobject_class

The return type of :meth:`SSLContext.wrap_bio`, defaults to
Expand Down Expand Up @@ -2753,6 +2933,9 @@ provided.
- :meth:`~SSLSocket.verify_client_post_handshake`
- :meth:`~SSLSocket.unwrap`
- :meth:`~SSLSocket.get_channel_binding`
- :meth:`~SSLSocket.set_ech_config`
- :meth:`~SSLSocket.get_ech_retry_config`
- :meth:`~SSLSocket.get_ech_status`
- :meth:`~SSLSocket.version`

When compared to :class:`SSLSocket`, this object lacks the following
Expand Down Expand Up @@ -2977,6 +3160,89 @@ of TLS/SSL. Some new TLS 1.3 features are not yet available.
- TLS 1.3 features like early data, deferred TLS client cert request,
and rekeying are not supported yet.

.. _ssl-encrypted-client-hello:

Encrypted Client Hello
^^^^^^^^^^^^^^^^^^^^^^

.. versionadded:: next

Encrypted Client Hello (ECH) encrypts values in the ClientHello record sent when
establishing a TLS connection. It requires an ECHConfigList containing algorithm
parameters, a public key, and a public name used in the unencrypted outer
ClientHello. ECH requires OpenSSL 4.0 or later with ECH enabled; check
:data:`HAS_ECH` before using it.

Servers can load a PEM file containing their ECH private key and configuration
with :meth:`SSLContext.load_ech_keys`. Clients use the corresponding public
ECHConfigList, published in DNS for each host, as the *ech_config* argument of
:meth:`SSLContext.wrap_socket` or :meth:`SSLContext.wrap_bio`, or for every
connection from a context with :meth:`SSLContext.set_ech_config`.

A server that has rotated its ECH keys rejects the connection and provides a
retry configuration, which the handshake raises as
:exc:`SSLECHRejectedError`. The following example accepts a binary-encoded
ECHConfigList discovered through DNS and retries once with the server's retry
configuration. If no configuration is supplied, it enables ECH GREASE, which a
server may answer with a retry configuration for a later connection::

import http.client
import socket
import ssl


def connect_with_tls_ech(hostname, ech_config=None, use_retry_config=True):
context = ssl.create_default_context()
if ech_config is not None:
context.set_ech_config(ech_config)
else:
context.options |= ssl.OP_ECH_GREASE
sock = socket.create_connection((hostname, 443), timeout=10)
try:
ssock = context.wrap_socket(sock, server_hostname=hostname)
except BaseException:
sock.close()
raise
if (use_retry_config
and ssock.get_ech_status()
== ssl.ECHStatus.ECH_STATUS_GREASE_ECH):
try:
retry_config = ssock.get_ech_retry_config()
finally:
ssock.close()
return connect_with_tls_ech(hostname, retry_config, False)
return ssock

hostname = "python.org"
ech_config = None # Replace with an ECHConfigList discovered through DNS.

with connect_with_tls_ech(hostname, ech_config) as ssock:
print("ECH status:", ssock.get_ech_status().name)
request = (
"GET / HTTP/1.1\r\n"
f"Host: {hostname}\r\n"
"Connection: close\r\n\r\n"
)
ssock.sendall(request.encode("ascii"))
with http.client.HTTPResponse(ssock) as response:
response.begin()
print(f"HTTP {response.status} {response.reason}")
for name, value in response.getheaders():
print(f"{name}: {value}")
print()
print(response.read().decode("utf-8", errors="replace"))


The following classes, methods, and attributes will be useful for using ECH:

- :class:`ECHStatus`
- :exc:`SSLECHRejectedError`
- :meth:`SSLContext.load_ech_keys`
- :meth:`SSLContext.set_ech_config`
- :meth:`SSLContext.set_outer_alpn_protocols`
- :meth:`SSLSocket.set_ech_config`
- :meth:`SSLSocket.get_ech_status`
- :meth:`SSLSocket.get_ech_retry_config`

.. seealso::

Expand Down
Loading
Loading