def _parse_all_cipher_suites() -> Dict[TlsVersionEnum, Set[CipherSuite]]: tls_version_to_cipher_suites: Dict[TlsVersionEnum, Set[CipherSuite]] = {} for tls_version in [ TlsVersionEnum.SSL_2_0, TlsVersionEnum.SSL_3_0, TlsVersionEnum.TLS_1_0, TlsVersionEnum.TLS_1_1, ]: openssl_cipher_strings = _parse_all_cipher_suites_with_legacy_openssl(tls_version) tls_version_to_cipher_suites[tls_version] = set() for cipher_suite_openssl_name in openssl_cipher_strings: cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[tls_version][cipher_suite_openssl_name] tls_version_to_cipher_suites[tls_version].add( CipherSuite( name=cipher_suite_rfc_name, openssl_name=cipher_suite_openssl_name, is_anonymous=True if "anon" in cipher_suite_rfc_name else False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name], ) ) # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites cipher_suites_from_legacy_openssl = _parse_all_cipher_suites_with_legacy_openssl(TlsVersionEnum.TLS_1_2) ssl_client_modern = SslClient(ssl_version=OpenSslVersionEnum(TlsVersionEnum.TLS_1_2.value)) ssl_client_modern.set_cipher_list("ALL:COMPLEMENTOFALL:-PSK:-SRP") cipher_suites_from_modern_openssl = set(ssl_client_modern.get_cipher_list()) # Combine the two sets of cipher suites openssl_cipher_strings = cipher_suites_from_legacy_openssl.union(cipher_suites_from_modern_openssl) tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2] = set() for cipher_suite_openssl_name in openssl_cipher_strings: # Ignore TLS 1.3 cipher suites if cipher_suite_openssl_name in _TLS_1_3_CIPHER_SUITES: continue cipher_suite_rfc_name = _OPENSSL_TO_RFC_NAMES_MAPPING[TlsVersionEnum.TLS_1_2][cipher_suite_openssl_name] tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_2].add( CipherSuite( name=cipher_suite_rfc_name, openssl_name=cipher_suite_openssl_name, is_anonymous=True if "anon" in cipher_suite_rfc_name else False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_rfc_name], ) ) # TLS 1.3 - the list is just hardcoded tls_version_to_cipher_suites[TlsVersionEnum.TLS_1_3] = { CipherSuite( # For TLS 1.3 OpenSSL started using the official names name=cipher_suite_name, openssl_name=cipher_suite_name, is_anonymous=False, key_size=_RFC_NAME_TO_KEY_SIZE_MAPPING[cipher_suite_name], ) for cipher_suite_name in _TLS_1_3_CIPHER_SUITES } return tls_version_to_cipher_suites
def _init(self, dest='localhost', port=443, timeout=5): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.settimeout(timeout) self.socket.connect((dest, port)) self.client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=self.socket, ssl_verify_locations=u'mozilla.pem')
def process_task(self, server_connectivity_info, plugin_command, options_dict=None): if options_dict and "verbose" in options_dict.keys(): verbose_mode = options_dict["verbose"] else: verbose_mode = False test_protocols = {"SSLv3", "TLSv1"} thread_pool = ThreadPool() ciphers_list = [] support_protocol_list = [] for proto in test_protocols: if self.test_protocol_support(PROTOCOL_VERSION[proto], server_connectivity_info): ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[proto]) ssl_client.set_cipher_list(proto) ciphers_list = ssl_client.get_cipher_list() for cipher in ciphers_list: thread_pool.add_job( (self._test_ciphersuite, (server_connectivity_info, PROTOCOL_VERSION[proto], cipher)) ) support_protocol_list.append(proto) thread_pool.start(nb_threads=min(len(ciphers_list), self.MAX_THREADS)) accept_ciphers = [] reject_ciphers = [] if verbose_mode: print " VERBOSE MODE PRINT" print " ------------------" for completed_job in thread_pool.get_result(): (job, cipher_result) = completed_job if isinstance(cipher_result, AcceptCipher): accept_ciphers.append(cipher_result) elif isinstance(cipher_result, RejectCipher): reject_ciphers.append(cipher_result) else: raise ValueError("Unexpected result") if verbose_mode: cipher_result.print_cipher() if verbose_mode: print " ----------------------" print " END VERBOSE MODE PRINT" print " ----------------------" for error_job in thread_pool.get_error(): (_, exception) = error_job raise exception thread_pool.join() support_vulnerable_ciphers_set = self.get_vulnerable_ciphers(accept_ciphers) is_vulnerable = True if len(support_vulnerable_ciphers_set) > 0 else False return BEASTVulnerabilityTesterResult( server_connectivity_info, plugin_command, options_dict, support_vulnerable_ciphers_set, is_vulnerable, support_protocol_list, )
def send_request(self, ssl_client: SslClient) -> str: """Send an HTTP GET to the server and return the HTTP status code. """ try: ssl_client.write(HttpRequestGenerator.get_request(self._hostname)) # Parse the response and print the Location header http_response = HttpResponseParser.parse_from_ssl_connection(ssl_client) if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response result = self.ERR_NOT_HTTP else: redirect = '' if 300 <= http_response.status < 400: redirect_location = http_response.getheader('Location') if redirect_location: # Add redirection URL to the result redirect = f' - {redirect_location}' result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect) except socket.timeout: result = self.ERR_HTTP_TIMEOUT except IOError: result = self.ERR_GENERIC return result
def send_request(self, ssl_client: SslClient) -> str: try: ssl_client.write(b'NOOP\r\n') result = ssl_client.read(2048).strip().decode('utf-8') except socket.timeout: result = 'Timeout on SMTP NOOP' return result
def send_request(self, ssl_client: SslClient) -> str: try: ssl_client.write(b"NOOP\r\n") result = ssl_client.read(2048).strip().decode("utf-8") except socket.timeout: result = "Timeout on SMTP NOOP" return result
def send_request(self, ssl_client: SslClient) -> str: """Send an HTTP GET to the server and return the HTTP status code. """ try: ssl_client.write(HttpRequestGenerator.get_request(self._hostname)) # Parse the response and print the Location header http_response = HttpResponseParser.parse_from_ssl_connection( ssl_client) if http_response.version == 9: # HTTP 0.9 => Probably not an HTTP response result = self.ERR_NOT_HTTP else: redirect = "" if 300 <= http_response.status < 400: redirect_location = http_response.getheader("Location") if redirect_location: # Add redirection URL to the result redirect = f" - {redirect_location}" result = self.GET_RESULT_FORMAT.format(http_response.status, http_response.reason, redirect) except socket.timeout: result = self.ERR_HTTP_TIMEOUT except IOError: result = self.ERR_GENERIC return result
def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.do_handshake() self.cert = ssl_client.get_peer_certificate()._x509
def get_cipher_list(self, ssl_protocol): """Returns list of cipher suites available for protocol version, saves in parameter ssl_protocol. Args: ssl_protocol (str):. """ ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[ssl_protocol]) ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL') return ssl_client.get_cipher_list()
def process_task(self, server_connectivity_info, plugin_command, options_dict=None): ssl_version = self.SSL_VERSIONS_MAPPING[plugin_command] # Get the list of available cipher suites for the given ssl version ssl_client = SslClient(ssl_version=ssl_version) ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL') cipher_list = ssl_client.get_cipher_list() # Scan for every available cipher suite thread_pool = ThreadPool() for cipher in cipher_list: thread_pool.add_job( (self._test_cipher_suite, (server_connectivity_info, ssl_version, cipher))) # Start processing the jobs; One thread per cipher thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS)) accepted_cipher_list = [] rejected_cipher_list = [] errored_cipher_list = [] # Store the results as they come for completed_job in thread_pool.get_result(): (job, cipher_result) = completed_job if isinstance(cipher_result, AcceptedCipherSuite): accepted_cipher_list.append(cipher_result) elif isinstance(cipher_result, RejectedCipherSuite): rejected_cipher_list.append(cipher_result) elif isinstance(cipher_result, ErroredCipherSuite): errored_cipher_list.append(cipher_result) else: raise ValueError('Unexpected result') # Store thread pool errors; only something completely unexpected would trigger an error for failed_job in thread_pool.get_error(): (_, exception) = failed_job raise exception thread_pool.join() # Test for the cipher suite preference preferred_cipher = self._get_preferred_cipher_suite( server_connectivity_info, ssl_version, accepted_cipher_list) # Generate the results plugin_result = OpenSSLCipherSuitesResult(server_connectivity_info, plugin_command, options_dict, preferred_cipher, accepted_cipher_list, rejected_cipher_list, errored_cipher_list) return plugin_result
def test_tls_1_3(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10) sock.connect(('tls13.crypto.mozilla.org', 443)) ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) self.assertTrue(ssl_client) ssl_client.shutdown() sock.close()
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_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 enable_ecdh_cipher_suites(tls_version: TlsVersionEnum, ssl_client: SslClient) -> None: """Set the elliptic curve cipher suites.""" if tls_version == TlsVersionEnum.TLS_1_3: # Cipher suites source: https://tools.ietf.org/html/rfc8446#appendix-B.4 ssl_client.set_ciphersuites( "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:" "TLS_AES_128_CCM_SHA256:TLS_AES_128_CCM_8_SHA256") else: # TLSv1.2; cipher suite source: https://www.openssl.org/docs/man1.0.2/man1/ciphers.html ssl_client.set_cipher_list("ECDH")
def test(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.do_handshake() self.name_entry = ssl_client.get_peer_certificate()._x509.get_subject_name_entries()[0]; self.assertIsNotNone(self.name_entry.get_data()) self.assertIsNotNone(self.name_entry.get_object())
def test(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) sslClient = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE) sslClient.do_handshake() self.x509ext = sslClient.get_peer_certificate()._x509.get_extensions()[0] self.assertIsNotNone(self.x509ext.get_data()) self.assertIsNotNone(self.x509ext.get_object()) self.assertIsNotNone(self.x509ext.get_critical())
def setUp(self): # Requires being online :( sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.fr", 443)) ssl_client = SslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.do_handshake() self.ssl_client = ssl_client self.cert = ssl_client.get_peer_certificate()
def test(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.do_handshake() self.name_entry = ssl_client.get_peer_certificate( )._x509.get_subject_name_entries()[0] self.assertIsNotNone(self.name_entry.get_data()) self.assertIsNotNone(self.name_entry.get_object())
def test(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.google.com', 443)) sslClient = SslClient(sock=sock, ssl_verify=OpenSslVerifyEnum.NONE) sslClient.do_handshake() self.x509ext = sslClient.get_peer_certificate()._x509.get_extensions()[0] self.assertIsNotNone(self.x509ext.get_data()) self.assertIsNotNone(self.x509ext.get_object()) self.assertIsNotNone(self.x509ext.get_critical())
def test_hostname_validation(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.fr", 443)) ssl_client = SslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.do_handshake() self.ssl_client = ssl_client self.cert = ssl_client.get_peer_certificate() self.assertEqual(X509_NAME_MATCHES_SAN, self.cert.matches_hostname('www.google.fr')) self.assertEqual(X509_NAME_MISMATCH, self.cert.matches_hostname('www.tests.com'))
def test_hostname_validation(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.google.fr', 443)) ssl_client = SslClient(ssl_version=OpenSslVersionEnum.SSLV23, sock=sock, ssl_verify=OpenSslVerifyEnum.NONE) ssl_client.do_handshake() self.ssl_client = ssl_client self.cert = ssl_client.get_peer_certificate() self.assertEqual(HostnameValidationResultEnum.NAME_MATCHES_SAN, self.cert.matches_hostname('www.google.fr')) self.assertEqual(HostnameValidationResultEnum.NAME_DOES_NOT_MATCH, self.cert.matches_hostname('www.tests.com'))
def __init__(self, client_certificate_chain_path, client_key_path, client_key_type=SSL_FILETYPE_PEM, client_key_password=''): # type: (str, str, int, Optional[str]) -> None """Create a container for SSL/TLS client authentication settings. Args: client_certificate_chain_path (str): Path to the client's certificate chain. client_key_path (str): Path to the client's private key. client_key_type (int): SSL_FILETYPE_PEM or SSL_FILETYPE_DER. client_key_password (Optional[str]): Password needed to decrypt the private key. """ self.client_certificate_chain_path = client_certificate_chain_path if not os.path.isfile(self.client_certificate_chain_path): raise ValueError('Could not open the client certificate file') self.client_key_path = client_key_path if not os.path.isfile(self.client_key_path): raise ValueError('Could not open the client private key file') self.client_key_password = client_key_password self.client_key_type = client_key_type if self.client_key_type not in [SSL_FILETYPE_ASN1, SSL_FILETYPE_PEM]: raise ValueError('Invalid certificate format specified') # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong SslClient(client_certchain_file=self.client_certificate_chain_path, client_key_file=self.client_key_path, client_key_type=self.client_key_type, client_key_password=self.client_key_password)
def __init__( self, client_certificate_chain_path: str, client_key_path: str, client_key_type: OpenSslFileTypeEnum = OpenSslFileTypeEnum.PEM, client_key_password: str = "", ) -> None: """ Args: client_certificate_chain_path: Path to the file containing the client's certificate. client_key_path: Path to the file containing the client's private key. client_key_type: The format of the key file. client_key_password: The password to decrypt the private key. """ self.client_certificate_chain_path = client_certificate_chain_path if not os.path.isfile(self.client_certificate_chain_path): raise ValueError("Could not open the client certificate file") self.client_key_path = client_key_path if not os.path.isfile(self.client_key_path): raise ValueError("Could not open the client private key file") self.client_key_password = client_key_password self.client_key_type = client_key_type if self.client_key_type not in OpenSslFileTypeEnum: raise ValueError("Invalid certificate format specified") # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong SslClient( client_certchain_file=self.client_certificate_chain_path, client_key_file=self.client_key_path, client_key_type=self.client_key_type, client_key_password=self.client_key_password, )
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 __init__(self, client_certificate_chain_path, client_key_path, client_key_type=OpenSslFileTypeEnum.PEM, client_key_password=''): # type: (Text, Text, OpenSslFileTypeEnum, Optional[Text]) -> None """ Args: client_certificate_chain_path (Text): Path to the file containing the client's certificate. client_key_path (Text): Path to the file containing the client's private key. client_key_type (OpenSslFileTypeEnum): The format of the key file. client_key_password (Optional[Text]): The password to decrypt the private key. """ self.client_certificate_chain_path = client_certificate_chain_path if not os.path.isfile(self.client_certificate_chain_path): raise ValueError('Could not open the client certificate file') self.client_key_path = client_key_path if not os.path.isfile(self.client_key_path): raise ValueError('Could not open the client private key file') self.client_key_password = client_key_password self.client_key_type = client_key_type if self.client_key_type not in OpenSslFileTypeEnum: raise ValueError('Invalid certificate format specified') # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong SslClient(client_certchain_file=self.client_certificate_chain_path, client_key_file=self.client_key_path, client_key_type=self.client_key_type, client_key_password=self.client_key_password)
def get_certificate_chain(host: str, port: int) -> List[str]: """ Connect to the host on the port and obtain certificate chain. TODO: Tests against WantReadError and WantX509LookupError needed. """ cert_chain = [] soc = socket(AF_INET, SOCK_STREAM, proto=0) soc.settimeout(3) try: soc.connect((host, port)) except gaierror: raise Exception(f"{host}:{port} is invalid or not known.") from None except timeout: raise Exception(f"Connection to {host}:{port} timed out.") from None except OverflowError: raise Exception(f"Illegal port: {port}. Port must be between 0-65535.") from None except TypeError: raise Exception(f"Illegal port: {port}. Port must be between 0-65535.") from None ssl_client = SslClient( ssl_version=OpenSslVersionEnum.SSLV23, underlying_socket=soc, ssl_verify=OpenSslVerifyEnum.NONE ) # Add Server Name Indication (SNI) extension to the CLIENT HELLO ssl_client.set_tlsext_host_name(host) try: ssl_client.do_handshake() cert_chain = ssl_client.get_received_chain() except WantReadError as err: raise ValueError(err.strerror) from None except WantX509LookupError as err: raise ValueError(err.strerror) from None except OpenSSLError as err: raise ValueError(err) from None finally: ssl_client.shutdown() soc = None return cert_chain
def process_task(self, server_connectivity_info, plugin_command, options_dict=None): ssl_version = self.SSL_VERSIONS_MAPPING[plugin_command] # Get the list of available cipher suites for the given ssl version ssl_client = SslClient(ssl_version=ssl_version) ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL') cipher_list = ssl_client.get_cipher_list() # Scan for every available cipher suite thread_pool = ThreadPool() for cipher in cipher_list: thread_pool.add_job((self._test_cipher_suite, (server_connectivity_info, ssl_version, cipher))) # Start processing the jobs; One thread per cipher thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS)) accepted_cipher_list = [] rejected_cipher_list = [] errored_cipher_list = [] # Store the results as they come for completed_job in thread_pool.get_result(): (job, cipher_result) = completed_job if isinstance(cipher_result, AcceptedCipherSuite): accepted_cipher_list.append(cipher_result) elif isinstance(cipher_result, RejectedCipherSuite): rejected_cipher_list.append(cipher_result) elif isinstance(cipher_result, ErroredCipherSuite): errored_cipher_list.append(cipher_result) else: raise ValueError('Unexpected result') # Store thread pool errors; only something completely unexpected would trigger an error for failed_job in thread_pool.get_error(): (_, exception) = failed_job raise exception thread_pool.join() # Test for the cipher suite preference preferred_cipher = self._get_preferred_cipher_suite(server_connectivity_info, ssl_version, accepted_cipher_list) # Generate the results plugin_result = OpenSSLCipherSuitesResult(server_connectivity_info, plugin_command, options_dict, preferred_cipher, accepted_cipher_list, rejected_cipher_list, errored_cipher_list) return plugin_result
def test_sct_parsing(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('sslanalyzer.comodoca.com', 443)) ssl_client = SslClient(underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) ssl_client.set_tlsext_status_ocsp() ssl_client.do_handshake() ocsp_response = ssl_client.get_tlsext_status_ocsp_resp() ssl_client.shutdown() sock.close() self.assertIsNotNone(ocsp_response.as_dict()['responses'][0]['singleExtensions']['ctCertificateScts'])
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 __post_init__(self) -> None: # Try to load the cert and key in OpenSSL; will raise an exception if something is wrong SslClient( client_certificate_chain=self.certificate_chain_path, client_key=self.key_path, client_key_type=self.key_type, client_key_password=self.key_password, )
def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10) sock.connect(('tls13.crypto.mozilla.org', 443)) ssl_client = SslClient(ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) self.ssl_client = ssl_client
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_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_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 _create_tls_1_3_session(server_host: str, server_port: int) -> _nassl.SSL_SESSION: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server_host, server_port)) ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE ) try: ssl_client.do_handshake() ssl_client.write(ModernOpenSslServer.HELLO_MSG) ssl_client.read(2048) session = ssl_client.get_session() finally: ssl_client.shutdown() return session
def test_tls_1_3_write_early_data_fail_when_used_on_non_reused_session(self): # Given a server that supports TLS 1.3 and early data with ModernOpenSslServer(max_early_data=512) as server: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((server.hostname, server.port)) # That does NOT have a previous session with the server ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE ) # When sending early data # It fails with pytest.raises(OpenSSLError, match='you should not call'): ssl_client.write_early_data(b'EARLY DATA') ssl_client.shutdown()
def test(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.cloudflare.com', 443)) ssl_client = SslClient(underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE) ssl_client.set_tlsext_status_ocsp() ssl_client.do_handshake() ocsp_response = ssl_client.get_tlsext_status_ocsp_resp() ssl_client.shutdown() self.assertEqual(ocsp_response.status, OcspResponseStatusEnum.SUCCESSFUL) # Test as_text() self.assertIsNotNone(ocsp_response.as_text()) # Test verify with a wrong certificate test_file = tempfile.NamedTemporaryFile(delete=False, mode='wt') test_file.write("""-----BEGIN CERTIFICATE----- MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG 9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1 OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO 8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk Pd2eQ9+DkopOz3UGU7c= -----END CERTIFICATE-----""") test_file.close() self.assertRaises(OcspResponseNotTrustedError, ocsp_response.verify, test_file.name) # No SCT extension self.assertFalse('singleExtensions' in ocsp_response.as_dict() ['responses'][0].keys())
def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("login.live.com", 443)) ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.set_tlsext_status_ocsp() ssl_client.do_handshake() self.ocsp_response = ssl_client.get_tlsext_status_ocsp_resp( )._ocsp_response
def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("login.live.com", 443)) ssl_client = SslClient(sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.set_tlsext_status_ocsp() ssl_client.do_handshake() self.ocsp_response = ssl_client.get_tlsext_status_ocsp_resp()._ocsp_response
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 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) self.assertTrue(session) # And the server only accepts 1 byte of early data max_early = session.get_max_early_data() self.assertEqual(1, max_early) # When creating a new connection with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock_early_data: 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) self.assertEqual(OpenSslEarlyDataStatusEnum.NOT_SENT, ssl_client_early_data.get_early_data_status()) # When sending too much early data # It fails self.assertRaisesRegex( OpenSSLError, 'too much early data', ssl_client_early_data.write_early_data, 'GET / HTTP/1.1\r\nData: {}\r\n\r\n'.format('*' * max_early))
def test_set_ciphersuites(self): # Given an SslClient for TLS 1.3 ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_3, ssl_verify=OpenSslVerifyEnum.NONE, ignore_client_authentication_requests=True, ) # With the default list of cipher disabled ssl_client.set_cipher_list("") # When setting a specific TLS 1.3 cipher suite as the list of supported ciphers ssl_client.set_ciphersuites("TLS_CHACHA20_POLY1305_SHA256") # That one cipher suite is the only one enabled ciphers = ssl_client.get_cipher_list() assert ["TLS_CHACHA20_POLY1305_SHA256"] == ciphers
def test_set_ciphersuites(self): # Given an SslClient for TLS 1.3 ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_3, ssl_verify=OpenSslVerifyEnum.NONE, ignore_client_authentication_requests=True, ) # With the default list of cipher disabled ssl_client.set_cipher_list('') # When setting a specific TLS 1.3 cipher suite as the list of supported ciphers ssl_client.set_ciphersuites('TLS_CHACHA20_POLY1305_SHA256') # That one cipher suite is the only one enabled ciphers = ssl_client.get_cipher_list() assert ['TLS_CHACHA20_POLY1305_SHA256'] == ciphers
def test_get_verified_chain_but_validation_failed(self): # Given an SslClient connecting to Google sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.google.com', 443)) ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_2, underlying_socket=sock, # That is configured to silently fail validation ssl_verify=OpenSslVerifyEnum.NONE ) # When doing a TLS handshake, it succeeds try: ssl_client.do_handshake() # And when requesting the verified certificate chain with pytest.raises(CouldNotBuildVerifiedChain): # It fails because certificate validation failed ssl_client.get_verified_chain() finally: ssl_client.shutdown()
def test_get_verified_chain(self): # Given an SslClient connecting to Google sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.yahoo.com', 443)) print(str(Path(__file__).absolute().parent / 'google_roots.pem')) ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_2, underlying_socket=sock, # That is configured to properly validate certificates ssl_verify=OpenSslVerifyEnum.PEER, ssl_verify_locations=str(Path(__file__).absolute().parent / 'mozilla.pem') ) # When doing a TLS handshake, it succeeds try: ssl_client.do_handshake() # And when requesting the verified certificate chain, it returns it assert ssl_client.get_verified_chain() finally: ssl_client.shutdown()
from nassl.ssl_client import OpenSslVersionEnum, SslClient, OpenSslVerifyEnum import socket mozilla_store = 'tests/mozilla.pem' sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.yahoo.com', 443)) ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_2, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.PEER, ssl_verify_locations=mozilla_store, ) ssl_client.set_tlsext_status_ocsp() ssl_client.do_handshake() print('Received certificate chain') for pem_cert in ssl_client.get_received_chain(): print(pem_cert) print('Verified certificate chain') for pem_cert in ssl_client.get_verified_chain(): print(pem_cert) print('OCSP Stapling') ocsp_resp = ssl_client.get_tlsext_status_ocsp_resp() if ocsp_resp: ocsp_resp.verify(mozilla_store) print(ocsp_resp.as_dict())
def test_tls_1_3_write_early_data_does_not_finish_handshake(self): # Given a server that supports TLS 1.3 and early data with ModernOpenSslServer(max_early_data=512) 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 accepts early data max_early = session.get_max_early_data() assert max_early > 0 # 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 early data ssl_client_early_data.write_early_data(b'EARLY DATA') # It succeeds assert not ssl_client_early_data.is_handshake_completed() assert OpenSslEarlyDataStatusEnum.REJECTED == ssl_client_early_data.get_early_data_status() # And after completing the handshake, the early data was accepted ssl_client_early_data.do_handshake() assert OpenSslEarlyDataStatusEnum.ACCEPTED == ssl_client_early_data.get_early_data_status() ssl_client_early_data.shutdown()
def get_ssl3_cipher_list(self): """Returns list of cipher suites available for protocol SSL 3.0 """ ssl_client = SslClient(ssl_version=SSLV3) ssl_client.set_cipher_list('SSLv3') return ssl_client.get_cipher_list()