# compatibile Windows 11 # compatibile Ubuntu 24.10 # compatibile python 3.12.7 import asyncio import sys import threading import websockets import json import signal from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import serialization, hashes from cryptography.fernet import Fernet from cryptography.hazmat.backends import default_backend connected_clients = {} server_private_key = None server_public_key = None main_task = None loop = None # Store the loop globally stop_future = None async def generate_server_keys(): """Genera le chiavi asimmetriche del server.""" global server_private_key, server_public_key server_private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) server_public_key = server_private_key.public_key() print("Server keys generated.") # Debugging print async def exchange_symmetric_key(websocket): """Gestisce lo scambio della chiave simmetrica con il client.""" global server_private_key, server_public_key # Invia la chiave pubblica del server al client public_key_pem = server_public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) await websocket.send(json.dumps({"type": "public_key", "key": public_key_pem.decode('utf-8')})) print(f"Server public key sent to {websocket.remote_address}") # Ricevi la chiave simmetrica crittografata dal client encrypted_symmetric_key_json = await websocket.recv() encrypted_symmetric_key_data = json.loads(encrypted_symmetric_key_json) if encrypted_symmetric_key_data["type"] == "encrypted_symmetric_key": encrypted_symmetric_key_bytes = encrypted_symmetric_key_data["key"].encode('utf-8') encrypted_symmetric_key = bytes.fromhex(encrypted_symmetric_key_bytes.decode('utf-8')) # Decrittografa la chiave simmetrica con la chiave privata del server symmetric_key = server_private_key.decrypt( encrypted_symmetric_key, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) print(f"Server symmetric key received and decrypted from {websocket.remote_address}") return symmetric_key else: raise Exception("Unexpected message type during key exchange") async def echo(websocket): """Gestisce una singola connessione WebSocket.""" client_port = websocket.remote_address[1] try: symmetric_key = await exchange_symmetric_key(websocket) fernet = Fernet(symmetric_key) connected_clients[websocket] = {'port': client_port, 'symmetric_key': fernet} print(f"Nuova connessione da: {websocket.remote_address} con porta: {client_port}") async for message in websocket: try: print(f"Ricevuto messaggio criptato da {websocket.remote_address} con porta: {client_port}") encrypted_message_data = json.loads(message) if encrypted_message_data["type"] == "encrypted_message": encrypted_message_bytes = encrypted_message_data["message"].encode('utf-8') encrypted_message = bytes.fromhex(encrypted_message_bytes.decode('utf-8')) decrypted_message_bytes = connected_clients[websocket]['symmetric_key'].decrypt(encrypted_message) decrypted_message = decrypted_message_bytes.decode('utf-8') print(f"Messaggio decriptato da {websocket.remote_address} con porta: {client_port}: {decrypted_message}") message_to_forward = { "sender_port": client_port, "message": decrypted_message } message_json = json.dumps(message_to_forward) for recipient, client_info in connected_clients.items(): if recipient != websocket: try: recipient_fernet = client_info['symmetric_key'] encrypted_forward_message_bytes = recipient_fernet.encrypt(message_json.encode('utf-8')) encrypted_forward_message = encrypted_forward_message_bytes.hex() message_to_send_recipient = { "type": "encrypted_message", "message": encrypted_forward_message } message_json_recipient = json.dumps(message_to_send_recipient) await recipient.send(message_json_recipient) print(f"Inoltrato messaggio da {client_port} a {client_info['port']} (criptato)") except websockets.ConnectionClosed: print(f"Impossibile inoltrare a {client_info['port']} (connessione chiusa)") else: print(f"Tipo di messaggio non gestito da {websocket.remote_address}: {encrypted_message_data['type']}") except Exception as e: print(f"Errore durante l'elaborazione del messaggio da {websocket.remote_address}: {e}") break except websockets.ConnectionClosedError: print(f"Connessione chiusa inaspettatamente da {websocket.remote_address} con porta: {client_port}") except websockets.ConnectionClosedOK: print(f"Connessione chiusa normalmente da {websocket.remote_address} con porta: {client_port}") except Exception as e: print(f"Errore durante la gestione della connessione da {websocket.remote_address} con porta: {client_port}: {e}") finally: if websocket in connected_clients: del connected_clients[websocket] print(f"Connessione con {websocket.remote_address} (porta: {client_port}) terminata e chiave rimossa.") else: print(f"Connessione con {websocket.remote_address} (porta: {client_port}) terminata (nessuna chiave da rimuovere).") async def server_main(stop_future): """Avvia il server WebSocket.""" async with websockets.serve(echo, "0.0.0.0", 8765, ping_interval=None): print("Server WebSocket in ascolto su ws://0.0.0.0:8765") await stop_future def input_thread(stop_future, loop): """Legge l'input da console in un thread separato.""" async def set_stop_future(): stop_future.set_result(None) while True: line = sys.stdin.readline().strip() if line == "exit": print("\nServer: Ricevuto comando 'exit', chiusura del server...") asyncio.run_coroutine_threadsafe(set_stop_future(), loop) # Use loop, not main_loop break async def main(): """Funzione principale per avviare il server e gestire l'input.""" global main_task, stop_future, loop loop = asyncio.get_running_loop() # Get the loop *here* stop_future = asyncio.Future() # Aggiunta la generazione delle chiavi PRIMA di avviare il server await generate_server_keys() main_task = asyncio.create_task(server_main(stop_future)) print(f"Main task created: {main_task}") input_thread_instance = threading.Thread(target=input_thread, args=(stop_future, loop), daemon=True) input_thread_instance.start() try: await main_task except asyncio.CancelledError: print("Server: Server task cancelled, shutting down...") except Exception as e: print(f"Server: Error in main: {e}") finally: print("Server: Server shutdown completed.") if __name__ == "__main__": asyncio.run(main())