Skip to content

Conversation

ronf
Copy link
Contributor

@ronf ronf commented Jul 29, 2025

This commit adds a new method SSLContext.set_ciphersuites which can be used to set TLS 1.3 cipher suites. It also updates the documentation, unit tests, and "what's new" text. A NEWS blurb will be added shortly.


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

{
int ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist);
if (ret == 0) {
/* Clearing the error queue is necessary on some OpenSSL versions,
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't remove this without understanding which OpenSSL versions it was referring to and whether they are still in use by CPython builds (1.1.x-ish API'd AWS-LC at a minimum, otherwise OpenSSL 3.0+).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The clearing of the error queue is still happening here. I just took advantage of an existing helper function setSSLError to take care of this:

static PyObject *
_setSSLError (_sslmodulestate *state, const char *errstr, int errcode, const char *filename, int lineno)
{
    if (errstr == NULL)
        errcode = ERR_peek_last_error();
    else
        errcode = 0;
    fill_and_set_sslerror(state, NULL, state->PySSLErrorObject, errcode, errstr, lineno, errcode);
    ERR_clear_error();
    return NULL;
}

Copy link
Member

Choose a reason for hiding this comment

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

Let's keep it that way and change it in a follow-up PR instead. Actually, the errcode parameter for _setSSLError is redundant as it's always overwritten.

@ronf ronf requested a review from AA-Turner as a code owner August 9, 2025 00:23
ronf and others added 4 commits August 8, 2025 18:00
Clarify when to use the original set_ciphers (TLS 1.2 and earlier)
vs. the new set_ciphersuites (TLS 1.3) methods and that both can
be used at once.
@ronf ronf force-pushed the ssl-ciphersuite-support branch from f2cda88 to d3375f1 Compare August 9, 2025 01:00
@ronf
Copy link
Contributor Author

ronf commented Aug 9, 2025

I think all the remaining review comments should be addressed in 48e5164. Let me know if you'd like any other changes.

ronf added 2 commits August 9, 2025 13:23
This commit reworks the set_ciphersuites() test cases, moving them into
their own class to avoid any changes to existing tests. It also makes
the cipher selection dynamic to avoid potentially trying to use a cipher
not available in some environments.
@ronf
Copy link
Contributor Author

ronf commented Aug 16, 2025

@picnixz, any further changes required? The build failure appears to be something in the test runner, as the only thing changed in the last checkin was capitalization in the docs. Prior to that, all tests succeeded.

@picnixz
Copy link
Member

picnixz commented Aug 16, 2025

I'm not available before next week but I'll have a look.

@picnixz picnixz self-assigned this Aug 16, 2025
@ronf
Copy link
Contributor Author

ronf commented Aug 17, 2025

That'd be great - thanks!

I spent some time today working on adding methods to manage signature algorithms and will create a new issue & PR for those as soon as these changes are merged.

@picnixz picnixz self-requested a review August 18, 2025 10:36
ciphers yet, but :meth:`SSLContext.get_ciphers` returns them.
- TLS 1.3 uses a disjunct set of cipher suites. All AES-GCM and ChaCha20
cipher suites are enabled by default. To restrict which TLS1.3 ciphers
are allowed, the method :meth:`SSLContext.set_ciphersuites` should be
Copy link
Member

Choose a reason for hiding this comment

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

For posterity: I really hate that OpenSSL named it SSL_CTX_set_ciphersuites for TLS 1.3 and later, and SSL_CTX_set_cipher_list for TLS 1.2 and below. I hope that this won't be annoying to users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was surprised what they did here at first and I'm not a big fan about the naming chosen, but I can see why they didn't want to try and extend the existing command. It allows all kinds of partial matches on the five individual elements that make up a cipher suite in TLS 1.2 and earlier, plus other special keywords like LOW/MEDIUM/HIGH. All of that is gone with TLS 1.3, and the new function is much simpler and only supports an exact match against the very small number of cipher suites defined in TLS 1.3.

I don't really know why they didn't include "_list" in the name of the OpenSSL function, given that this seems to be the convention for other such calls that take a string argument.

The good news here is that things like post-quantum crypto are going to drive migration to TLS 1.3, and at some point setting TLS 1.2 cipher suites will no longer matter. At that point, the previous set_ciphers() can be deprecated and eventually removed, with only set_ciphersuites() remaining.

{
int ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist);
if (ret == 0) {
/* Clearing the error queue is necessary on some OpenSSL versions,
Copy link
Member

Choose a reason for hiding this comment

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

Let's keep it that way and change it in a follow-up PR instead. Actually, the errcode parameter for _setSSLError is redundant as it's always overwritten.

@ronf
Copy link
Contributor Author

ronf commented Aug 21, 2025

Thanks for your review! I think I've addressed this round of review comments, including reverting the change to _ssl__SSLContext_set_ciphers_impl for now. Let me know if anything else is needed.

@picnixz picnixz self-requested a review August 22, 2025 08:11
@ronf
Copy link
Contributor Author

ronf commented Aug 28, 2025

Just checking in - are there any further changes you'd like me to make?

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.

Just a few nitpicks.

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.

A final nitpick and we're good to go I think

@ronf
Copy link
Contributor Author

ronf commented Aug 29, 2025

I've gone ahead and created issue #138252 in preparation for the next round of changes, to add support for getting and setting TLS signature algorithms. I have those changes ready, but will hold off until this change is merged before I submit the PR, to avoid any merge conflicts.

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.

I'm going to amend it directly and merge this.

self.assertRaises(ssl.SSLEOFError, sslobj.read)


@unittest.skipUnless(has_tls_version('TLSv1_3'), "TLS 1.3 is not available")
Copy link
Member

Choose a reason for hiding this comment

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

Note to myself: change the requires_tls_version decorator so that it works.

min_version=ssl.TLSVersion.TLSv1_2,
max_version=ssl.TLSVersion.TLSv1_2) as s:
s.connect(self.server_addr)
self.assertEqual(s.cipher()[1], 'TLSv1.2')
Copy link
Member

Choose a reason for hiding this comment

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

This test is asserting that the default TLSv1_2 cipher suite is actually being used right? I'm fine with the current test but I'd like to fortify it (which I'll do) in the future where we have a ciphersuite for TLSv1.3 and a ciphersuite for TLSv1.2 and if we use min/max versions, then we're going to be using the TLSv1.2 ciphersuite we specified.

@picnixz picnixz enabled auto-merge (squash) August 30, 2025 09:12
@picnixz picnixz merged commit bacb777 into python:main Aug 30, 2025
45 checks passed
@ronf ronf deleted the ssl-ciphersuite-support branch August 30, 2025 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet