| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 919c7e8 commit 6401823
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -215,6 +215,25 @@ purposes. | |||
| 215 | 215 | :data:`VERIFY_X509_STRICT` in its default verify flags. | |
| 216 | 216 | ||
| 217 | 217 | ||
| 218 | + Signature algorithms | ||
| 219 | + ^^^^^^^^^^^^^^^^^^^^ | ||
| 220 | + | ||
| 221 | + .. function:: get_sigalgs() | ||
| 222 | + | ||
| 223 | + Return a list of available TLS signature algorithm names used | ||
| 224 | + by servers to complete the TLS handshake or clients requesting | ||
| 225 | + certificate-based authentication. For example:: | ||
| 226 | + | ||
| 227 | + >>> ssl.get_sigalgs() # doctest: +SKIP | ||
| 228 | + ['ecdsa_secp256r1_sha256', 'ecdsa_secp384r1_sha384', ...] | ||
| 229 | + | ||
| 230 | + These names can be used when building string values to pass to the | ||
| 231 | + :meth:`SSLContext.set_client_sigalgs` and | ||
| 232 | + :meth:`SSLContext.set_server_sigalgs` methods. | ||
| 233 | + | ||
| 234 | + .. versionadded:: next | ||
| 235 | + | ||
| 236 | + | ||
| 218 | 237 | Exceptions | |
| 219 | 238 | ^^^^^^^^^^ | |
| 220 | 239 | ||
@@ -1297,6 +1316,22 @@ SSL sockets also have the following additional methods and attributes: | |||
| 1297 | 1316 | ||
| 1298 | 1317 | .. versionadded:: next | |
| 1299 | 1318 | ||
| 1319 | + .. method:: SSLSocket.client_sigalg() | ||
| 1320 | + | ||
| 1321 | + Return the signature algorithm used for performing certificate-based client | ||
| 1322 | + authentication on this connection, or ``None`` if no connection has been | ||
| 1323 | + established or client authentication didn't occur. | ||
| 1324 | + | ||
| 1325 | + .. versionadded:: next | ||
| 1326 | + | ||
| 1327 | + .. method:: SSLSocket.server_sigalg() | ||
| 1328 | + | ||
| 1329 | + Return the signature algorithm used by the server to complete the TLS | ||
| 1330 | + handshake on this connection, or ``None`` if no connection has been | ||
| 1331 | + established or the cipher suite has no signature. | ||
| 1332 | + | ||
| 1333 | + .. versionadded:: next | ||
| 1334 | + | ||
| 1300 | 1335 | .. method:: SSLSocket.compression() | |
| 1301 | 1336 | ||
| 1302 | 1337 | Return the compression algorithm being used as a string, or ``None`` | |
@@ -1725,6 +1760,35 @@ to speed up repeated connections from the same clients. | |||
| 1725 | 1760 | ||
| 1726 | 1761 | .. versionadded:: next | |
| 1727 | 1762 | ||
| 1763 | + .. method:: SSLContext.set_client_sigalgs(sigalgs) | ||
| 1764 | + | ||
| 1765 | + Set the signature algorithms allowed for certificate-based client | ||
| 1766 | + authentication. It should be a string in the `OpenSSL client sigalgs | ||
| 1767 | + list format | ||
| 1768 | + <https://docs.openssl.org/master/man3/SSL_CTX_set1_client_sigalgs_list/>`_. | ||
| 1769 | + | ||
| 1770 | + .. note:: | ||
| 1771 | + | ||
| 1772 | + When connected, the :meth:`SSLSocket.client_sigalg` method of SSL | ||
| 1773 | + sockets will return the signature algorithm used for performing | ||
| 1774 | + certificate-based client authentication on that connection. | ||
| 1775 | + | ||
| 1776 | + .. versionadded:: next | ||
| 1777 | + | ||
| 1778 | + .. method:: SSLContext.set_server_sigalgs(sigalgs) | ||
| 1779 | + | ||
| 1780 | + Set the signature algorithms allowed for the server to complete the TLS | ||
| 1781 | + handshake. It should be a string in the `OpenSSL sigalgs list format | ||
| 1782 | + <https://docs.openssl.org/master/man3/SSL_CTX_set1_sigalgs_list/>`_. | ||
| 1783 | + | ||
| 1784 | + .. note:: | ||
| 1785 | + | ||
| 1786 | + When connected, the :meth:`SSLSocket.server_sigalg` method of SSL | ||
| 1787 | + sockets will return the signature algorithm used by the server to | ||
| 1788 | + complete the TLS handshake on that connection. | ||
| 1789 | + | ||
| 1790 | + .. versionadded:: next | ||
| 1791 | + | ||
| 1728 | 1792 | .. method:: SSLContext.set_alpn_protocols(protocols) | |
| 1729 | 1793 | ||
| 1730 | 1794 | Specify which protocols the socket should advertise during the SSL/TLS | |
@@ -2876,7 +2940,7 @@ of TLS/SSL. Some new TLS 1.3 features are not yet available. | |||
| 2876 | 2940 | process certificate requests while they send or receive application data | |
| 2877 | 2941 | from the server. | |
| 2878 | 2942 | - TLS 1.3 features like early data, deferred TLS client cert request, | |
| 2879 | - signature algorithm configuration, and rekeying are not supported yet. | ||
| 2943 | + and rekeying are not supported yet. | ||
| 2880 | 2944 | ||
| 2881 | 2945 | ||
| 2882 | 2946 | .. seealso:: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -442,6 +442,23 @@ ssl | |||
| 442 | 442 | connection is made. | |
| 443 | 443 | (Contributed by Ron Frederick in :gh:`137197`.) | |
| 444 | 444 | ||
| 445 | + * Added new methods for managing signature algorithms: | ||
| 446 | + | ||
| 447 | + * :func:`ssl.get_sigalgs` returns a list of all available TLS signature | ||
| 448 | + algorithms. This call requires OpenSSL 3.4 or later. | ||
| 449 | + * :meth:`ssl.SSLContext.set_client_sigalgs` sets the signature algorithms | ||
| 450 | + allowed for certificate-based client authentication. | ||
| 451 | + * :meth:`ssl.SSLContext.set_server_sigalgs` sets the signature algorithms | ||
| 452 | + allowed for the server to complete the TLS handshake. | ||
| 453 | + * :meth:`ssl.SSLSocket.client_sigalg` returns the signature algorithm | ||
| 454 | + selected for client authentication on the current connection. This call | ||
| 455 | + requires OpenSSL 3.5 or later. | ||
| 456 | + * :meth:`ssl.SSLSocket.server_sigalg` returns the signature algorithm | ||
| 457 | + selected for the server to complete the TLS handshake on the current | ||
| 458 | + connection. This call requires OpenSSL 3.5 or later. | ||
| 459 | + | ||
| 460 | + (Contributed by Ron Frederick in :gh:`138252`.) | ||
| 461 | + | ||
| 445 | 462 | ||
| 446 | 463 | tarfile | |
| 447 | 464 | ------- | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,6 +13,9 @@ | |||
| 13 | 13 | ||
| 14 | 14 | Functions: | |
| 15 | 15 | ||
| 16 | + get_sigalgs -- return a list of all available TLS signature | ||
| 17 | + algorithms (requires OpenSSL 3.4 or later) | ||
| 18 | + | ||
| 16 | 19 | cert_time_to_seconds -- convert time string used for certificate | |
| 17 | 20 | notBefore and notAfter functions to integer | |
| 18 | 21 | seconds past the Epoch (the time values | |
@@ -112,6 +115,7 @@ | |||
| 112 | 115 | except ImportError: | |
| 113 | 116 | # RAND_egd is not supported on some platforms | |
| 114 | 117 | pass | |
| 118 | + from _ssl import get_sigalgs | ||
| 115 | 119 | ||
| 116 | 120 | ||
| 117 | 121 | from _ssl import ( | |
@@ -935,6 +939,14 @@ def group(self): | |||
| 935 | 939 | """Return the currently selected key agreement group name.""" | |
| 936 | 940 | return self._sslobj.group() | |
| 937 | 941 | ||
| 942 | + def client_sigalg(self): | ||
| 943 | + """Return the selected client authentication signature algorithm.""" | ||
| 944 | + return self._sslobj.client_sigalg() | ||
| 945 | + | ||
| 946 | + def server_sigalg(self): | ||
| 947 | + """Return the selected server handshake signature algorithm.""" | ||
| 948 | + return self._sslobj.server_sigalg() | ||
| 949 | + | ||
| 938 | 950 | def shared_ciphers(self): | |
| 939 | 951 | """Return a list of ciphers shared by the client during the handshake or | |
| 940 | 952 | None if this is not a valid server connection. | |
@@ -1222,6 +1234,22 @@ def group(self): | |||
| 1222 | 1234 | else: | |
| 1223 | 1235 | return self._sslobj.group() | |
| 1224 | 1236 | ||
| 1237 | + @_sslcopydoc | ||
| 1238 | + def client_sigalg(self): | ||
| 1239 | + self._checkClosed() | ||
| 1240 | + if self._sslobj is None: | ||
| 1241 | + return None | ||
| 1242 | + else: | ||
| 1243 | + return self._sslobj.client_sigalg() | ||
| 1244 | + | ||
| 1245 | + @_sslcopydoc | ||
| 1246 | + def server_sigalg(self): | ||
| 1247 | + self._checkClosed() | ||
| 1248 | + if self._sslobj is None: | ||
| 1249 | + return None | ||
| 1250 | + else: | ||
| 1251 | + return self._sslobj.server_sigalg() | ||
| 1252 | + | ||
| 1225 | 1253 | @_sslcopydoc | |
| 1226 | 1254 | def shared_ciphers(self): | |
| 1227 | 1255 | self._checkClosed() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,6 +51,10 @@ | |||
| 51 | 51 | CAN_GET_SELECTED_OPENSSL_GROUP = ssl.OPENSSL_VERSION_INFO >= (3, 2) | |
| 52 | 52 | CAN_IGNORE_UNKNOWN_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 3) | |
| 53 | 53 | CAN_GET_AVAILABLE_OPENSSL_GROUPS = ssl.OPENSSL_VERSION_INFO >= (3, 5) | |
| 54 | + CAN_GET_AVAILABLE_OPENSSL_SIGALGS = ssl.OPENSSL_VERSION_INFO >= (3, 4) | ||
| 55 | + CAN_SET_CLIENT_SIGALGS = "AWS-LC" not in ssl.OPENSSL_VERSION | ||
| 56 | + CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS = ssl.OPENSSL_VERSION_INFO >= (3, 3) | ||
| 57 | + CAN_GET_SELECTED_OPENSSL_SIGALG = ssl.OPENSSL_VERSION_INFO >= (3, 5) | ||
| 54 | 58 | PY_SSL_DEFAULT_CIPHERS = sysconfig.get_config_var('PY_SSL_DEFAULT_CIPHERS') | |
| 55 | 59 | ||
| 56 | 60 | PROTOCOL_TO_TLS_VERSION = {} | |
@@ -294,7 +298,8 @@ def test_wrap_socket(sock, *, | |||
| 294 | 298 | USE_SAME_TEST_CONTEXT = False | |
| 295 | 299 | _TEST_CONTEXT = None | |
| 296 | 300 | ||
| 297 | - def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True): | ||
| 301 | + def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True, | ||
| 302 | + client_cert=None): | ||
| 298 | 303 | """Create context | |
| 299 | 304 | ||
| 300 | 305 | client_context, server_context, hostname = testing_context() | |
@@ -321,6 +326,10 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True): | |||
| 321 | 326 | if server_chain: | |
| 322 | 327 | server_context.load_verify_locations(SIGNING_CA) | |
| 323 | 328 | ||
| 329 | + if client_cert: | ||
| 330 | + client_context.load_cert_chain(client_cert) | ||
| 331 | + server_context.verify_mode = ssl.CERT_REQUIRED | ||
| 332 | + | ||
| 324 | 333 | if USE_SAME_TEST_CONTEXT: | |
| 325 | 334 | if _TEST_CONTEXT is not None: | |
| 326 | 335 | _TEST_CONTEXT = client_context, server_context, hostname | |
@@ -990,6 +999,37 @@ def test_get_groups(self): | |||
| 990 | 999 | self.assertNotIn('P-256', ctx.get_groups()) | |
| 991 | 1000 | self.assertIn('P-256', ctx.get_groups(include_aliases=True)) | |
| 992 | 1001 | ||
| 1002 | + @unittest.skipUnless(CAN_GET_AVAILABLE_OPENSSL_SIGALGS, | ||
| 1003 | + "SSL library doesn't support getting sigalgs") | ||
| 1004 | + def test_get_sigalgs(self): | ||
| 1005 | + self.assertIn('rsa_pss_rsae_sha256', ssl.get_sigalgs()) | ||
| 1006 | + | ||
| 1007 | + @unittest.skipUnless(CAN_SET_CLIENT_SIGALGS, | ||
| 1008 | + "SSL library doesn't support setting client sigalgs") | ||
| 1009 | + def test_set_client_sigalgs(self): | ||
| 1010 | + ctx = ssl.create_default_context() | ||
| 1011 | + | ||
| 1012 | + self.assertIsNone(ctx.set_client_sigalgs('rsa_pss_rsae_sha256')) | ||
| 1013 | + | ||
| 1014 | + self.assertRaises(ssl.SSLError, ctx.set_client_sigalgs, | ||
| 1015 | + 'rsa_pss_rsae_sha256:foo') | ||
| 1016 | + | ||
| 1017 | + # Ignoring unknown sigalgs is only supported since OpenSSL 3.3. | ||
| 1018 | + if CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS: | ||
| 1019 | + self.assertIsNone(ctx.set_client_sigalgs('rsa_pss_rsae_sha256:?foo')) | ||
| 1020 | + | ||
| 1021 | + def test_set_server_sigalgs(self): | ||
| 1022 | + ctx = ssl.create_default_context() | ||
| 1023 | + | ||
| 1024 | + self.assertIsNone(ctx.set_server_sigalgs('rsa_pss_rsae_sha256')) | ||
| 1025 | + | ||
| 1026 | + self.assertRaises(ssl.SSLError, ctx.set_server_sigalgs, | ||
| 1027 | + 'rsa_pss_rsae_sha256:foo') | ||
| 1028 | + | ||
| 1029 | + # Ignoring unknown sigalgs is only supported since OpenSSL 3.3. | ||
| 1030 | + if CAN_IGNORE_UNKNOWN_OPENSSL_SIGALGS: | ||
| 1031 | + self.assertIsNone(ctx.set_server_sigalgs('rsa_pss_rsae_sha256:?foo')) | ||
| 1032 | + | ||
| 993 | 1033 | def test_options(self): | |
| 994 | 1034 | # Test default SSLContext options | |
| 995 | 1035 | ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) | |
@@ -2814,6 +2854,9 @@ def server_params_test(client_context, server_context, indata=b"FOO\n", | |||
| 2814 | 2854 | }) | |
| 2815 | 2855 | if CAN_GET_SELECTED_OPENSSL_GROUP: | |
| 2816 | 2856 | stats.update({'group': s.group()}) | |
| 2857 | + if CAN_GET_SELECTED_OPENSSL_SIGALG: | ||
| 2858 | + stats.update({'client_sigalg': s.client_sigalg()}) | ||
| 2859 | + stats.update({'server_sigalg': s.server_sigalg()}) | ||
| 2817 | 2860 | s.close() | |
| 2818 | 2861 | stats['server_alpn_protocols'] = server.selected_alpn_protocols | |
| 2819 | 2862 | stats['server_shared_ciphers'] = server.shared_ciphers | |
@@ -4273,6 +4316,71 @@ def test_groups(self): | |||
| 4273 | 4316 | chatty=True, connectionchatty=True, | |
| 4274 | 4317 | sni_name=hostname) | |
| 4275 | 4318 | ||
| 4319 | + @unittest.skipUnless(CAN_SET_CLIENT_SIGALGS, | ||
| 4320 | + "SSL library doesn't support setting client sigalgs") | ||
| 4321 | + def test_client_sigalgs(self): | ||
| 4322 | + # no mutual auth, so cient_sigalg should be None | ||
| 4323 | + client_context, server_context, hostname = testing_context() | ||
| 4324 | + stats = server_params_test(client_context, server_context, | ||
| 4325 | + chatty=True, connectionchatty=True, | ||
| 4326 | + sni_name=hostname) | ||
| 4327 | + if CAN_GET_SELECTED_OPENSSL_SIGALG: | ||
| 4328 | + self.assertIsNone(stats['client_sigalg']) | ||
| 4329 | + | ||
| 4330 | + # server auto, client rsa_pss_rsae_sha384 | ||
| 4331 | + sigalg = "rsa_pss_rsae_sha384" | ||
| 4332 | + client_context, server_context, hostname = \ | ||
| 4333 | + testing_context(client_cert=SIGNED_CERTFILE) | ||
| 4334 | + client_context.set_client_sigalgs(sigalg) | ||
| 4335 | + stats = server_params_test(client_context, server_context, | ||
| 4336 | + chatty=True, connectionchatty=True, | ||
| 4337 | + sni_name=hostname) | ||
| 4338 | + if CAN_GET_SELECTED_OPENSSL_SIGALG: | ||
| 4339 | + self.assertEqual(stats['client_sigalg'], sigalg) | ||
| 4340 | + | ||
| 4341 | + @unittest.skipUnless(CAN_SET_CLIENT_SIGALGS, | ||
| 4342 | + "SSL library doesn't support setting client sigalgs") | ||
| 4343 | + def test_client_sigalgs_mismatch(self): | ||
| 4344 | + client_context, server_context, hostname = \ | ||
| 4345 | + testing_context(client_cert=SIGNED_CERTFILE) | ||
| 4346 | + client_context.set_client_sigalgs("rsa_pss_rsae_sha256") | ||
| 4347 | + server_context.set_client_sigalgs("rsa_pss_rsae_sha384") | ||
| 4348 | + | ||
| 4349 | + # Some systems return ConnectionResetError on handshake failures | ||
| 4350 | + with self.assertRaises((ssl.SSLError, ConnectionResetError)): | ||
| 4351 | + server_params_test(client_context, server_context, | ||
| 4352 | + chatty=True, connectionchatty=True, | ||
| 4353 | + sni_name=hostname) | ||
| 4354 | + | ||
| 4355 | + def test_server_sigalgs(self): | ||
| 4356 | + # server rsa_pss_rsae_sha384, client auto | ||
| 4357 | + sigalg = "rsa_pss_rsae_sha384" | ||
| 4358 | + client_context, server_context, hostname = testing_context() | ||
| 4359 | + server_context.set_server_sigalgs(sigalg) | ||
| 4360 | + stats = server_params_test(client_context, server_context, | ||
| 4361 | + chatty=True, connectionchatty=True, | ||
| 4362 | + sni_name=hostname) | ||
| 4363 | + if CAN_GET_SELECTED_OPENSSL_SIGALG: | ||
| 4364 | + self.assertEqual(stats['server_sigalg'], sigalg) | ||
| 4365 | + | ||
| 4366 | + # server auto, client rsa_pss_rsae_sha384 | ||
| 4367 | + client_context, server_context, hostname = testing_context() | ||
| 4368 | + client_context.set_server_sigalgs(sigalg) | ||
| 4369 | + stats = server_params_test(client_context, server_context, | ||
| 4370 | + chatty=True, connectionchatty=True, | ||
| 4371 | + sni_name=hostname) | ||
| 4372 | + if CAN_GET_SELECTED_OPENSSL_SIGALG: | ||
| 4373 | + self.assertEqual(stats['server_sigalg'], sigalg) | ||
| 4374 | + | ||
| 4375 | + def test_server_sigalgs_mismatch(self): | ||
| 4376 | + client_context, server_context, hostname = testing_context() | ||
| 4377 | + client_context.set_server_sigalgs("rsa_pss_rsae_sha256") | ||
| 4378 | + server_context.set_server_sigalgs("rsa_pss_rsae_sha384") | ||
| 4379 | + with self.assertRaises(ssl.SSLError): | ||
| 4380 | + server_params_test(client_context, server_context, | ||
| 4381 | + chatty=True, connectionchatty=True, | ||
| 4382 | + sni_name=hostname) | ||
| 4383 | + | ||
| 4276 | 4384 | def test_selected_alpn_protocol(self): | |
| 4277 | 4385 | # selected_alpn_protocol() is None unless ALPN is used. | |
| 4278 | 4386 | client_context, server_context, hostname = testing_context() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + :mod:`ssl`: :class:`~ssl.SSLContext` objects can now set client and server | ||
| 2 | + TLS signature algorithms. If Python has been built with OpenSSL 3.5 or later, | ||
| 3 | + :class:`~ssl.SSLSocket` objects can return the signature algorithms selected | ||
| 4 | + on a connection. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments