Skip to content

Conversation

@irl
Copy link

@irl irl commented Jun 12, 2025

Exposes options for clients to use Encrypted Client Hello (ECH) when establishing TLS connections. It is up to the user to source the ECH public keys before making use of these options.

To use these options requires a version of openssl that includes these options, which currently are on a feature branch:

https://github.com/openssl/openssl/tree/feature/ech

We do not expect that the API here will have any substaintial changes when it is merged.

For testing, clone the above repository, checkout the feature/ech branch, and build. Then use this openssl installation when building cPython:

git clone https://github.com/openssl/openssl.git
pushd openssl
git checkout feature/ech
./configure --prefix=$HOME/opt/openssl
make -j8
make install_sw
popd
git clone https://github.com/irl/cpython.git
git checkout issue-89730
./configure --with-pydebug --with-openssl=$HOME/opt/openssl
make -j8

Additionally I have added to the documentation an example of the use of these options, which is built at: https://irl.github.io/cpython/library/ssl.html#encrypted-client-hello


📚 Documentation preview 📚: https://cpython-previews--135435.org.readthedocs.build/

@python-cla-bot
Copy link

python-cla-bot bot commented Jun 12, 2025

All commit authors signed the Contributor License Agreement.

CLA signed

Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you mentioned, the ECH feature is not yet available in OpenSSL (https://github.com/openssl/openssl/tree/feature/ech). I would like to defer this until it's released.

In addition:

  • please remove blank lines as much as possile. Too many blank lines hinder the reading flow.
  • check return codes from OpenSSL (SSL_ech_get1_retry_config may fail for instance, so you should check if it succeeded, and if not, correctly handle the return code)
  • we need tests

Comment on lines 1 to 8
Adds support for Encrypted Client Hello (ECH) to the ssl module. Clients may
use the options exposed to establish TLS connections using ECH. Third-party
libraries like dnspython can be used to query for HTTPS and SVCB records
that contain the public keys required to use ECH with specific servers. If
no public key is available, an option is available to "GREASE" the
connection, and it is possible to retrieve the public key from the retry
configuration sent by servers that support ECH as they terminate the initial
connection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This description should also be put in What's New rather here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what this means.

@bedevere-app
Copy link

bedevere-app bot commented Jun 13, 2025

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@irl irl force-pushed the issue-89730 branch 2 times, most recently from 27dcb2f to 7092144 Compare June 27, 2025 13:03
@irl irl marked this pull request as draft June 27, 2025 13:05
Exposes options for clients to use Encrypted Client Hello (ECH) when
establishing TLS connections. It is up to the user to source the
ECH public keys before making use of these options.
if (es_in == NULL) {
goto fail;
}
es = OSSL_ECHSTORE_new(NULL, NULL));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double closing parenthesis looks like a typo.

if (retval == NULL) {
goto fail;
}
PyTuple_SET_ITEM(retval, 0, PyLong_FromLong(status));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: PyLong_FromLong can return NULL. Should execution continue in that case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No indeed, so this should be checked.

Py_INCREF(Py_None);
}
return retval;
fail:
Copy link

@AdamKorcz AdamKorcz Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWICS retval needs to be free'd to in fail.

context.set_ech_config(ech_config)
with socket.create_connection((hostname, 443)) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
if (ssock.get_ech_status == ECHStatus.ECH_STATUS_GREASE_ECH

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always resolve to False because it is checking ssock.get_ech_status as a bound method object and not the result of the method. It should be ssock.get_ech_status().

Copy link
Member

@picnixz picnixz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering it's still not released in OpenSSL, I prefer that we close this PR and re-open it once it's shipped. There are also conflicts to solve and the implementation is lacking tests and there is no ETA for that feature (we also need to find another way to configure this, because I'm not really happy with the configure check, we may need more checks with OPENSSL_VERSION for instance).

if (retval == NULL) {
goto fail;
}
PyTuple_SET_ITEM(retval, 0, PyLong_FromLong(status));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No indeed, so this should be checked.

/*[clinic end generated code: output=cea53188b338cf80 input=3ef0514cae6ab6ee]*/
{
#ifdef HAVE_OPENSSL_ECH
BIO *es_in = BIO_new_mem_buf(ech_config->buf, ech_config->len);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we avoid cryptic variable names please?

Comment on lines +4804 to +4805
Py_RETURN_NONE;
fail:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Py_RETURN_NONE;
fail:
Py_RETURN_NONE;
fail:

}
OSSL_ECHSTORE_free(es);
BIO_free_all(es_in);
PyBuffer_Release(ech_config);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIR, there is no need for that. The caller handles it implicitly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants