pyca / pyca/pyopenssl

Issue with DTLS: Unable to Achieve Handshake Between Client and Serve

Open
#1,323 1 comment 0 reactions 0 assignees View on GitHub

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.