def test_tls_in_tls_makefile_rw_text(self): """ Creates a separate buffer for reading and writing using text mode and utf-8 encoding. """ self.start_destination_server() self.start_proxy_server() sock = socket.create_connection( (self.proxy_server.host, self.proxy_server.port)) with self.client_context.wrap_socket( sock, server_hostname="localhost") as proxy_sock: with SSLTransport(proxy_sock, self.client_context, server_hostname="localhost") as destination_sock: read = destination_sock.makefile("r", encoding="utf-8") write = destination_sock.makefile("w", encoding="utf-8") write.write(sample_request(binary=False)) write.flush() response = read.read() if "\r" not in response: # Carriage return will be removed when reading as a file on # some platforms. We add it before the comparison. response = response.replace("\n", "\r\n") validate_response(response, binary=False)
def test_start_closed_socket(self): """ Errors generated from an unconnected socket should bubble up.""" sock = socket.socket(socket.AF_INET) context = ssl.create_default_context() sock.close() with pytest.raises(OSError): SSLTransport(sock, context)
def test_tls_in_tls_makefile_raw_rw_binary(self, buffering): """ Uses makefile with read, write and binary modes without buffering. """ self.start_destination_server() self.start_proxy_server() sock = socket.create_connection( (self.proxy_server.host, self.proxy_server.port)) with self.client_context.wrap_socket( sock, server_hostname="localhost") as proxy_sock: with SSLTransport(proxy_sock, self.client_context, server_hostname="localhost") as destination_sock: file = destination_sock.makefile("rwb", buffering) file.write(sample_request()) file.flush() response = bytearray(65536) wrote = file.readinto(response) assert wrote is not None # Allocated response is bigger than the actual response, we # rtrim remaining x00 bytes. str_response = response.decode("utf-8").rstrip("\x00") validate_response(str_response, binary=False) file.close()
def test_ssl_object_attributes(self): """ Ensures common ssl attributes are exposed """ self.start_dummy_server() sock = socket.create_connection((self.host, self.port)) with SSLTransport(sock, self.client_context, server_hostname="localhost") as ssock: cipher = ssock.cipher() assert type(cipher) == tuple # No chosen protocol through ALPN or NPN. assert ssock.selected_alpn_protocol() is None assert ssock.selected_npn_protocol() is None shared_ciphers = ssock.shared_ciphers() assert type(shared_ciphers) == list assert len(shared_ciphers) > 0 assert ssock.compression() is None validate_peercert(ssock) ssock.send(sample_request()) response = consume_socket(ssock) validate_response(response)
def test_close_after_handshake(self): """ Socket errors should be bubbled up """ self.start_dummy_server() sock = socket.create_connection((self.host, self.port)) with SSLTransport(sock, self.client_context, server_hostname="localhost") as ssock: ssock.close() with pytest.raises(OSError): ssock.send(b"blaaargh")
def test_unbuffered_text_makefile(self): self.start_dummy_server() sock = socket.create_connection((self.host, self.port)) with SSLTransport(sock, self.client_context, server_hostname="localhost") as ssock: with pytest.raises(ValueError): ssock.makefile("r", buffering=0) ssock.send(sample_request()) response = consume_socket(ssock) validate_response(response)
def test_wrap_existing_socket(self): """ Validates a single TLS layer can be established. """ self.start_dummy_server() sock = socket.create_connection((self.host, self.port)) with SSLTransport(sock, self.client_context, server_hostname="localhost") as ssock: assert ssock.version() is not None ssock.send(sample_request()) response = consume_socket(ssock) validate_response(response)
def test_unwrap_existing_socket(self): """ Validates we can break up the TLS layer A full request/response is sent over TLS, and later over plain text. """ def shutdown_handler(listener): sock = listener.accept()[0] ssl_sock = self.server_context.wrap_socket(sock, server_side=True) request = consume_socket(ssl_sock) validate_request(request) ssl_sock.sendall(sample_response()) unwrapped_sock = ssl_sock.unwrap() request = consume_socket(unwrapped_sock) validate_request(request) unwrapped_sock.sendall(sample_response()) self.start_dummy_server(shutdown_handler) sock = socket.create_connection((self.host, self.port)) ssock = SSLTransport(sock, self.client_context, server_hostname="localhost") # request/response over TLS. ssock.sendall(sample_request()) response = consume_socket(ssock) validate_response(response) # request/response over plaintext after unwrap. ssock.unwrap() sock.sendall(sample_request()) response = consume_socket(sock) validate_response(response)
def test_socket_object_attributes(self): """ Ensures common socket attributes are exposed """ self.start_dummy_server() sock = socket.create_connection((self.host, self.port)) with SSLTransport(sock, self.client_context, server_hostname="localhost") as ssock: assert ssock.fileno() is not None test_timeout = 10 ssock.settimeout(test_timeout) assert ssock.gettimeout() == test_timeout assert ssock.socket.gettimeout() == test_timeout ssock.send(sample_request()) response = consume_socket(ssock) validate_response(response)
def test_tls_in_tls_recv_into_unbuffered(self): """ Valides recv_into without a preallocated buffer. """ self.start_destination_server() self.start_proxy_server() sock = socket.create_connection( (self.proxy_server.host, self.proxy_server.port)) with self.client_context.wrap_socket( sock, server_hostname="localhost") as proxy_sock: with SSLTransport(proxy_sock, self.client_context, server_hostname="localhost") as destination_sock: destination_sock.send(sample_request()) response = destination_sock.recv_into(None) validate_response(response)
def test_wrong_sni_hint(self): """ Provides a wrong sni hint to validate an exception is thrown. """ self.start_destination_server() self.start_proxy_server() sock = socket.create_connection( (self.proxy_server.host, self.proxy_server.port)) with self.client_context.wrap_socket( sock, server_hostname="localhost") as proxy_sock: with pytest.raises(Exception) as e: SSLTransport(proxy_sock, self.client_context, server_hostname="veryverywrong") # ssl.CertificateError is a child of ValueError in python3.6 or # before. After python3.7 it's a child of SSLError assert e.type in [ssl.SSLError, ssl.CertificateError]
def test_tls_in_tls_tunnel(self): """ Basic communication over the TLS in TLS tunnel. """ self.start_destination_server() self.start_proxy_server() sock = socket.create_connection( (self.proxy_server.host, self.proxy_server.port)) with self.client_context.wrap_socket( sock, server_hostname="localhost") as proxy_sock: with SSLTransport(proxy_sock, self.client_context, server_hostname="localhost") as destination_sock: assert destination_sock.version() is not None destination_sock.send(sample_request()) response = consume_socket(destination_sock) validate_response(response)
def test_tls_in_tls_recv_into_sendall(self): """ Valides recv_into and sendall also work as expected. Other tests are using recv/send. """ self.start_destination_server() self.start_proxy_server() sock = socket.create_connection( (self.proxy_server.host, self.proxy_server.port)) with self.client_context.wrap_socket( sock, server_hostname="localhost") as proxy_sock: with SSLTransport(proxy_sock, self.client_context, server_hostname="localhost") as destination_sock: destination_sock.sendall(sample_request()) response = bytearray(65536) destination_sock.recv_into(response) str_response = response.decode("utf-8").rstrip("\x00") validate_response(str_response, binary=False)
def test_unwrap_existing_socket(self): """ Validates we can break up the TLS layer Request is sent over TLS. response received over regular TCP. Currently disabled as its flaky on occassion. """ def shutdown_handler(listener): sock = listener.accept()[0] ssl_sock = self.server_context.wrap_socket(sock, server_side=True) request = consume_socket(ssl_sock) validate_request(request) sock = ssl_sock.unwrap() sock.send(sample_response()) self.start_dummy_server(shutdown_handler) sock = socket.create_connection((self.host, self.port)) ssock = SSLTransport(sock, self.client_context, server_hostname="localhost") ssock.send(sample_request()) ssock.unwrap() response = consume_socket(sock) validate_response(response)