Skip to content

Commit a6ad433

Browse files
committed
gh-136306: Address additional review comments
1 parent 9b4066b commit a6ad433

4 files changed

Lines changed: 64 additions & 20 deletions

File tree

Doc/library/ssl.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1641,7 +1641,7 @@ to speed up repeated connections from the same clients.
16411641

16421642
.. versionadded:: 3.6
16431643

1644-
.. method:: SSLContext.get_groups()
1644+
.. method:: SSLContext.get_groups(*, include_aliases=False)
16451645

16461646
Get a list of groups implemented for key agreement, taking into account
16471647
the SSLContext's current TLS ``minimum_version`` and ``maximum_version``
@@ -1653,6 +1653,11 @@ to speed up repeated connections from the same clients.
16531653
>>> ctx.get_groups()
16541654
['secp256r1', 'secp384r1', 'secp521r1', 'x25519', 'x448', 'brainpoolP256r1tls13', 'brainpoolP384r1tls13', 'brainpoolP512r1tls13', 'ffdhe2048', 'ffdhe3072', 'ffdhe4096', 'ffdhe6144', 'ffdhe8192', 'MLKEM512', 'MLKEM768', 'MLKEM1024', 'SecP256r1MLKEM768', 'X25519MLKEM768', 'SecP384r1MLKEM1024']
16551655

1656+
By default, this method returns only the preferred IANA names for the
1657+
available groups. However, if the ``include_aliases`` parameter is set to
1658+
:const:`True` this method will also return any associated aliases such as
1659+
the ECDH curve names supported in older versions of OpenSSL.
1660+
16561661
.. versionadded:: next
16571662

16581663
.. method:: SSLContext.set_default_verify_paths()

Doc/whatsnew/3.15.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ ssl
158158
supports "External PSKs" in TLSv1.3, as described in RFC 9258.
159159
(Contributed by Will Childs-Klein in :gh:`133624`.)
160160

161+
* Added new methods for managing groups used for SSL key agreement
162+
163+
* :meth:`SSLContext.set_groups` sets the groups allowed for doing
164+
key agreement, extending the previous :meth:`set_ecdh_curve` method.
165+
This new API provides the ability to list multiple groups and
166+
supports fixed-field and post-quantum groups in addition to ECDH
167+
curves. This method can also be used to control what key shares
168+
are sent in the TLS handshake.
169+
* :meth:`SSLSocket.group` returns the group selected for doing key
170+
agreement on the current connection after the TLS handshake completes.
171+
This call requires OpenSSL 3.2 or later.
172+
* :meth:`SSLContext.get_groups` returns a list of all available key
173+
agreement groups compatible with the minimum and maximum TLS versions
174+
currently set in the context. This call requires OpenSSL 3.5 or later.
175+
176+
(Contributed by Ron Frederick in :gh:`136306`)
177+
161178

162179
tarfile
163180
-------

Lib/test/test_ssl.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
PROTOCOLS = sorted(ssl._PROTOCOL_NAMES)
4949
HOST = socket_helper.HOST
5050
IS_OPENSSL_3_0_0 = ssl.OPENSSL_VERSION_INFO >= (3, 0, 0)
51+
CAN_GET_SELECTED_OPENSSL_GROUP = ssl.OPENSSL_VERSION_INFO >= (3, 2)
52+
CAN_GET_AVAILABLE_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 5)
5153
PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS')
5254

5355
PROTOCOL_TO_TLS_VERSION = {}
@@ -960,14 +962,25 @@ def test_get_ciphers(self):
960962
len(intersection), 2, f"\ngot: {sorted(names)}\nexpected: {sorted(expected)}"
961963
)
962964

963-
def test_groups(self):
965+
def test_set_groups(self):
964966
ctx = ssl.create_default_context()
965-
self.assertIsNone(ctx.set_groups('P-256'))
967+
968+
# Test valid group list
966969
self.assertIsNone(ctx.set_groups('P-256:X25519'))
967970

968-
if ssl.OPENSSL_VERSION_INFO >= (3, 5):
969-
self.assertNotIn('P-256', ctx.get_groups())
970-
self.assertIn('P-256', ctx.get_groups(include_aliases=True))
971+
# Test invalid group list
972+
self.assertRaises(ssl.SSLError, ctx.set_groups, 'P-256:xxx')
973+
974+
@unittest.skipUnless(CAN_GET_AVAILABLE_OPENSSL_GROUPS,
975+
"OpenSSL version doesn't support getting groups")
976+
def test_get_groups(self):
977+
ctx = ssl.create_default_context()
978+
979+
# P-256 isn't an IANA name, so it shouldn't be returned by default
980+
self.assertNotIn('P-256', ctx.get_groups())
981+
982+
# Aliases like P-256 sbould be returned when include_aliases is set
983+
self.assertIn('P-256', ctx.get_groups(include_aliases=True))
971984

