def _test_cipher_suite(server_connectivity_info, ssl_version, openssl_cipher_name): # type: (ServerConnectivityInfo, OpenSslVersionEnum, Text) -> CipherSuite """Initiates a SSL handshake with the server using the SSL version and the cipher suite specified. """ requires_legacy_openssl = None if ssl_version == OpenSslVersionEnum.TLSV1_2: # For TLS 1.2, we need to pick the right version of OpenSSL depending on which cipher suite requires_legacy_openssl = WorkaroundForTls12ForCipherSuites.requires_legacy_openssl(openssl_cipher_name) ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection( override_ssl_version=ssl_version, should_use_legacy_openssl=requires_legacy_openssl ) ssl_connection.ssl_client.set_cipher_list(openssl_cipher_name) if len(ssl_connection.ssl_client.get_cipher_list()) != 1: raise ValueError('Passed an OpenSSL string for multiple cipher suites: "{}"'.format(openssl_cipher_name)) try: # Perform the SSL handshake ssl_connection.connect() cipher_result = AcceptedCipherSuite.from_ongoing_ssl_connection(ssl_connection, ssl_version) except SSLHandshakeRejected as e: cipher_result = RejectedCipherSuite(openssl_cipher_name, ssl_version, str(e)) except ClientCertificateRequested: cipher_result = AcceptedCipherSuite.from_ongoing_ssl_connection(ssl_connection, ssl_version) except Exception as e: cipher_result = ErroredCipherSuite(openssl_cipher_name, ssl_version, e) finally: ssl_connection.close() return cipher_result
def _get_preferred_cipher_suite( cls, server_connectivity_info: ServerConnectivityInfo, ssl_version: OpenSslVersionEnum, accepted_cipher_list: List["AcceptedCipherSuite"], ) -> Optional["AcceptedCipherSuite"]: """Try to detect the server's preferred cipher suite among all cipher suites supported by SSLyze. """ if len(accepted_cipher_list) < 2: return None accepted_cipher_names = [ cipher.openssl_name for cipher in accepted_cipher_list ] should_use_legacy_openssl = None # For TLS 1.2, we need to figure whether the modern or legacy OpenSSL should be used to connect if ssl_version == OpenSslVersionEnum.TLSV1_2: should_use_legacy_openssl = True # If there are more than two modern-supported cipher suites, use the modern OpenSSL for cipher_name in accepted_cipher_names: modern_supported_cipher_count = 0 if not WorkaroundForTls12ForCipherSuites.requires_legacy_openssl( cipher_name): modern_supported_cipher_count += 1 if modern_supported_cipher_count > 1: should_use_legacy_openssl = False break first_cipher_str = ", ".join(accepted_cipher_names) # Swap the first two ciphers in the list to see if the server always picks the client's first cipher second_cipher_str = ", ".join( [accepted_cipher_names[1], accepted_cipher_names[0]] + accepted_cipher_names[2:]) try: first_cipher = cls._get_selected_cipher_suite( server_connectivity_info, ssl_version, first_cipher_str, should_use_legacy_openssl) second_cipher = cls._get_selected_cipher_suite( server_connectivity_info, ssl_version, second_cipher_str, should_use_legacy_openssl) except (SslHandshakeRejected, ConnectionError): # Could not complete a handshake return None if first_cipher.name == second_cipher.name: # The server has its own preference for picking a cipher suite return first_cipher else: # The server has no preferred cipher suite as it follows the client's preference for picking a cipher suite return None
def _get_preferred_cipher_suite( cls, server_connectivity_info: ServerConnectivityInfo, ssl_version: OpenSslVersionEnum, accepted_cipher_list: List['AcceptedCipherSuite'] ) -> Optional['AcceptedCipherSuite']: """Try to detect the server's preferred cipher suite among all cipher suites supported by SSLyze. """ if len(accepted_cipher_list) < 2: return None accepted_cipher_names = [cipher.openssl_name for cipher in accepted_cipher_list] should_use_legacy_openssl = None # For TLS 1.2, we need to figure whether the modern or legacy OpenSSL should be used to connect if ssl_version == OpenSslVersionEnum.TLSV1_2: should_use_legacy_openssl = True # If there are more than two modern-supported cipher suites, use the modern OpenSSL for cipher_name in accepted_cipher_names: modern_supported_cipher_count = 0 if not WorkaroundForTls12ForCipherSuites.requires_legacy_openssl(cipher_name): modern_supported_cipher_count += 1 if modern_supported_cipher_count > 1: should_use_legacy_openssl = False break first_cipher_str = ', '.join(accepted_cipher_names) # Swap the first two ciphers in the list to see if the server always picks the client's first cipher second_cipher_str = ', '.join([accepted_cipher_names[1], accepted_cipher_names[0]] + accepted_cipher_names[2:]) try: first_cipher = cls._get_selected_cipher_suite( server_connectivity_info, ssl_version, first_cipher_str, should_use_legacy_openssl ) second_cipher = cls._get_selected_cipher_suite( server_connectivity_info, ssl_version, second_cipher_str, should_use_legacy_openssl ) except (SslHandshakeRejected, ConnectionError): # Could not complete a handshake return None if first_cipher.name == second_cipher.name: # The server has its own preference for picking a cipher suite return first_cipher else: # The server has no preferred cipher suite as it follows the client's preference for picking a cipher suite return None
def _should_use_legacy_openssl(self, ssl_version, accepted_ciphers): # For TLS 1.2, we need to figure whether the modern or legacy OpenSSL should be used to connect should_use_legacy_openssl = None if ssl_version == OpenSslVersionEnum.TLSV1_2: should_use_legacy_openssl = True # If there are more than two modern-supported cipher suites, use the modern OpenSSL for cipher in accepted_ciphers: modern_supported_cipher_count = 0 if not WorkaroundForTls12ForCipherSuites.requires_legacy_openssl(cipher.openssl_name): modern_supported_cipher_count += 1 if modern_supported_cipher_count > 1: should_use_legacy_openssl = False break return should_use_legacy_openssl
def _test_cipher_suite(server_connectivity_info, ssl_version, openssl_cipher_name): # type: (ServerConnectivityInfo, OpenSslVersionEnum, Text) -> CipherSuite """Initiates a SSL handshake with the server using the SSL version and the cipher suite specified. """ requires_legacy_openssl = None if ssl_version == OpenSslVersionEnum.TLSV1_2: # For TLS 1.2, we need to pick the right version of OpenSSL depending on which cipher suite requires_legacy_openssl = WorkaroundForTls12ForCipherSuites.requires_legacy_openssl( openssl_cipher_name) ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection( override_ssl_version=ssl_version, should_use_legacy_openssl=requires_legacy_openssl) ssl_connection.ssl_client.set_cipher_list(openssl_cipher_name) if len(ssl_connection.ssl_client.get_cipher_list()) != 1: raise ValueError( 'Passed an OpenSSL string for multiple cipher suites: "{}"'. format(openssl_cipher_name)) try: # Perform the SSL handshake ssl_connection.connect() cipher_result = AcceptedCipherSuite.from_ongoing_ssl_connection( ssl_connection, ssl_version) # type: CipherSuite except SSLHandshakeRejected as e: cipher_result = RejectedCipherSuite(openssl_cipher_name, ssl_version, str(e)) except ClientCertificateRequested: cipher_result = AcceptedCipherSuite.from_ongoing_ssl_connection( ssl_connection, ssl_version) except Exception as e: cipher_result = ErroredCipherSuite(openssl_cipher_name, ssl_version, e) finally: ssl_connection.close() return cipher_result
def _test_cipher_suite(server_connectivity_info: ServerConnectivityInfo, ssl_version: OpenSslVersionEnum, openssl_cipher_name: str) -> 'CipherSuite': """Initiates a SSL handshake with the server using the SSL version and the cipher suite specified. """ requires_legacy_openssl = True if ssl_version == OpenSslVersionEnum.TLSV1_2: # For TLS 1.2, we need to pick the right version of OpenSSL depending on which cipher suite requires_legacy_openssl = WorkaroundForTls12ForCipherSuites.requires_legacy_openssl( openssl_cipher_name) elif ssl_version == OpenSslVersionEnum.TLSV1_3: requires_legacy_openssl = False ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection( override_ssl_version=ssl_version, should_use_legacy_openssl=requires_legacy_openssl) # Only enable the cipher suite to test; not trivial anymore since OpenSSL 1.1.1 and TLS 1.3 if ssl_version == OpenSslVersionEnum.TLSV1_3: # The function to control cipher suites is different for TLS 1.3 # Disable the default, non-TLS 1.3 cipher suites ssl_connection.ssl_client.set_cipher_list('') # Enable the one TLS 1.3 cipher suite we want to test ssl_connection.ssl_client.set_ciphersuites(openssl_cipher_name) else: if not requires_legacy_openssl: # Disable the TLS 1.3 cipher suites if we are using the modern client ssl_connection.ssl_client.set_ciphersuites('') ssl_connection.ssl_client.set_cipher_list(openssl_cipher_name) if len(ssl_connection.ssl_client.get_cipher_list()) != 1: raise ValueError( f'Passed an OpenSSL string for multiple cipher suites: "{openssl_cipher_name}": ' f'{str(ssl_connection.ssl_client.get_cipher_list())}') try: # Perform the SSL handshake ssl_connection.connect() cipher_result: CipherSuite = AcceptedCipherSuite.from_ongoing_ssl_connection( ssl_connection, ssl_version) except SslHandshakeRejected as e: cipher_result = RejectedCipherSuite(openssl_cipher_name, ssl_version, str(e)) except ClientCertificateRequested: # TODO(AD): Sometimes get_current_cipher_name() called in from_ongoing_ssl_connection() will return None # When the handshake failed due to ClientCertificateRequested # We need to rewrite this logic to not use OpenSSL for looking up key size and RFC names as it is # too complicated # cipher_result = AcceptedCipherSuite.from_ongoing_ssl_connection(ssl_connection, ssl_version) # The ClientCertificateRequested exception already proves that the cipher suite was accepted # Workaround here: cipher_result = AcceptedCipherSuite(openssl_cipher_name, ssl_version, None, None) except Exception as e: cipher_result = ErroredCipherSuite(openssl_cipher_name, ssl_version, e) finally: ssl_connection.close() return cipher_result
def _test_cipher_suite( server_connectivity_info: ServerConnectivityInfo, ssl_version: OpenSslVersionEnum, openssl_cipher_name: str ) -> 'CipherSuite': """Initiates a SSL handshake with the server using the SSL version and the cipher suite specified. """ requires_legacy_openssl = True if ssl_version == OpenSslVersionEnum.TLSV1_2: # For TLS 1.2, we need to pick the right version of OpenSSL depending on which cipher suite requires_legacy_openssl = WorkaroundForTls12ForCipherSuites.requires_legacy_openssl(openssl_cipher_name) elif ssl_version == OpenSslVersionEnum.TLSV1_3: requires_legacy_openssl = False ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection( override_ssl_version=ssl_version, should_use_legacy_openssl=requires_legacy_openssl ) # Only enable the cipher suite to test; not trivial anymore since OpenSSL 1.1.1 and TLS 1.3 if ssl_version == OpenSslVersionEnum.TLSV1_3: # The function to control cipher suites is different for TLS 1.3 # Disable the default, non-TLS 1.3 cipher suites ssl_connection.ssl_client.set_cipher_list('') # Enable the one TLS 1.3 cipher suite we want to test ssl_connection.ssl_client.set_ciphersuites(openssl_cipher_name) else: if not requires_legacy_openssl: # Disable the TLS 1.3 cipher suites if we are using the modern client ssl_connection.ssl_client.set_ciphersuites('') ssl_connection.ssl_client.set_cipher_list(openssl_cipher_name) if len(ssl_connection.ssl_client.get_cipher_list()) != 1: raise ValueError(f'Passed an OpenSSL string for multiple cipher suites: "{openssl_cipher_name}": ' f'{str(ssl_connection.ssl_client.get_cipher_list())}') try: # Perform the SSL handshake ssl_connection.connect() cipher_result: CipherSuite = AcceptedCipherSuite.from_ongoing_ssl_connection(ssl_connection, ssl_version) except SslHandshakeRejected as e: cipher_result = RejectedCipherSuite(openssl_cipher_name, ssl_version, str(e)) except ClientCertificateRequested: # TODO(AD): Sometimes get_current_cipher_name() called in from_ongoing_ssl_connection() will return None # When the handshake failed due to ClientCertificateRequested # We need to rewrite this logic to not use OpenSSL for looking up key size and RFC names as it is # too complicated # cipher_result = AcceptedCipherSuite.from_ongoing_ssl_connection(ssl_connection, ssl_version) # The ClientCertificateRequested exception already proves that the cipher suite was accepted # Workaround here: cipher_result = AcceptedCipherSuite(openssl_cipher_name, ssl_version, None, None) except Exception as e: cipher_result = ErroredCipherSuite(openssl_cipher_name, ssl_version, e) finally: ssl_connection.close() return cipher_result