# compatibile Windows 11 # compatibile Ubuntu 24.10 # compatibile python 3.12.7 import asyncio import signal from time import sleep import websockets import json import functools # Import functools from cryptography.fernet import Fernet from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.backends import default_backend websocket = None # Variabile globale per tenere traccia del websocket receive_task = None # Variabile globale per la task di ricezione main_task = None # Variabile globale to hold the main task - ADDED BACK symmetric_key = None # Client's symmetric key server_public_key = None # Server's public key fernet = None # Fernet object for symmetric encryption async def receive_messages(ws): """Funzione asincrona dedicata alla ricezione e gestione dei messaggi dal server.""" global fernet try: while True: try: response = await ws.recv() # Prova a decodificare la risposta come JSON try: data = json.loads(response) #print(f"Data ricevuta (task receive): {data}") if data["type"] == "public_key": # Riceve la chiave pubblica del server public_key_pem = data["key"].encode('utf-8') global server_public_key server_public_key = serialization.load_pem_public_key( public_key_pem, backend=default_backend() ) print("Server public key received and loaded.") elif data["type"] == "encrypted_message": encrypted_message_bytes = data["message"].encode('utf-8') encrypted_message = bytes.fromhex(encrypted_message_bytes.decode('utf-8')) # Decrittografa il messaggio con la chiave simmetrica decrypted_message_bytes = fernet.decrypt(encrypted_message) decrypted_message = decrypted_message_bytes.decode('utf-8') # Prova a decodificare il messaggio decrittografato come JSON try: message_content = json.loads(decrypted_message) print(f"Messaggio decrittografato (task receive): {message_content}") if "action" in message_content: if message_content["action"] == "saluta": nome = message_content.get("nome", "Sconosciuto") elif message_content["action"] == "somma": a = message_content.get("a", 0) b = message_content.get("b", 0) risultato = a + b else: pass else: pass except json.JSONDecodeError: print(f"Messaggio decrittografato non JSON (task receive): {decrypted_message}") else: print(f"Tipo di messaggio non gestito (task receive): {data['type']}") except (json.JSONDecodeError, KeyError): print(f"Ricevuto (task receive - non JSON): {response}") except websockets.ConnectionClosed: print("Connessione chiusa dal server (task receive).") break except Exception as e: print(f"Errore durante la ricezione (task receive): {e}") break finally: print("Task di ricezione terminato.") async def connect_and_send(): """Si connette al server WebSocket, invia messaggi e avvia la ricezione in background.""" global websocket, receive_task, main_task, symmetric_key, server_public_key, fernet # Usa la variabile globale, including main_task uri = "ws://localhost:8765" symmetric_key = Fernet.generate_key() # Genera chiave simmetrica per questo client fernet = Fernet(symmetric_key) # Initialize Fernet object try: async with websockets.connect(uri, ping_interval=None) as ws: websocket = ws # Assegna il websocket connesso alla variabile globale print(f"Connesso al server WebSocket: {uri}") # Avvia la task di ricezione in background receive_task = asyncio.create_task(receive_messages(websocket)) # Task creation is NOW INSIDE connect_and_send print(f"Task di ricezione creata: {receive_task}") # DEBUG # Ricevi chiave pubblica server (handled in receive_messages) print("Attendo la chiave pubblica del server...") await asyncio.sleep(1) # Wait briefly for public key to be received in receive_task # Invia la chiave simmetrica criptata al server if server_public_key: encrypted_symmetric_key = server_public_key.encrypt( symmetric_key, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) encrypted_symmetric_key_hex = encrypted_symmetric_key.hex() encrypted_symmetric_key_str = encrypted_symmetric_key_hex.encode('utf-8').decode('utf-8') key_exchange_message = { "type": "encrypted_symmetric_key", "key": encrypted_symmetric_key_str } await websocket.send(json.dumps(key_exchange_message)) print("Chiave simmetrica criptata e inviata al server.") else: raise Exception("Server public key not received.") n = 0 # Inizializza n qui while True: try: # Invia un semplice messaggio di testo CRITTOGRAFATO message_text = f"Ciao dal client n.{n}" encrypted_message_bytes = fernet.encrypt(message_text.encode('utf-8')) encrypted_message = encrypted_message_bytes.hex() message_to_send = { "type": "encrypted_message", "message": encrypted_message } message_json_str = json.dumps(message_to_send) print(f"SEND client n.{n} (encrypted): {message_text}") await websocket.send(message_json_str) # Invia messaggio JSON crittografato n += 1 # Incrementa n dopo l'invio # Invia un messaggio JSON CRITTOGRAFATO messaggio_json_payload = {"action": "saluta", "nome": "Alice"} messaggio_json_str_payload = json.dumps(messaggio_json_payload) encrypted_json_bytes = fernet.encrypt(messaggio_json_str_payload.encode('utf-8')) encrypted_json_message = encrypted_json_bytes.hex() json_message_to_send = { "type": "encrypted_message", "message": encrypted_json_message } json_message_json_str = json.dumps(json_message_to_send) await websocket.send(json_message_json_str) # Invia un altro messaggio JSON (richiesta di somma) CRITTOGRAFATO messaggio_json_payload = {"action": "somma", "a": 5, "b": 3} messaggio_json_str_payload = json.dumps(messaggio_json_payload) encrypted_json_bytes = fernet.encrypt(messaggio_json_str_payload.encode('utf-8')) encrypted_json_message = encrypted_json_bytes.hex() json_message_to_send = { "type": "encrypted_message", "message": encrypted_json_message } json_message_json_str = json.dumps(json_message_to_send) await websocket.send(json_message_json_str) await asyncio.sleep(5) # Usa asyncio.sleep per non bloccare il loop eventi except websockets.ConnectionClosed: print("Connessione chiusa dal server (task send).") break except Exception as e: print(f"Errore durante l'invio (task send): {e}") break except asyncio.CancelledError: print("Connessione interrotta e chiusa correttamente a seguito di CancelledError.") # DEBUG except Exception as e: print(f"Errore in connect_and_send: {e}") finally: print("Finally block in connect_and_send started.") # DEBUG if websocket: # Controlla solo se websocket esiste (non è None) - MODIFICA QUI await websocket.close() print("Websocket chiuso in finally block.") # DEBUG if receive_task and not receive_task.cancelled(): # Controlla se receive_task è definito e non è già cancellata print(f"Status receive_task before cancel: {receive_task.cancelled()}") # DEBUG receive_task.cancel() print("Task di ricezione cancellata in finally block.") # DEBUG print(f"Status receive_task after cancel: {receive_task.cancelled()}") # DEBUG print("Finally block in connect_and_send finished.") # DEBUG return asyncio.current_task() # Return the main task # Modifica significativa qui: signal_handler now DOES accept 'task' as argument again def signal_handler(sig, frame, task): # ADDED 'task' argument back """Gestisce il segnale SIGINT (CTRL+C) per chiudere correttamente la connessione.""" print("\nRicevuto CTRL+C, chiusura della connessione WebSocket...") print("Calling task.cancel() from signal_handler") if task is not None: # Now 'task' IS passed correctly task.cancel() print("task.cancel() called.") else: print("Task is None in signal_handler (this SHOULD NOT happen now).") # This should not happen anymore if __name__ == "__main__": main_task_instance = None try: main_task_instance = asyncio.run(connect_and_send()) # Use asyncio.run directly except KeyboardInterrupt: # KeyboardInterrupt può essere sollevata anche se non gestita direttamente da signal pass # Gestito già dal signal handler e dalla cancellazione della task except asyncio.CancelledError: # Cattura CancelledError se la task viene cancellata print("Task connect_and_send cancelled.") # Debug message finally: partial_signal_handler = functools.partial(signal_handler, task=main_task_instance) signal.signal(signal.SIGINT, partial_signal_handler) # Register signal handler parziale print("Applicazione client terminata.")