972985
def test_options(self):
973986
# Test default SSLContext options
@@ -2710,7 +2723,7 @@ def server_params_test(client_context, server_context, indata=b"FOO\n",
27102723
'session_reused': s.session_reused,
27112724
'session': s.session,
27122725
})
2713-
if ssl.OPENSSL_VERSION_INFO >= (3, 2):
2726+
if CAN_GET_SELECTED_OPENSSL_GROUP:
27142727
stats.update({'group': s.group()})
27152728
s.close()
27162729
stats['server_alpn_protocols'] = server.selected_alpn_protocols
@@ -4146,7 +4159,7 @@ def test_groups(self):
41464159
stats = server_params_test(client_context, server_context,
41474160
chatty=True, connectionchatty=True,
41484161
sni_name=hostname)
4149-
if ssl.OPENSSL_VERSION_INFO >= (3, 2):
4162+
if CAN_GET_SELECTED_OPENSSL_GROUP:
41504163
self.assertEqual(stats['group'], "secp384r1")
41514164

41524165
# server auto, client secp384r1
@@ -4156,7 +4169,7 @@ def test_groups(self):
41564169
stats = server_params_test(client_context, server_context,
41574170
chatty=True, connectionchatty=True,
41584171
sni_name=hostname)
4159-
if ssl.OPENSSL_VERSION_INFO >= (3, 2):
4172+
if CAN_GET_SELECTED_OPENSSL_GROUP:
41604173
self.assertEqual(stats['group'], "secp384r1")
41614174

41624175
# server / client curve mismatch

Modules/_ssl.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,46 +3462,55 @@ _ssl__SSLContext_get_groups_impl(PySSLContext *self, int include_aliases)
34623462
/*[clinic end generated code: output=6d6209dd1051529b input=3e8ee5deb277dcc5]*/
34633463
{
34643464
#if OPENSSL_VERSION_NUMBER >= 0x30500000L
3465-
STACK_OF(OPENSSL_CSTRING) *groups;
3465+
STACK_OF(OPENSSL_CSTRING) *groups = NULL;
34663466
const char *group;
34673467
size_t i, num;
3468-
PyObject *item, *result;
3468+
PyObject *item, *result = NULL;
34693469

34703470
if ((groups = sk_OPENSSL_CSTRING_new_null()) == NULL) {
34713471
_setSSLError(get_state_ctx(self), "Can't allocate stack", 0, __FILE__, __LINE__);
3472-
return NULL;
3472+
goto error;
34733473
}
34743474

3475+
/*
3476+
* Note: The "groups" stack is dynamically allocated, but the strings
3477+
* returned in the stack are references to internal constants which
3478+
* should NOT be modified or freed. They should also be plain ASCII,
3479+
* so there should be no decoding issue when converting to Unicode.
3480+
*/
3481+
34753482
if (!SSL_CTX_get0_implemented_groups(self->ctx, include_aliases, groups)) {
34763483
_setSSLError(get_state_ctx(self), "Can't get groups", 0, __FILE__, __LINE__);
3477-
sk_OPENSSL_CSTRING_free(groups);
3478-
return NULL;
3484+
goto error;
34793485
}
34803486

34813487
num = sk_OPENSSL_CSTRING_num(groups);
34823488
result = PyList_New(num);
34833489
if (result == NULL) {
34843490
_setSSLError(get_state_ctx(self), "Can't allocate list", 0, __FILE__, __LINE__);
3485-
sk_OPENSSL_CSTRING_free(groups);
3486-
return NULL;
3491+
goto error;
34873492
}
34883493

34893494
for (i = 0; i < num; ++i) {
34903495
group = sk_OPENSSL_CSTRING_value(groups, i);
3496+
assert(group != NULL);
3497+
34913498
item = PyUnicode_DecodeFSDefault(group);
34923499

34933500
if (item == NULL) {
34943501
_setSSLError(get_state_ctx(self), "Can't allocate group name", 0, __FILE__, __LINE__);
3495-
Py_XDECREF(result);
3496-
sk_OPENSSL_CSTRING_free(groups);
3497-
return NULL;
3502+
goto error;
34983503
}
34993504

3500-
PyList_SET_ITEM(result, i, PyUnicode_DecodeFSDefault(group));
3505+
PyList_SET_ITEM(result, i, item);
35013506
}
35023507

35033508
sk_OPENSSL_CSTRING_free(groups);
35043509
return result;
3510+
error:
3511+
Py_XDECREF(result);
3512+
sk_OPENSSL_CSTRING_free(groups);
3513+
return NULL;
35053514
#else
35063515
PyErr_SetString(PyExc_NotImplementedError,
35073516
"Getting implemented groups requires OpenSSL 3.5 or later.");

0 commit comments

Comments
 (0)