gh-96931: Fix incorrect results in ssl.SSLSocket.shared_ciphers (#96932)

This commit is contained in:
Benjamin Fogle 2023-03-22 10:08:41 -04:00 committed by GitHub
parent ea93bde4ec
commit af9c34f6ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 12 deletions

View File

@ -1218,7 +1218,7 @@ SSL sockets also have the following additional methods and attributes:
.. method:: SSLSocket.shared_ciphers()
Return the list of ciphers shared by the client during the handshake. Each
Return the list of ciphers available in both the client and server. Each
entry of the returned list is a three-value tuple containing the name of the
cipher, the version of the SSL protocol that defines its use, and the number
of secret bits the cipher uses. :meth:`~SSLSocket.shared_ciphers` returns

View File

@ -2082,13 +2082,13 @@ class SimpleBackgroundTests(unittest.TestCase):
self.assertIs(sslobj._sslobj.owner, sslobj)
self.assertIsNone(sslobj.cipher())
self.assertIsNone(sslobj.version())
self.assertIsNotNone(sslobj.shared_ciphers())
self.assertIsNone(sslobj.shared_ciphers())
self.assertRaises(ValueError, sslobj.getpeercert)
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
self.assertIsNone(sslobj.get_channel_binding('tls-unique'))
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
self.assertTrue(sslobj.cipher())
self.assertIsNotNone(sslobj.shared_ciphers())
self.assertIsNone(sslobj.shared_ciphers())
self.assertIsNotNone(sslobj.version())
self.assertTrue(sslobj.getpeercert())
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
@ -4051,7 +4051,7 @@ class ThreadedTests(unittest.TestCase):
def test_shared_ciphers(self):
client_context, server_context, hostname = testing_context()
client_context.set_ciphers("AES128:AES256")
server_context.set_ciphers("AES256")
server_context.set_ciphers("AES256:eNULL")
expected_algs = [
"AES256", "AES-256",
# TLS 1.3 ciphers are always enabled

View File

@ -0,0 +1 @@
Fix incorrect results from :meth:`ssl.SSLSocket.shared_ciphers`

View File

@ -1998,24 +1998,44 @@ static PyObject *
_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self)
/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/
{
STACK_OF(SSL_CIPHER) *ciphers;
int i;
STACK_OF(SSL_CIPHER) *server_ciphers;
STACK_OF(SSL_CIPHER) *client_ciphers;
int i, len;
PyObject *res;
const SSL_CIPHER* cipher;
ciphers = SSL_get_ciphers(self->ssl);
if (!ciphers)
/* Rather than use SSL_get_shared_ciphers, we use an equivalent algorithm because:
1) It returns a colon seperated list of strings, in an undefined
order, that we would have to post process back into tuples.
2) It will return a truncated string with no indication that it has
done so, if the buffer is too small.
*/
server_ciphers = SSL_get_ciphers(self->ssl);
if (!server_ciphers)
Py_RETURN_NONE;
res = PyList_New(sk_SSL_CIPHER_num(ciphers));
client_ciphers = SSL_get_client_ciphers(self->ssl);
if (!client_ciphers)
Py_RETURN_NONE;
res = PyList_New(sk_SSL_CIPHER_num(server_ciphers));
if (!res)
return NULL;
for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i));
len = 0;
for (i = 0; i < sk_SSL_CIPHER_num(server_ciphers); i++) {
cipher = sk_SSL_CIPHER_value(server_ciphers, i);
if (sk_SSL_CIPHER_find(client_ciphers, cipher) < 0)
continue;
PyObject *tup = cipher_to_tuple(cipher);
if (!tup) {
Py_DECREF(res);
return NULL;
}
PyList_SET_ITEM(res, i, tup);
PyList_SET_ITEM(res, len++, tup);
}
Py_SET_SIZE(res, len);
return res;
}