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_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_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 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 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_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(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 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_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 _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_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 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 _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_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 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()
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(CertificateChainVerificationFailed): # 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=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()
def test_set_ciphersuites(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)) # And a client that only supports a specific TLS 1.3 cipher suite ssl_client = SslClient( ssl_version=OpenSslVersionEnum.TLSV1_3, underlying_socket=sock, ssl_verify=OpenSslVerifyEnum.NONE, ) ssl_client.set_ciphersuites("TLS_CHACHA20_POLY1305_SHA256") # When doing the TLS 1.3 handshake, it succeeds try: ssl_client.do_handshake() finally: ssl_client.shutdown() # And client's cipher suite was used assert "TLS_CHACHA20_POLY1305_SHA256" == ssl_client.get_current_cipher_name( )
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()
class EarlyDataClient(): def __init__(self, **kw): self.socket = None self.session = None self.client = None self.dest = kw.get('dest', 'localhost') self.port = kw.get('port', 443) self.socket_timeout = kw.get('socket_timeout', 5) self.regular_data = kw.get('regular_data', b'XXX-REGULAR-DATA-XXX') self.early_data = kw.get('early_data', b'XXX-EARLY-DATA-XXX') self.read_size = kw.get('read_size', 2048) 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 _finish_handshake(self): if not self.client.is_handshake_completed(): self.client.do_handshake() print('\tCipher suite:', self.client.get_current_cipher_name()) def _close(self): self.client.shutdown() self.socket.close() print('\n') def _write_read_close(self, data_to_send=b'XXX-REGULAR-DATA-XXX', read_size=2048): try: self.client.write(data_to_send) self.client.read(2048) self.session = self.client.get_session() except socket.timeout as e: print('\n\tSocket was timed out. Closing...') finally: self._close() def _send_early_data(self, early_data_to_send=b'XXX-EARLY-DATA-XXX'): self.client.set_session(self.session) self.client.write_early_data(early_data_to_send) self._finish_handshake() print('\t', self.client.get_early_data_status()) self._close() def test_early(self, early_data_to_send=b'XXX-EARLY-DATA-XXX'): print('First Session:') self._init(dest=self.dest, port=self.port) self._finish_handshake() self._write_read_close(data_to_send=self.regular_data) if self.session: print('Reused Session:') self._init(dest=self.dest, port=self.port) if self.session.get_max_early_data() > 0: self._send_early_data() else: print('\n\tServer does not support Early-Data. Closing...') self._close else: print('\nPrevious session failed, can`t send early data. Closing...') print('='*80, '\n')
def get_certificate_chain(host: str, port: int) -> List[str]: """Connect to the host on the port and obtain certificate chain""" func_name: str = "get_certificate_chain" cert_chain: list = [] soc = socket(AF_INET, SOCK_STREAM, proto=0) soc.settimeout(3) try: soc.connect((host, port)) except gaierror: raise Exception( f"{func_name}: {host}:{port} is invalid or not known.") from None except timeout: raise Exception( f"{func_name}: Connection to {host}:{port} timed out.") from None except (OverflowError, TypeError): raise Exception( f"{func_name}: Illegal port: {port}. Port must be between 0-65535." ) from None except ConnectionRefusedError: raise Exception( f"{func_name}: Connection to {host}:{port} refused.") 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 IOError as err: raise ValueError( f"{func_name}: {host} did not respond to the Client Hello." ) from None except (WantReadError, WantX509LookupError) as err: raise ValueError(f"{func_name}: {err.strerror}") from None except OpenSSLError as err: if "1408F10B" in err.args[0]: # https://github.com/openssl/openssl/issues/6805 raise ValueError( f"{func_name}: Remote host is not using SSL/TLS on port: {port}" ) from None raise ValueError(f"{func_name}: {err}") from None finally: # shutdown() will also close the underlying socket ssl_client.shutdown() return cert_chain