Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 25bfcd5

Browse files
committed
Issue #27866: Add SSLContext.get_ciphers() method to get a list of all enabled ciphers.
1 parent dffa394 commit 25bfcd5

5 files changed

Lines changed: 211 additions & 1 deletion

File tree

Doc/library/ssl.rst

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,62 @@ to speed up repeated connections from the same clients.
12591259

12601260
.. versionadded:: 3.4
12611261

1262+
.. method:: SSLContext.get_ciphers()
1263+
1264+
Get a list of enabled ciphers. The list is in order of cipher priority.
1265+
See :meth:`SSLContext.set_ciphers`.
1266+
1267+
Example::
1268+
1269+
>>> ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
1270+
>>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
1271+
>>> ctx.get_ciphers() # OpenSSL 1.0.x
1272+
[{'alg_bits': 256,
1273+
'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA '
1274+
'Enc=AESGCM(256) Mac=AEAD',
1275+
'id': 50380848,
1276+
'name': 'ECDHE-RSA-AES256-GCM-SHA384',
1277+
'protocol': 'TLSv1/SSLv3',
1278+
'strength_bits': 256},
1279+
{'alg_bits': 128,
1280+
'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA '
1281+
'Enc=AESGCM(128) Mac=AEAD',
1282+
'id': 50380847,
1283+
'name': 'ECDHE-RSA-AES128-GCM-SHA256',
1284+
'protocol': 'TLSv1/SSLv3',
1285+
'strength_bits': 128}]
1286+
1287+
On OpenSSL 1.1 and newer the cipher dict contains additional fields::
1288+
>>> ctx.get_ciphers() # OpenSSL 1.1+
1289+
[{'aead': True,
1290+
'alg_bits': 256,
1291+
'auth': 'auth-rsa',
1292+
'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA '
1293+
'Enc=AESGCM(256) Mac=AEAD',
1294+
'digest': None,
1295+
'id': 50380848,
1296+
'kea': 'kx-ecdhe',
1297+
'name': 'ECDHE-RSA-AES256-GCM-SHA384',
1298+
'protocol': 'TLSv1.2',
1299+
'strength_bits': 256,
1300+
'symmetric': 'aes-256-gcm'},
1301+
{'aead': True,
1302+
'alg_bits': 128,
1303+
'auth': 'auth-rsa',
1304+
'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA '
1305+
'Enc=AESGCM(128) Mac=AEAD',
1306+
'digest': None,
1307+
'id': 50380847,
1308+
'kea': 'kx-ecdhe',
1309+
'name': 'ECDHE-RSA-AES128-GCM-SHA256',
1310+
'protocol': 'TLSv1.2',
1311+
'strength_bits': 128,
1312+
'symmetric': 'aes-128-gcm'}]
1313+
1314+
Availability: OpenSSL 1.0.2+
1315+
1316+
.. versionadded:: 3.6
1317+
12621318
.. method:: SSLContext.set_default_verify_paths()
12631319

12641320
Load a set of default "certification authority" (CA) certificates from

Lib/test/test_ssl.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,15 @@ def test_ciphers(self):
834834
with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"):
835835
ctx.set_ciphers("^$:,;?*'dorothyx")
836836

837+
@unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old')
838+
def test_get_ciphers(self):
839+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
840+
ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
841+
names = set(d['name'] for d in ctx.get_ciphers())
842+
self.assertEqual(names,
843+
{'ECDHE-RSA-AES256-GCM-SHA384',
844+
'ECDHE-RSA-AES128-GCM-SHA256'})
845+
837846
@skip_if_broken_ubuntu_ssl
838847
def test_options(self):
839848
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ Core and Builtins
7777
Library
7878
-------
7979

80+
- Issue #27866: Add SSLContext.get_ciphers() method to get a list of all
81+
enabled ciphers.
82+
8083
- Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.
8184

8285
- Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.

Modules/_ssl.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,76 @@ cipher_to_tuple(const SSL_CIPHER *cipher)
15191519
return NULL;
15201520
}
15211521

1522+
#if OPENSSL_VERSION_NUMBER >= 0x10002000UL
1523+
static PyObject *
1524+
cipher_to_dict(const SSL_CIPHER *cipher)
1525+
{
1526+
const char *cipher_name, *cipher_protocol;
1527+
1528+
unsigned long cipher_id;
1529+
int alg_bits, strength_bits, len;
1530+
char buf[512] = {0};
1531+
#if OPENSSL_VERSION_1_1
1532+
int aead, nid;
1533+
const char *skcipher = NULL, *digest = NULL, *kx = NULL, *auth = NULL;
1534+
#endif
1535+
PyObject *retval;
1536+
1537+
retval = PyDict_New();
1538+
if (retval == NULL) {
1539+
goto error;
1540+
}
1541+
1542+
/* can be NULL */
1543+
cipher_name = SSL_CIPHER_get_name(cipher);
1544+
cipher_protocol = SSL_CIPHER_get_version(cipher);
1545+
cipher_id = SSL_CIPHER_get_id(cipher);
1546+
SSL_CIPHER_description(cipher, buf, sizeof(buf) - 1);
1547+
len = strlen(buf);
1548+
if (len > 1 && buf[len-1] == '\n')
1549+
buf[len-1] = '\0';
1550+
strength_bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
1551+
1552+
#if OPENSSL_VERSION_1_1
1553+
aead = SSL_CIPHER_is_aead(cipher);
1554+
nid = SSL_CIPHER_get_cipher_nid(cipher);
1555+
skcipher = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
1556+
nid = SSL_CIPHER_get_digest_nid(cipher);
1557+
digest = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
1558+
nid = SSL_CIPHER_get_kx_nid(cipher);
1559+
kx = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
1560+
nid = SSL_CIPHER_get_auth_nid(cipher);
1561+
auth = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
1562+
#endif
1563+
1564+
retval = Py_BuildValue(
1565+
"{sksssssssisi"
1566+
#if OPENSSL_VERSION_1_1
1567+
"sOssssssss"
1568+
#endif
1569+
"}",
1570+
"id", cipher_id,
1571+
"name", cipher_name,
1572+
"protocol", cipher_protocol,
1573+
"description", buf,
1574+
"strength_bits", strength_bits,
1575+
"alg_bits", alg_bits
1576+
#if OPENSSL_VERSION_1_1
1577+
,"aead", aead ? Py_True : Py_False,
1578+
"symmetric", skcipher,
1579+
"digest", digest,
1580+
"kea", kx,
1581+
"auth", auth
1582+
#endif
1583+
);
1584+
return retval;
1585+
1586+
error:
1587+
Py_XDECREF(retval);
1588+
return NULL;
1589+
}
1590+
#endif
1591+
15221592
/*[clinic input]
15231593
_ssl._SSLSocket.shared_ciphers
15241594
[clinic start generated code]*/
@@ -2478,6 +2548,52 @@ _ssl__SSLContext_set_ciphers_impl(PySSLContext *self, const char *cipherlist)
24782548
Py_RETURN_NONE;
24792549
}
24802550

2551+
#if OPENSSL_VERSION_NUMBER >= 0x10002000UL
2552+
/*[clinic input]
2553+
_ssl._SSLContext.get_ciphers
2554+
[clinic start generated code]*/
2555+
2556+
static PyObject *
2557+
_ssl__SSLContext_get_ciphers_impl(PySSLContext *self)
2558+
/*[clinic end generated code: output=a56e4d68a406dfc4 input=a2aadc9af89b79c5]*/
2559+
{
2560+
SSL *ssl = NULL;
2561+
STACK_OF(SSL_CIPHER) *sk = NULL;
2562+
SSL_CIPHER *cipher;
2563+
int i=0;
2564+
PyObject *result = NULL, *dct;
2565+
2566+
ssl = SSL_new(self->ctx);
2567+
if (ssl == NULL) {
2568+
_setSSLError(NULL, 0, __FILE__, __LINE__);
2569+
goto exit;
2570+
}
2571+
sk = SSL_get_ciphers(ssl);
2572+
2573+
result = PyList_New(sk_SSL_CIPHER_num(sk));
2574+
if (result == NULL) {
2575+
goto exit;
2576+
}
2577+
2578+
for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
2579+
cipher = sk_SSL_CIPHER_value(sk, i);
2580+
dct = cipher_to_dict(cipher);
2581+
if (dct == NULL) {
2582+
Py_CLEAR(result);
2583+
goto exit;
2584+
}
2585+
PyList_SET_ITEM(result, i, dct);
2586+
}
2587+
2588+
exit:
2589+
if (ssl != NULL)
2590+
SSL_free(ssl);
2591+
return result;
2592+
2593+
}
2594+
#endif
2595+
2596+
24812597
#ifdef OPENSSL_NPN_NEGOTIATED
24822598
static int
24832599
do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
@@ -3645,6 +3761,7 @@ static struct PyMethodDef context_methods[] = {
36453761
_SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF
36463762
_SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF
36473763
_SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF
3764+
_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
36483765
{NULL, NULL} /* sentinel */
36493766
};
36503767

Modules/clinic/_ssl.c.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,27 @@ _ssl__SSLContext_set_ciphers(PySSLContext *self, PyObject *arg)
378378
return return_value;
379379
}
380380

381+
#if (OPENSSL_VERSION_NUMBER >= 0x10002000UL)
382+
383+
PyDoc_STRVAR(_ssl__SSLContext_get_ciphers__doc__,
384+
"get_ciphers($self, /)\n"
385+
"--\n"
386+
"\n");
387+
388+
#define _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF \
389+
{"get_ciphers", (PyCFunction)_ssl__SSLContext_get_ciphers, METH_NOARGS, _ssl__SSLContext_get_ciphers__doc__},
390+
391+
static PyObject *
392+
_ssl__SSLContext_get_ciphers_impl(PySSLContext *self);
393+
394+
static PyObject *
395+
_ssl__SSLContext_get_ciphers(PySSLContext *self, PyObject *Py_UNUSED(ignored))
396+
{
397+
return _ssl__SSLContext_get_ciphers_impl(self);
398+
}
399+
400+
#endif /* (OPENSSL_VERSION_NUMBER >= 0x10002000UL) */
401+
381402
PyDoc_STRVAR(_ssl__SSLContext__set_npn_protocols__doc__,
382403
"_set_npn_protocols($self, protos, /)\n"
383404
"--\n"
@@ -1128,6 +1149,10 @@ _ssl_enum_crls(PyObject *module, PyObject *args, PyObject *kwargs)
11281149
#define _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF
11291150
#endif /* !defined(_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF) */
11301151

1152+
#ifndef _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
1153+
#define _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
1154+
#endif /* !defined(_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF) */
1155+
11311156
#ifndef _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
11321157
#define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
11331158
#endif /* !defined(_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF) */
@@ -1143,4 +1168,4 @@ _ssl_enum_crls(PyObject *module, PyObject *args, PyObject *kwargs)
11431168
#ifndef _SSL_ENUM_CRLS_METHODDEF
11441169
#define _SSL_ENUM_CRLS_METHODDEF
11451170
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
1146-
/*[clinic end generated code: output=6057f95343369849 input=a9049054013a1b77]*/
1171+
/*[clinic end generated code: output=2e7907a7d8f97ccf input=a9049054013a1b77]*/

0 commit comments

Comments
 (0)