@@ -321,6 +321,23 @@ Exceptions
321321
322322 A human readable string of the verification error.
323323
324+ .. exception :: SSLECHRejectedError
325+
326+ A subclass of :exc: `SSLError ` raised when the handshake fails because the
327+ server rejected :ref: `Encrypted Client Hello <ssl-encrypted-client-hello >`.
328+ The connection cannot be used. If :attr: `retry_config ` is not empty, the
329+ client may retry once with a new connection using that configuration.
330+
331+ This exception is only raised when :data: `HAS_ECH ` is ``True ``.
332+
333+ .. attribute :: retry_config
334+
335+ The server's ECH retry configuration as a binary-encoded ECHConfigList,
336+ or empty :class: `bytes ` if the server did not provide one or the server
337+ could not be authenticated under the outer server name.
338+
339+ .. versionadded :: next
340+
324341.. exception :: CertificateError
325342
326343 An alias for :exc: `SSLCertVerificationError `.
@@ -1430,7 +1447,10 @@ SSL sockets also have the following additional methods and attributes:
14301447 bytes if no retry configuration is available. When the status returned by
14311448 :meth: `SSLSocket.get_ech_status ` after the handshake is
14321449 :attr: `ECHStatus.ECH_STATUS_GREASE_ECH `, the configuration can be used
1433- for a new connection using ECH.
1450+ for a new connection using ECH. When the server rejects a real ECH
1451+ attempt, the handshake raises :exc: `SSLECHRejectedError `, which carries
1452+ the same configuration in its :attr: `~SSLECHRejectedError.retry_config `
1453+ attribute.
14341454
14351455 Only available when :data: `HAS_ECH ` is ``True ``.
14361456
@@ -1445,6 +1465,26 @@ SSL sockets also have the following additional methods and attributes:
14451465
14461466 .. versionadded :: next
14471467
1468+ .. method :: SSLSocket.set_ech_config(ech_config)
1469+
1470+ Set the Encrypted Client Hello (ECH) configuration for this connection
1471+ only, replacing any configuration set on the context with
1472+ :meth: `SSLContext.set_ech_config `. The *ech_config * parameter accepts the
1473+ same values. Unlike the context's configuration, which applies to every
1474+ connection made from it, a connection's configuration is only used by that
1475+ connection and can therefore be chosen per destination host, which is how
1476+ ECH configurations are published in DNS.
1477+
1478+ The configuration must be set before the handshake starts, so this is
1479+ mostly useful with ``do_handshake_on_connect=False `` or on a socket that
1480+ is not connected yet. :meth: `SSLContext.wrap_socket ` and
1481+ :meth: `SSLContext.wrap_bio ` accept the configuration as their *ech_config *
1482+ argument instead.
1483+
1484+ Only available when :data: `HAS_ECH ` is ``True ``.
1485+
1486+ .. versionadded :: next
1487+
14481488.. method :: SSLSocket.selected_alpn_protocol()
14491489
14501490 Return the protocol that was selected during the TLS handshake. If
@@ -1932,6 +1972,14 @@ to speed up repeated connections from the same clients.
19321972 an HTTPS resource record in DNS or from :meth: `SSLSocket.get_ech_retry_config `.
19331973 Each call replaces the context's previous configuration.
19341974
1975+ The configuration applies to every connection made from the context, and a
1976+ server that cannot decrypt it rejects the connection with
1977+ :exc: `SSLECHRejectedError `. Because ECH configurations are published per
1978+ host, a context with a configuration should only be used for connections to
1979+ the hosts that publish it. To use a configuration for a single connection,
1980+ pass it as the *ech_config * argument of :meth: `wrap_socket ` or
1981+ :meth: `wrap_bio ` instead.
1982+
19351983 If the input value provided contains no suitable value (e.g. if it only contains
19361984 ECH configuration versions that are not supported), an :class: `SSLError ` will be
19371985 raised.
@@ -2080,7 +2128,7 @@ to speed up repeated connections from the same clients.
20802128
20812129.. method :: SSLContext.wrap_socket(sock, server_side=False, \
20822130 do_handshake_on_connect=True, suppress_ragged_eofs=True, \
2083- server_hostname=None, session=None)
2131+ server_hostname=None, session=None, ech_config=None )
20842132
20852133 Wrap an existing Python socket *sock * and return an instance of
20862134 :attr: `SSLContext.sslsocket_class ` (default :class: `SSLSocket `). The
@@ -2121,6 +2169,10 @@ to speed up repeated connections from the same clients.
21212169
21222170 *session *, see :attr: `~SSLSocket.session `.
21232171
2172+ *ech_config *, see :meth: `SSLSocket.set_ech_config `. Specifying
2173+ *ech_config * will raise a :exc: `ValueError ` if *server_side * is true, and
2174+ a :exc: `NotImplementedError ` if :data: `HAS_ECH ` is ``False ``.
2175+
21242176 To wrap an :class: `SSLSocket ` in another :class: `SSLSocket `, use
21252177 :meth: `SSLContext.wrap_bio `.
21262178
@@ -2135,6 +2187,9 @@ to speed up repeated connections from the same clients.
21352187 The method returns an instance of :attr: `SSLContext.sslsocket_class `
21362188 instead of hard-coded :class: `SSLSocket `.
21372189
2190+ .. versionchanged :: next
2191+ *ech_config * argument was added.
2192+
21382193.. attribute :: SSLContext.sslsocket_class
21392194
21402195 The return type of :meth: `SSLContext.wrap_socket `, defaults to
@@ -2145,16 +2200,17 @@ to speed up repeated connections from the same clients.
21452200 .. versionadded :: 3.7
21462201
21472202.. method :: SSLContext.wrap_bio(incoming, outgoing, server_side=False, \
2148- server_hostname=None, session=None)
2203+ server_hostname=None, session=None, \
2204+ ech_config=None)
21492205
21502206 Wrap the BIO objects *incoming * and *outgoing * and return an instance of
21512207 :attr: `SSLContext.sslobject_class ` (default :class: `SSLObject `). The SSL
21522208 routines will read input data from the incoming BIO and write data to the
21532209 outgoing BIO.
21542210
2155- The *server_side *, *server_hostname * and *session * parameters have the
2156- same meaning as in :meth: `SSLContext.wrap_socket `, and are validated in
2157- the same way: in particular a :exc: `ValueError ` is raised when
2211+ The *server_side *, *server_hostname *, *session * and * ech_config *
2212+ parameters have the same meaning as in :meth: `SSLContext.wrap_socket `,
2213+ and are validated in the same way: in particular a :exc: `ValueError ` is raised when
21582214 :attr: `~SSLContext.check_hostname ` is enabled but no *server_hostname * is
21592215 given, since there would be no name to match the peer's certificate
21602216 against.
@@ -2173,6 +2229,9 @@ to speed up repeated connections from the same clients.
21732229 *server_hostname * was accepted, and verified the certificate chain but
21742230 never the peer's identity.
21752231
2232+ .. versionchanged :: next
2233+ *ech_config * argument was added.
2234+
21762235.. attribute :: SSLContext.sslobject_class
21772236
21782237 The return type of :meth: `SSLContext.wrap_bio `, defaults to
@@ -2891,6 +2950,7 @@ provided.
28912950 - :meth: `~SSLSocket.verify_client_post_handshake `
28922951 - :meth: `~SSLSocket.unwrap `
28932952 - :meth: `~SSLSocket.get_channel_binding `
2953+ - :meth: `~SSLSocket.set_ech_config `
28942954 - :meth: `~SSLSocket.get_ech_retry_config `
28952955 - :meth: `~SSLSocket.get_ech_status `
28962956 - :meth: `~SSLSocket.version `
@@ -3132,51 +3192,51 @@ ClientHello. ECH requires OpenSSL 4.0 or later with ECH enabled; check
31323192
31333193Servers can load a PEM file containing their ECH private key and configuration
31343194with :meth: `SSLContext.load_ech_keys `. Clients use the corresponding public
3135- ECHConfigList with :meth: `SSLContext.set_ech_config `.
3195+ ECHConfigList, published in DNS for each host, as the *ech_config * argument of
3196+ :meth: `SSLContext.wrap_socket ` or :meth: `SSLContext.wrap_bio `, or for every
3197+ connection from a context with :meth: `SSLContext.set_ech_config `.
31363198
3137- Configuration values may be discovered through DNS or through the server's
3138- retry configuration. The following example accepts a binary-encoded
3139- ECHConfigList discovered through DNS. If none is supplied, it enables ECH
3140- GREASE to request a retry configuration from the server::
3199+ A server that has rotated its ECH keys rejects the connection and provides a
3200+ retry configuration, which the handshake raises as
3201+ :exc: `SSLECHRejectedError `. The following example accepts a binary-encoded
3202+ ECHConfigList discovered through DNS and retries once with the server's retry
3203+ configuration. If no configuration is supplied, it enables ECH GREASE, which a
3204+ server may answer with a retry configuration for a later connection::
31413205
31423206 import socket
31433207 import ssl
31443208
31453209
3146- def connect_with_tls_ech(hostname, ech_config=None, use_retry_config =True):
3210+ def connect_with_tls_ech(hostname, ech_config=None, retry =True):
31473211 context = ssl.create_default_context()
3148- if ech_config is not None:
3149- context.set_ech_config(ech_config)
3150- else:
3212+ if ech_config is None:
31513213 context.options |= ssl.OP_ECH_GREASE
31523214 sock = socket.create_connection((hostname, 443))
31533215 try:
3154- ssock = context.wrap_socket(sock, server_hostname=hostname)
3155- except BaseException:
3156- sock.close()
3216+ return context.wrap_socket(sock, server_hostname=hostname,
3217+ ech_config=ech_config)
3218+ except ssl.SSLECHRejectedError as exc:
3219+ if retry and exc.retry_config:
3220+ return connect_with_tls_ech(hostname, exc.retry_config,
3221+ retry=False)
31573222 raise
3158- if (use_retry_config
3159- and ssock.get_ech_status()
3160- == ssl.ECHStatus.ECH_STATUS_GREASE_ECH):
3161- try:
3162- retry_config = ssock.get_ech_retry_config()
3163- finally:
3164- ssock.close()
3165- return connect_with_tls_ech(hostname, retry_config, False)
3166- return ssock
31673223
31683224 hostname = "www.python.org"
31693225 ech_config = None # Replace with an ECHConfigList discovered through DNS.
31703226
31713227 with connect_with_tls_ech(hostname, ech_config) as ssock:
31723228 print(ssock.get_ech_status())
3229+ if ssock.get_ech_status() == ssl.ECHStatus.ECH_STATUS_GREASE_ECH:
3230+ print("retry configuration:", ssock.get_ech_retry_config())
31733231
31743232The following classes, methods, and attributes will be useful for using ECH:
31753233
31763234 - :class: `ECHStatus `
3235+ - :exc: `SSLECHRejectedError `
31773236 - :meth: `SSLContext.load_ech_keys `
31783237 - :meth: `SSLContext.set_ech_config `
31793238 - :meth: `SSLContext.set_outer_alpn_protocols `
3239+ - :meth: `SSLSocket.set_ech_config `
31803240 - :meth: `SSLSocket.get_ech_status `
31813241 - :meth: `SSLSocket.get_ech_retry_config `
31823242
0 commit comments