mirror of https://github.com/python/cpython
Issue #27866: Add SSLContext.get_ciphers() method to get a list of all enabled ciphers.
This commit is contained in:
parent
dffa3949c7
commit
25bfcd5d9e
|
@ -1259,6 +1259,62 @@ to speed up repeated connections from the same clients.
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
.. method:: SSLContext.get_ciphers()
|
||||||
|
|
||||||
|
Get a list of enabled ciphers. The list is in order of cipher priority.
|
||||||
|
See :meth:`SSLContext.set_ciphers`.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
>>> ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
|
||||||
|
>>> ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
|
||||||
|
>>> ctx.get_ciphers() # OpenSSL 1.0.x
|
||||||
|
[{'alg_bits': 256,
|
||||||
|
'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA '
|
||||||
|
'Enc=AESGCM(256) Mac=AEAD',
|
||||||
|
'id': 50380848,
|
||||||
|
'name': 'ECDHE-RSA-AES256-GCM-SHA384',
|
||||||
|
'protocol': 'TLSv1/SSLv3',
|
||||||
|
'strength_bits': 256},
|
||||||
|
{'alg_bits': 128,
|
||||||
|
'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA '
|
||||||
|
'Enc=AESGCM(128) Mac=AEAD',
|
||||||
|
'id': 50380847,
|
||||||
|
'name': 'ECDHE-RSA-AES128-GCM-SHA256',
|
||||||
|
'protocol': 'TLSv1/SSLv3',
|
||||||
|
'strength_bits': 128}]
|
||||||
|
|
||||||
|
On OpenSSL 1.1 and newer the cipher dict contains additional fields::
|
||||||
|
>>> ctx.get_ciphers() # OpenSSL 1.1+
|
||||||
|
[{'aead': True,
|
||||||
|
'alg_bits': 256,
|
||||||
|
'auth': 'auth-rsa',
|
||||||
|
'description': 'ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA '
|
||||||
|
'Enc=AESGCM(256) Mac=AEAD',
|
||||||
|
'digest': None,
|
||||||
|
'id': 50380848,
|
||||||
|
'kea': 'kx-ecdhe',
|
||||||
|
'name': 'ECDHE-RSA-AES256-GCM-SHA384',
|
||||||
|
'protocol': 'TLSv1.2',
|
||||||
|
'strength_bits': 256,
|
||||||
|
'symmetric': 'aes-256-gcm'},
|
||||||
|
{'aead': True,
|
||||||
|
'alg_bits': 128,
|
||||||
|
'auth': 'auth-rsa',
|
||||||
|
'description': 'ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=RSA '
|
||||||
|
'Enc=AESGCM(128) Mac=AEAD',
|
||||||
|
'digest': None,
|
||||||
|
'id': 50380847,
|
||||||
|
'kea': 'kx-ecdhe',
|
||||||
|
'name': 'ECDHE-RSA-AES128-GCM-SHA256',
|
||||||
|
'protocol': 'TLSv1.2',
|
||||||
|
'strength_bits': 128,
|
||||||
|
'symmetric': 'aes-128-gcm'}]
|
||||||
|
|
||||||
|
Availability: OpenSSL 1.0.2+
|
||||||
|
|
||||||
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
.. method:: SSLContext.set_default_verify_paths()
|
.. method:: SSLContext.set_default_verify_paths()
|
||||||
|
|
||||||
Load a set of default "certification authority" (CA) certificates from
|
Load a set of default "certification authority" (CA) certificates from
|
||||||
|
|
|
@ -834,6 +834,15 @@ class ContextTests(unittest.TestCase):
|
||||||
with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"):
|
with self.assertRaisesRegex(ssl.SSLError, "No cipher can be selected"):
|
||||||
ctx.set_ciphers("^$:,;?*'dorothyx")
|
ctx.set_ciphers("^$:,;?*'dorothyx")
|
||||||
|
|
||||||
|
@unittest.skipIf(ssl.OPENSSL_VERSION_INFO < (1, 0, 2, 0, 0), 'OpenSSL too old')
|
||||||
|
def test_get_ciphers(self):
|
||||||
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
ctx.set_ciphers('ECDHE+AESGCM:!ECDSA')
|
||||||
|
names = set(d['name'] for d in ctx.get_ciphers())
|
||||||
|
self.assertEqual(names,
|
||||||
|
{'ECDHE-RSA-AES256-GCM-SHA384',
|
||||||
|
'ECDHE-RSA-AES128-GCM-SHA256'})
|
||||||
|
|
||||||
@skip_if_broken_ubuntu_ssl
|
@skip_if_broken_ubuntu_ssl
|
||||||
def test_options(self):
|
def test_options(self):
|
||||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||||
|
|
|
@ -77,6 +77,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #27866: Add SSLContext.get_ciphers() method to get a list of all
|
||||||
|
enabled ciphers.
|
||||||
|
|
||||||
- Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.
|
- Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.
|
||||||
|
|
||||||
- Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
|
- Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
|
||||||
|
|
117
Modules/_ssl.c
117
Modules/_ssl.c
|
@ -1519,6 +1519,76 @@ cipher_to_tuple(const SSL_CIPHER *cipher)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000UL
|
||||||
|
static PyObject *
|
||||||
|
cipher_to_dict(const SSL_CIPHER *cipher)
|
||||||
|
{
|
||||||
|
const char *cipher_name, *cipher_protocol;
|
||||||
|
|
||||||
|
unsigned long cipher_id;
|
||||||
|
int alg_bits, strength_bits, len;
|
||||||
|
char buf[512] = {0};
|
||||||
|
#if OPENSSL_VERSION_1_1
|
||||||
|
int aead, nid;
|
||||||
|
const char *skcipher = NULL, *digest = NULL, *kx = NULL, *auth = NULL;
|
||||||
|
#endif
|
||||||
|
PyObject *retval;
|
||||||
|
|
||||||
|
retval = PyDict_New();
|
||||||
|
if (retval == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* can be NULL */
|
||||||
|
cipher_name = SSL_CIPHER_get_name(cipher);
|
||||||
|
cipher_protocol = SSL_CIPHER_get_version(cipher);
|
||||||
|
cipher_id = SSL_CIPHER_get_id(cipher);
|
||||||
|
SSL_CIPHER_description(cipher, buf, sizeof(buf) - 1);
|
||||||
|
len = strlen(buf);
|
||||||
|
if (len > 1 && buf[len-1] == '\n')
|
||||||
|
buf[len-1] = '\0';
|
||||||
|
strength_bits = SSL_CIPHER_get_bits(cipher, &alg_bits);
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_1_1
|
||||||
|
aead = SSL_CIPHER_is_aead(cipher);
|
||||||
|
nid = SSL_CIPHER_get_cipher_nid(cipher);
|
||||||
|
skcipher = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||||
|
nid = SSL_CIPHER_get_digest_nid(cipher);
|
||||||
|
digest = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||||
|
nid = SSL_CIPHER_get_kx_nid(cipher);
|
||||||
|
kx = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||||
|
nid = SSL_CIPHER_get_auth_nid(cipher);
|
||||||
|
auth = nid != NID_undef ? OBJ_nid2ln(nid) : NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
retval = Py_BuildValue(
|
||||||
|
"{sksssssssisi"
|
||||||
|
#if OPENSSL_VERSION_1_1
|
||||||
|
"sOssssssss"
|
||||||
|
#endif
|
||||||
|
"}",
|
||||||
|
"id", cipher_id,
|
||||||
|
"name", cipher_name,
|
||||||
|
"protocol", cipher_protocol,
|
||||||
|
"description", buf,
|
||||||
|
"strength_bits", strength_bits,
|
||||||
|
"alg_bits", alg_bits
|
||||||
|
#if OPENSSL_VERSION_1_1
|
||||||
|
,"aead", aead ? Py_True : Py_False,
|
||||||
|
"symmetric", skcipher,
|
||||||
|
"digest", digest,
|
||||||
|
"kea", kx,
|
||||||
|
"auth", auth
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(retval);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
_ssl._SSLSocket.shared_ciphers
|
_ssl._SSLSocket.shared_ciphers
|
||||||
[clinic start generated code]*/
|
[clinic start generated code]*/
|
||||||
|
@ -2478,6 +2548,52 @@ _ssl__SSLContext_set_ciphers_impl(PySSLContext *self, const char *cipherlist)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000UL
|
||||||
|
/*[clinic input]
|
||||||
|
_ssl._SSLContext.get_ciphers
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLContext_get_ciphers_impl(PySSLContext *self)
|
||||||
|
/*[clinic end generated code: output=a56e4d68a406dfc4 input=a2aadc9af89b79c5]*/
|
||||||
|
{
|
||||||
|
SSL *ssl = NULL;
|
||||||
|
STACK_OF(SSL_CIPHER) *sk = NULL;
|
||||||
|
SSL_CIPHER *cipher;
|
||||||
|
int i=0;
|
||||||
|
PyObject *result = NULL, *dct;
|
||||||
|
|
||||||
|
ssl = SSL_new(self->ctx);
|
||||||
|
if (ssl == NULL) {
|
||||||
|
_setSSLError(NULL, 0, __FILE__, __LINE__);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
sk = SSL_get_ciphers(ssl);
|
||||||
|
|
||||||
|
result = PyList_New(sk_SSL_CIPHER_num(sk));
|
||||||
|
if (result == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) {
|
||||||
|
cipher = sk_SSL_CIPHER_value(sk, i);
|
||||||
|
dct = cipher_to_dict(cipher);
|
||||||
|
if (dct == NULL) {
|
||||||
|
Py_CLEAR(result);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
PyList_SET_ITEM(result, i, dct);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (ssl != NULL)
|
||||||
|
SSL_free(ssl);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef OPENSSL_NPN_NEGOTIATED
|
#ifdef OPENSSL_NPN_NEGOTIATED
|
||||||
static int
|
static int
|
||||||
do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
|
do_protocol_selection(int alpn, unsigned char **out, unsigned char *outlen,
|
||||||
|
@ -3645,6 +3761,7 @@ static struct PyMethodDef context_methods[] = {
|
||||||
_SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF
|
_SSL__SSLCONTEXT_SET_SERVERNAME_CALLBACK_METHODDEF
|
||||||
_SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF
|
_SSL__SSLCONTEXT_CERT_STORE_STATS_METHODDEF
|
||||||
_SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF
|
_SSL__SSLCONTEXT_GET_CA_CERTS_METHODDEF
|
||||||
|
_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -378,6 +378,27 @@ exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (OPENSSL_VERSION_NUMBER >= 0x10002000UL)
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_ssl__SSLContext_get_ciphers__doc__,
|
||||||
|
"get_ciphers($self, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
|
#define _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF \
|
||||||
|
{"get_ciphers", (PyCFunction)_ssl__SSLContext_get_ciphers, METH_NOARGS, _ssl__SSLContext_get_ciphers__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLContext_get_ciphers_impl(PySSLContext *self);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_ssl__SSLContext_get_ciphers(PySSLContext *self, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
return _ssl__SSLContext_get_ciphers_impl(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* (OPENSSL_VERSION_NUMBER >= 0x10002000UL) */
|
||||||
|
|
||||||
PyDoc_STRVAR(_ssl__SSLContext__set_npn_protocols__doc__,
|
PyDoc_STRVAR(_ssl__SSLContext__set_npn_protocols__doc__,
|
||||||
"_set_npn_protocols($self, protos, /)\n"
|
"_set_npn_protocols($self, protos, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
|
@ -1128,6 +1149,10 @@ exit:
|
||||||
#define _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF
|
#define _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF
|
||||||
#endif /* !defined(_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF) */
|
#endif /* !defined(_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF) */
|
||||||
|
|
||||||
|
#ifndef _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
|
||||||
|
#define _SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF
|
||||||
|
#endif /* !defined(_SSL__SSLCONTEXT_GET_CIPHERS_METHODDEF) */
|
||||||
|
|
||||||
#ifndef _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
|
#ifndef _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
|
||||||
#define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
|
#define _SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF
|
||||||
#endif /* !defined(_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF) */
|
#endif /* !defined(_SSL__SSLCONTEXT_SET_ECDH_CURVE_METHODDEF) */
|
||||||
|
@ -1143,4 +1168,4 @@ exit:
|
||||||
#ifndef _SSL_ENUM_CRLS_METHODDEF
|
#ifndef _SSL_ENUM_CRLS_METHODDEF
|
||||||
#define _SSL_ENUM_CRLS_METHODDEF
|
#define _SSL_ENUM_CRLS_METHODDEF
|
||||||
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
|
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
|
||||||
/*[clinic end generated code: output=6057f95343369849 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=2e7907a7d8f97ccf input=a9049054013a1b77]*/
|
||||||
|
|
Loading…
Reference in New Issue