class ClientAuthenticationTestCase(unittest.TestCase): @unittest.skipIf(not ModernOpenSslServer.is_platform_supported(), 'Not on Linux 64') def test_optional_client_auth(self): # Given a server that supports optional client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.OPTIONAL) as server: server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # SSLyze correctly detects that client auth is optional self.assertEqual(server_info.client_auth_requirement, ClientAuthenticationServerConfigurationEnum.OPTIONAL) @unittest.skipIf(not ModernOpenSslServer.is_platform_supported(), 'Not on Linux 64') def test_required_client_auth(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # SSLyze correctly detects that client auth is required self.assertEqual(server_info.client_auth_requirement, ClientAuthenticationServerConfigurationEnum.REQUIRED)
def test_works_when_client_auth_succeeded(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client provides a client certificate client_creds = ClientAuthenticationCredentials( client_certificate_chain_path=server. get_client_certificate_path(), client_key_path=server.get_client_key_path(), ) server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port, client_auth_credentials=client_creds, ) server_info = server_test.perform() # The plugin works fine plugin = HttpHeadersPlugin() plugin_result = plugin.process_task(server_info, HttpHeadersScanCommand()) self.assertIsNone(plugin_result.expect_ct_header) self.assertTrue(plugin_result.as_text()) self.assertTrue(plugin_result.as_xml())
def test_tls_1_3_write_early_data_fail_when_trying_to_send_more_than_max_early_data(self): # Given a server that supports TLS 1.3 and early data with ModernOpenSslServer(max_early_data=1) as server: # That has a previous TLS 1.3 session with the server session = self._create_tls_1_3_session(server.hostname, server.port) assert session # And the server only accepts 1 byte of early data max_early = session.get_max_early_data() assert 1 == max_early # When creating a new connection sock_early_data = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_early_data.settimeout(5) sock_early_data.connect((server.hostname, server.port)) ssl_client_early_data = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock_early_data, ssl_verify=OpenSslVerifyEnum.NONE ) # That re-uses the previous TLS 1.3 session ssl_client_early_data.set_session(session) assert OpenSslEarlyDataStatusEnum.NOT_SENT == ssl_client_early_data.get_early_data_status() # When sending too much early data # It fails with pytest.raises(OpenSSLError, match='too much early data'): ssl_client_early_data.write_early_data( 'GET / HTTP/1.1\r\nData: {}\r\n\r\n'.format('*' * max_early).encode('ascii') ) ssl_client_early_data.shutdown()
def test_get_dh_info_ecdh_p256(self): with ModernOpenSslServer(cipher="ECDHE-RSA-AES256-SHA", groups="P-256") as server: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_2, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) try: ssl_client.do_handshake() finally: ssl_client.shutdown() dh_info = ssl_client.get_dh_info() assert isinstance(dh_info, NistEcDhKeyExchangeInfo) assert dh_info.key_type == OpenSslEvpPkeyEnum.EC assert dh_info.key_size == 256 assert dh_info.curve == OpenSslEcNidEnum.PRIME256V1 assert len(dh_info.public_key) == 65 assert len(dh_info.x) == 32 assert len(dh_info.y) == 32
def test_works_when_client_auth_succeeded(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client provides a client certificate client_creds = ClientAuthenticationCredentials( client_certificate_chain_path=server. get_client_certificate_path(), client_key_path=server.get_client_key_path(), ) server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port, client_auth_credentials=client_creds, ) server_info = server_test.perform() # SessionResumptionPlugin works fine plugin = SessionResumptionPlugin() plugin_result = plugin.process_task( server_info, SessionResumptionSupportScanCommand()) assert plugin_result.successful_resumptions_nb == 5 assert plugin_result.as_text() assert plugin_result.as_xml()
def test_works_when_client_auth_succeeded(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: server_location = ServerNetworkLocation( hostname=server.hostname, ip_address=server.ip_address, port=server.port) # And sslyze provides a client certificate network_config = ServerNetworkConfiguration( tls_server_name_indication=server.hostname, tls_client_auth_credentials=ClientAuthenticationCredentials( certificate_chain_path=server.get_client_certificate_path( ), key_path=server.get_client_key_path()), ) server_info = check_connectivity_to_server_and_return_info( server_location, network_config) # When testing for resumption, it succeeds result: SessionResumptionSupportScanResult = SessionResumptionSupportImplementation.scan_server( server_info) assert result.session_id_successful_resumptions_count assert result.session_id_resumption_result == TlsResumptionSupportEnum.FULLY_SUPPORTED
def test_http_error(self): # Given a server to scan with ModernOpenSslServer( # And the server will trigger an error when receiving an HTTP request should_reply_to_http_requests=False) as server: server_location = ServerNetworkLocation( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = check_connectivity_to_server_and_return_info( server_location) # When scanning for HTTP headers, it succeeds result: HttpHeadersScanResult = HttpHeadersImplementation.scan_server( server_info) # And the result mention the error returned by the server when sending an HTTP request assert result.http_error_trace assert result.http_request_sent # And the other result fields are not set assert not result.http_path_redirected_to assert not result.expect_ct_header # And a CLI output can be generated assert HttpHeadersImplementation.cli_connector_cls.result_to_console_output( result) # And the result can be converted to JSON result_as_json = HttpHeadersScanResultAsJson.from_orm(result).json() assert result_as_json
def test_server_cipher_ordering(self): configured_ciphers = [ 'ECDHE-RSA-CHACHA20-POLY1305', 'ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES128-SHA256', 'ECDHE-RSA-AES256-SHA384', 'ECDHE-RSA-AES128-SHA', 'ECDHE-RSA-AES256-SHA', 'AES128-GCM-SHA256', 'AES256-GCM-SHA384', 'AES128-SHA256', 'AES256-SHA256', 'AES128-SHA', 'AES256-SHA' ] random.shuffle(configured_ciphers) cipher_string = ":".join(configured_ciphers) with ModernOpenSslServer(cipher=cipher_string, prefer_server_order=True) as server: # And the client does NOT provide a client certificate server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # OpenSslCipherSuitesPlugin works even when a client cert was not supplied plugin = OpenSslCipherSuitesPlugin() plugin_result = plugin.process_task(server_info, Tlsv12ScanCommand()) detected_ciphers = [ c.openssl_name for c in plugin_result.accepted_cipher_list ] assert configured_ciphers == detected_ciphers
def test_cipher_suite_preferred_by_server(self): # Given an ordered list of cipher suites configured_cipher_suites = [ "ECDHE-RSA-CHACHA20-POLY1305", "ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-RSA-AES128-SHA256", "ECDHE-RSA-AES256-SHA384", "ECDHE-RSA-AES128-SHA", "ECDHE-RSA-AES256-SHA", "AES128-GCM-SHA256", "AES256-GCM-SHA384", "AES128-SHA256", "AES256-SHA256", "AES128-SHA", "AES256-SHA", ] random.shuffle(configured_cipher_suites) cipher_string = ":".join(configured_cipher_suites) # And a server that is configured with this list as its prefered cipher suites with ModernOpenSslServer( openssl_cipher_string=cipher_string, should_enable_server_cipher_preference=True ) as server: server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, ip_address=server.ip_address, port=server.port ) server_info = ServerConnectivityTester().perform(server_location) # When scanning for cipher suites, it succeeds result: CipherSuitesScanResult = Tlsv12ScanImplementation.scan_server(server_info) # And the server's cipher suite preference was detected assert result.cipher_suite_preferred_by_server assert configured_cipher_suites[0] == result.cipher_suite_preferred_by_server.cipher_suite.openssl_name
def test_get_dh_info_ecdh_x25519(self): with ModernOpenSslServer(cipher="ECDHE-RSA-AES256-SHA", groups="X25519") as server: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_2, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE, ) try: ssl_client.do_handshake() finally: ssl_client.shutdown() dh_info = ssl_client.get_ephemeral_key() assert isinstance(dh_info, EcDhEphemeralKeyInfo) assert dh_info.type == OpenSslEvpPkeyEnum.X25519 assert dh_info.size == 253 assert dh_info.curve == OpenSslEcNidEnum.X25519 assert len(dh_info.public_bytes) == 32
def test_set_groups_curve_x448(self): # Given a server that supports a bunch of curves with ModernOpenSslServer( cipher="ECDHE-RSA-AES256-SHA", groups="X25519:prime256v1:X448:secp384r1:secp192k1") as server: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) # And a client that only supports a specific curve: X448 ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_2, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE, ) configured_curve = OpenSslEcNidEnum.X448 ssl_client.set_groups([configured_curve]) # When the client connects to the server try: ssl_client.do_handshake() finally: ssl_client.shutdown() # The curve enabled in the client is the one that was used dh_info = ssl_client.get_ephemeral_key() assert isinstance(dh_info, EcDhEphemeralKeyInfo) assert dh_info.curve == configured_curve assert dh_info.type == OpenSslEvpPkeyEnum.X448 assert dh_info.size == 448 assert len(dh_info.public_bytes) == 56
def test_ed25519_certificate(self): # Given a server that is configured with an ED25519 certificate with ModernOpenSslServer( server_certificate_path=Path(__file__).parent.absolute() / "server-ed25519-cert.pem", server_key_path=Path(__file__).parent.absolute() / "server-ed25519-key.pem", ) as server: server_location = ServerNetworkLocation( hostname=server.hostname, port=server.port, ip_address=server.ip_address) server_info = check_connectivity_to_server_and_return_info( server_location) # When running the scan, it succeeds scan_result = CertificateInfoImplementation.scan_server( server_info) assert scan_result.certificate_deployments[ 0].received_certificate_chain # And the result can be converted to JSON result_as_json = CertificateInfoScanResultAsJson.from_orm( scan_result).json() assert result_as_json # And the result can be converted to console output result_as_txt = CertificateInfoImplementation.cli_connector_cls.result_to_console_output( scan_result) assert result_as_txt
def test_succeeds_when_client_auth_failed(self): # Given a server that requires client authentication with ModernOpenSslServer(client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client does NOT provide a client certificate server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, port=server.port, ip_address=server.ip_address ) server_info = ServerConnectivityTester().perform(server_location) # When running the scan, it succeeds plugin_result = CertificateInfoImplementation.scan_server(server_info) assert plugin_result.certificate_deployments[0].received_certificate_chain
def test_optional_client_auth(self): # Given a server that supports optional client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.OPTIONAL) as server: server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, port=server.port, ip_address=server.ip_address) server_info = ServerConnectivityTester().perform(server_location) # SSLyze correctly detects that client auth is optional assert server_info.tls_probing_result.client_auth_requirement == ClientAuthRequirementEnum.OPTIONAL
def test_optional_client_auth(self): # Given a server that supports optional client authentication with ModernOpenSslServer(client_auth_config=ClientAuthConfigEnum.OPTIONAL) as server: server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port ) server_info = server_test.perform() # SSLyze correctly detects that client auth is optional assert server_info.client_auth_requirement == ClientAuthenticationServerConfigurationEnum.OPTIONAL
def test_required_client_auth_tls_1_3(self): # Given a TLS 1.3 server that requires client authentication with ModernOpenSslServer(client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port ) server_info = server_test.perform() # SSLyze correctly detects that client auth is required # TODO(AD): Fix this bug; it should be REQUIRED assert server_info.client_auth_requirement == ClientAuthenticationServerConfigurationEnum.OPTIONAL
def test_required_client_auth_tls_1_3(self): # Given a TLS 1.3 server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, port=server.port, ip_address=server.ip_address) server_info = ServerConnectivityTester().perform(server_location) # SSLyze correctly detects that client auth is required assert server_info.tls_probing_result.client_auth_requirement == ClientAuthRequirementEnum.REQUIRED
def test_succeeds_when_client_auth_failed_tls_1_3(self): # Given a TLS 1.3 server that requires client authentication with ModernOpenSslServer(client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And SSLyze does NOT provide a client certificate server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, ip_address=server.ip_address, port=server.port ) server_info = ServerConnectivityTester().perform(server_location) # When scanning for cipher suites, it succeeds result: CipherSuitesScanResult = Tlsv13ScanImplementation.scan_server(server_info) assert result.accepted_cipher_suites
def test_required_client_auth(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # SSLyze correctly detects that client auth is required self.assertEqual(server_info.client_auth_requirement, ClientAuthenticationServerConfigurationEnum.REQUIRED)
def test_fails_when_client_auth_failed_session(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And sslyze does NOT provide a client certificate server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = ServerConnectivityTester().perform(server_location) # When testing for resumption, it fails with pytest.raises(ClientCertificateRequested): SessionResumptionSupportImplementation.perform(server_info)
def test_tls_1_3(self): # Given a server that supports TLS 1.3 with ModernOpenSslServer() as server: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) # When doing the TLS 1.3 handshake, it succeeds try: ssl_client.do_handshake() finally: ssl_client.shutdown()
def test_fails_when_client_auth_failed(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client does NOT provide a client certificate server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # The plugin fails when a client cert was not supplied plugin = HttpHeadersPlugin() with self.assertRaises(ClientCertificateRequested): plugin.process_task(server_info, HttpHeadersScanCommand())
def test_early_data_enabled(self): # Given a server to scan that supports early data with ModernOpenSslServer(max_early_data=256) as server: server_location = ServerNetworkLocationViaDirectConnection( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = ServerConnectivityTester().perform(server_location) # When testing for early data support, it succeeds result: EarlyDataScanResult = EarlyDataImplementation.perform( server_info) # And the right result is returned assert result.supports_early_data
def test_optional_client_auth(self): # Given a server that supports optional client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.OPTIONAL) as server: server_location = ServerNetworkLocation( hostname=server.hostname, port=server.port, ip_address=server.ip_address) tls_probing_result = check_connectivity_to_server( server_location=server_location, network_configuration=ServerNetworkConfiguration. default_for_server_location(server_location), ) # SSLyze correctly detects that client auth is optional assert tls_probing_result.client_auth_requirement == ClientAuthRequirementEnum.OPTIONAL
def test_client_authentication_tls_1_3(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client provides an invalid client certificate (actually the server cert) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) # When doing the handshake the right error is returned with pytest.raises(ClientCertificateRequested): ssl_client.do_handshake()
def test_early_data_disabled(self): # Given a server to scan that does NOT support early data because it it is disabled with ModernOpenSslServer(max_early_data=None) as server: server_location = ServerNetworkLocation( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = check_connectivity_to_server_and_return_info( server_location) # When testing for early data support, it succeeds result: EarlyDataScanResult = EarlyDataImplementation.scan_server( server_info) # And the right result is returned assert not result.supports_early_data
def test_required_client_auth_tls_1_3(self): # Given a TLS 1.3 server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: server_location = ServerNetworkLocation( hostname=server.hostname, port=server.port, ip_address=server.ip_address) tls_probing_result = check_connectivity_to_server( server_location=server_location, network_configuration=ServerNetworkConfiguration. default_for_server_location(server_location), ) # SSLyze correctly detects that client auth is required assert tls_probing_result.client_auth_requirement == ClientAuthRequirementEnum.REQUIRED
def test_succeeds_when_client_auth_failed(self): # Given a server that requires client authentication with ModernOpenSslServer(client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client does NOT provide a client certificate server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port ) server_info = server_test.perform() # CertificateInfoPlugin works even when a client cert was not supplied plugin = CertificateInfoPlugin() plugin_result = plugin.process_task(server_info, CertificateInfoScanCommand()) assert plugin_result.received_certificate_chain assert plugin_result.as_text() assert plugin_result.as_xml()
def test_client_authentication_no_certificate_supplied(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client does NOT provide a client certificate sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) ssl_client = self._SSL_CLIENT_CLS( ssl_version=OpenSslVersionEnum.SSLV23, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE, ) # When doing the handshake the right error is returned self.assertRaisesRegex(ClientCertificateRequested, 'Server requested a client certificate', ssl_client.do_handshake)
class RobotPluginPluginTestCase(unittest.TestCase): def test_robot_attack_good(self): # Validate the bug fix for https://github.com/nabla-c0d3/sslyze/issues/282 server_test = ServerConnectivityTester(hostname='guide.duo.com') server_info = server_test.perform() plugin = RobotPlugin() plugin_result = plugin.process_task(server_info, RobotScanCommand()) # On Travis CI we sometimes get inconsistent results if IS_RUNNING_ON_TRAVIS: self.assertIn(plugin_result.robot_result_enum, [ RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE, RobotScanResultEnum.UNKNOWN_INCONSISTENT_RESULTS ]) else: self.assertEqual(plugin_result.robot_result_enum, RobotScanResultEnum.NOT_VULNERABLE_NO_ORACLE) self.assertTrue(plugin_result.as_text()) self.assertTrue(plugin_result.as_xml()) @unittest.skip('Not implemented') def test_robot_attack_bad(self): # TODO(AD): Find a vulnerable server? pass @unittest.skipIf(not ModernOpenSslServer.is_platform_supported(), 'Not on Linux 64') def test_fails_when_client_auth_failed(self): # Given a server that requires client authentication with ModernOpenSslServer( client_auth_config=ClientAuthConfigEnum.REQUIRED) as server: # And the client does NOT provide a client certificate server_test = ServerConnectivityTester( hostname=server.hostname, ip_address=server.ip_address, port=server.port) server_info = server_test.perform() # The plugin fails when a client cert was not supplied plugin = RobotPlugin() with self.assertRaises(ClientCertificateRequested): plugin.process_task(server_info, RobotScanCommand())