Skip to content

Commit 0a2b63f

Browse files
[3.10] gh-96931: Fix incorrect results in ssl.SSLSocket.shared_ciphers (GH-96932) (#102919)
gh-96931: Fix incorrect results in ssl.SSLSocket.shared_ciphers (GH-96932) (cherry picked from commit af9c34f) Co-authored-by: Benjamin Fogle <benfogle@gmail.com>
1 parent 206c2b1 commit 0a2b63f

File tree

4 files changed

+33
-12
lines changed

4 files changed

+33
-12
lines changed

Doc/library/ssl.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,7 @@ SSL sockets also have the following additional methods and attributes:
13111311

13121312
.. method:: SSLSocket.shared_ciphers()
13131313

1314-
Return the list of ciphers shared by the client during the handshake. Each
1314+
Return the list of ciphers available in both the client and server. Each
13151315
entry of the returned list is a three-value tuple containing the name of the
13161316
cipher, the version of the SSL protocol that defines its use, and the number
13171317
of secret bits the cipher uses. :meth:`~SSLSocket.shared_ciphers` returns

Lib/test/test_ssl.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -2326,13 +2326,13 @@ def test_bio_handshake(self):
23262326
self.assertIs(sslobj._sslobj.owner, sslobj)
23272327
self.assertIsNone(sslobj.cipher())
23282328
self.assertIsNone(sslobj.version())
2329-
self.assertIsNotNone(sslobj.shared_ciphers())
2329+
self.assertIsNone(sslobj.shared_ciphers())
23302330
self.assertRaises(ValueError, sslobj.getpeercert)
23312331
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
23322332
self.assertIsNone(sslobj.get_channel_binding('tls-unique'))
23332333
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
23342334
self.assertTrue(sslobj.cipher())
2335-
self.assertIsNotNone(sslobj.shared_ciphers())
2335+
self.assertIsNone(sslobj.shared_ciphers())
23362336
self.assertIsNotNone(sslobj.version())
23372337
self.assertTrue(sslobj.getpeercert())
23382338
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
@@ -4310,7 +4310,7 @@ def cb_wrong_return_type(ssl_sock, server_name, initial_context):
43104310
def test_shared_ciphers(self):
43114311
client_context, server_context, hostname = testing_context()
43124312
client_context.set_ciphers("AES128:AES256")
4313-
server_context.set_ciphers("AES256")
4313+
server_context.set_ciphers("AES256:eNULL")
43144314
expected_algs = [
43154315
"AES256", "AES-256",
43164316
# TLS 1.3 ciphers are always enabled
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix incorrect results from :meth:`ssl.SSLSocket.shared_ciphers`

Modules/_ssl.c

+28-8
Original file line numberDiff line numberDiff line change
@@ -2005,24 +2005,44 @@ static PyObject *
20052005
_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self)
20062006
/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/
20072007
{
2008-
STACK_OF(SSL_CIPHER) *ciphers;
2009-
int i;
2008+
STACK_OF(SSL_CIPHER) *server_ciphers;
2009+
STACK_OF(SSL_CIPHER) *client_ciphers;
2010+
int i, len;
20102011
PyObject *res;
2012+
const SSL_CIPHER* cipher;
2013+
2014+
/* Rather than use SSL_get_shared_ciphers, we use an equivalent algorithm because:
2015+
2016+
1) It returns a colon seperated list of strings, in an undefined
2017+
order, that we would have to post process back into tuples.
2018+
2) It will return a truncated string with no indication that it has
2019+
done so, if the buffer is too small.
2020+
*/
20112021

2012-
ciphers = SSL_get_ciphers(self->ssl);
2013-
if (!ciphers)
2022+
server_ciphers = SSL_get_ciphers(self->ssl);
2023+
if (!server_ciphers)
20142024
Py_RETURN_NONE;
2015-
res = PyList_New(sk_SSL_CIPHER_num(ciphers));
2025+
client_ciphers = SSL_get_client_ciphers(self->ssl);
2026+
if (!client_ciphers)
2027+
Py_RETURN_NONE;
2028+
2029+
res = PyList_New(sk_SSL_CIPHER_num(server_ciphers));
20162030
if (!res)
20172031
return NULL;
2018-
for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
2019-
PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i));
2032+
len = 0;
2033+
for (i = 0; i < sk_SSL_CIPHER_num(server_ciphers); i++) {
2034+
cipher = sk_SSL_CIPHER_value(server_ciphers, i);
2035+
if (sk_SSL_CIPHER_find(client_ciphers, cipher) < 0)
2036+
continue;
2037+
2038+
PyObject *tup = cipher_to_tuple(cipher);
20202039
if (!tup) {
20212040
Py_DECREF(res);
20222041
return NULL;
20232042
}
2024-
PyList_SET_ITEM(res, i, tup);
2043+
PyList_SET_ITEM(res, len++, tup);
20252044
}
2045+
Py_SET_SIZE(res, len);
20262046
return res;
20272047
}
20282048

0 commit comments

Comments
 (0)