Issue with DTLS: Unable to Achieve Handshake Between Client and Serve
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 944
- Forks
- 440
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 6
Description
I have been working for a week on creating a DTLS (Datagram Transport Layer Security) client-server setup, but I am consistently failing to achieve a successful handshake. Despite multiple attempts and configurations, the handshake process does not complete as expected.
`import socket
import logging
from OpenSSL import SSL
from openssl_psk import patch_context
import time
import threading
import hashlib
patch_context()
logging.basicConfig(level=logging.INFO)
def psk_client_callback(connection, hint):
logging.info(f"[TLSClient] PSK client callback called with hint: {hint}")
identity = b'client-identity'
key = b'1a2b3c4d5e6f'
logging.info(f"[TLSClient] Returning identity: {identity}, key: {key}")
return (identity, key)
class TLSClient:
def init(self, config):
self.context = SSL.Context(SSL.DTLS_METHOD)
self.context.set_cipher_list(b'PSK-AES256-CBC-SHA')
self.context.set_psk_client_callback(psk_client_callback)
self.context.set_options(SSL.OP_NO_RENEGOTIATION)
self.context.set_info_callback(lambda conn, where, ret: print(f"[TLSClient] Info: where={where}, ret={ret}, state={conn.get_state_string()}"))
self.client_socket = None
self.config = config
self.ssl_conn = None
self.callback_running = False
self._running = False
def log_handshake_progress(self, conn):
state = conn.get_state_string()
pending = conn.pending()
cipher_name = conn.get_cipher_name()
version = conn.get_protocol_version_name()
logging.info(f"[TLSClient] Handshake state: {state}, Pending: {pending}, Cipher: {cipher_name}, Version: {version}")
def start_client(self):
try:
self._running = True
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#self.client_socket.setblocking(False)
self.client_socket.connect(self.config['address'])
self.context.set_timeout(30)
self.ssl_conn = SSL.Connection(self.context, self.client_socket)
self.ssl_conn.set_connect_state()
logging.info("[TLSClient] Starting DTLS handshake...")
while self._running:
try:
self.log_handshake_progress(self.ssl_conn)
self.ssl_conn.do_handshake()
except SSL.WantReadError:
self.log_handshake_progress(self.ssl_conn)
pass
else:
logging.info("[TLSClient] else handshake.")
self._running = False
self.log_handshake_progress(self.ssl_conn)
logging.info("[TLSClient] DTLS handshake completed.")
# Send a message to the server
message = b"Hello from Client!"
self.ssl_conn.send(message)
logging.info(f"[TLSClient] Sent to server: {message}")
# Receive a response from the server
data = self.ssl_conn.recv(self.config['buffer_size'])
logging.info(f"[TLSClient] Received from server: {data.decode()}")
self.ssl_conn.shutdown()
self.ssl_conn.close()
except SSL.Error as e:
logging.error(f"[TLSClient] SSL error: {e}")
except Exception as e:
logging.error(f"[TLSClient] Error: {e}")
finally:
self.callback_running = False # Stop callback thread
if self.client_socket:
self.client_socket.close()
logging.info("[TLSClient] Client stopped")
def psk_server_callback(connection, identity):
logging.info(f"[TLSServer] PSK server callback called with identity: {identity}")
if identity == b'client-identity':
key = b'1a2b3c4d5e6f'
logging.info(f"[TLSServer] Returning key: {key}")
return key
return None
class TLSServer:
def init(self, config):
self.context = SSL.Context(SSL.DTLS_METHOD)
self.context.set_cipher_list(b'PSK-AES256-CBC-SHA')
self.context.set_psk_server_callback(psk_server_callback)
self.context.set_options(SSL.OP_NO_QUERY_MTU)
self.context.set_info_callback(lambda conn, where, ret: print(f"[TLSServer] Info: where={where}, ret={ret}, state={conn.get_state_string()}"))
# Setup cookie generation and verification
self.context.set_cookie_generate_callback(self.generate_cookie)
self.context.set_cookie_verify_callback(self.verify_cookie)
self.server_socket = None
self._running = False
self.config = config
self.ssl_conn = None
def generate_cookie(self, ssl):
logging.info("[TLSServer] generate_cookie")
return b"xyzzy"
def verify_cookie(self, ssl, cookie):
logging.info("[TLSServer] verify_cookie")
return cookie == b"xyzzy"
def log_handshake_progress(self, conn: SSL.Connection):
state = conn.get_state_string()
pending = conn.pending()
cipher_name = conn.get_cipher_name()
version = conn.get_protocol_version_name()
logging.info(f"[TLSServer] Handshake state: {state}, Pending: {pending}, Cipher: {cipher_name}, Version: {version}")
def start_server(self):
try:
self._running = True
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#self.server_socket.setblocking(False)
self.server_socket.bind(self.config['address'])
logging.info("[TLSServer] Server is running and waiting for connections...")
s_handshaking = False
self.context.set_timeout(30)
s_listening = True
import select
while self._running:
try:
#ready_sockets, _, _ = select.select([self.server_socket], [], [])
#sock = self.server_socket
#for sock in ready_sockets:
data, addr = self.server_socket.recvfrom(self.config['buffer_size'])
ssl_conn = SSL.Connection(self.context, self.server_socket)
ssl_conn.set_accept_state()
ssl_conn.set_tlsext_host_name(self.config['address'][0].encode())
ssl_conn.set_ciphertext_mtu(1500)
#self.invoke_client_callback(data, addr)
self.log_handshake_progress(ssl_conn)
if len(data) > 0 and data[0] == 22 and data[13] == 1:
logging.info("[TLSServer] Received ClientHello from client")
logging.info(f"[TLSServer] Received initial data from {addr}: {data}")
if s_listening:
try:
ssl_conn.DTLSv1_listen()
logging.info("[TLSServer] After DTLSv1_listen")
except SSL.WantReadError:
logging.info("[TLSServer] WantReadError during DTLSv1_listen")
continue
else:
s_listening = False
s_handshaking = True
logging.info("[TLSServer] s_listening=False")
ssl_conn.bio_write(data)
logging.info(f"[TLSServer] Starting DTLS handshake with {addr}...")
while s_handshaking:
try:
self.log_handshake_progress(ssl_conn)
ssl_conn.do_handshake()
break
except SSL.WantReadError:
self.log_handshake_progress(ssl_conn)
self._running = False
s_handshaking = False
pass
except SSL.Error as e:
logging.error(f"[TLSServer] SSL error occurred during handshake: {e}")
self.log_handshake_progress(ssl_conn)
self._running = False
s_handshaking = False
break
self.log_handshake_progress(ssl_conn)
logging.info(f"[TLSServer] DTLS handshake with {addr} completed.")
except SSL.Error as e:
logging.error(f"[TLSServer] SSL error occurred: {e}")
except Exception as e:
logging.error(f"[TLSServer] An error occurred: {e}")
except socket.error as e:
logging.error(f"[TLSServer] Socket error: {e}")
finally:
self.cleanup()
def cleanup(self):
self._running = False
if self.server_socket:
self.server_socket.close()
logging.info("[TLSServer] Server cleaned up and stopped.")
if name == "main":
server_config = {
'address': ('localhost', 4433),
'buffer_size': 4096
}
client_config = {
'address': ('localhost', 4433),
'buffer_size': 4096
}
server = TLSServer(server_config)
server_thread = threading.Thread(target=server.start_server)
server_thread.start()
time.sleep(1)
""" client = TLSClient(client_config)
client_thread = threading.Thread(target=client.start_client)
client_thread.start() """
time.sleep(120)
server._running = False
#client_thread.join()
server_thread.join()`
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the handshake with the Python code included in the issue and inspect the pyOpenSSL DTLS client and server setup, including DTLSv1_listen and the socket handling. Done means the client and server complete the DTLS handshake and exchange the demonstrated message and response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- networking, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100