summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGab <24553253+gabrix73@users.noreply.github.com>2025-03-08 14:59:35 +0100
committerGitHub <noreply@github.com>2025-03-08 14:59:35 +0100
commit4a3ae9929f4657a0f2e9ccf393205655895bb501 (patch)
treecc4e8089f178b791875a9a3b34f842cf7466ba4f
parent489f247cffd1a63936a884c6d336b318179b39d8 (diff)
downloadsecurewhisper-4a3ae9929f4657a0f2e9ccf393205655895bb501.tar.gz
securewhisper-4a3ae9929f4657a0f2e9ccf393205655895bb501.tar.xz
securewhisper-4a3ae9929f4657a0f2e9ccf393205655895bb501.zip
Add files via upload
-rw-r--r--icon.pngbin0 -> 149382 bytes
-rw-r--r--mesh_chat.py121
-rw-r--r--network/__init__.py9
-rw-r--r--network/__pycache__/__init__.cpython-313.pycbin0 -> 392 bytes
-rw-r--r--network/__pycache__/mesh.cpython-313.pycbin0 -> 20340 bytes
-rw-r--r--network/__pycache__/tor_manager.cpython-313.pycbin0 -> 10264 bytes
-rw-r--r--network/mesh.py304
-rw-r--r--network/tor_manager.py196
-rw-r--r--security/__init__.py9
-rw-r--r--security/__pycache__/__init__.cpython-313.pycbin0 -> 398 bytes
-rw-r--r--security/__pycache__/crypto.cpython-313.pycbin0 -> 2391 bytes
-rw-r--r--security/__pycache__/memory.cpython-313.pycbin0 -> 2155 bytes
-rw-r--r--security/crypto.py37
-rw-r--r--security/memory.py38
-rw-r--r--test_health.py61
-rw-r--r--tor/torrc5
-rw-r--r--tor_data/torrc13
-rw-r--r--ui/__init__.py8
-rw-r--r--ui/__pycache__/__init__.cpython-313.pycbin0 -> 335 bytes
-rw-r--r--ui/__pycache__/chat_window.cpython-313.pycbin0 -> 8603 bytes
-rw-r--r--ui/chat_window.py159
21 files changed, 960 insertions, 0 deletions
diff --git a/icon.png b/icon.png
new file mode 100644
index 0000000..28413ee
--- /dev/null
+++ b/icon.png
Binary files differ
diff --git a/mesh_chat.py b/mesh_chat.py
new file mode 100644
index 0000000..f44edc9
--- /dev/null
+++ b/mesh_chat.py
@@ -0,0 +1,121 @@
+
+import os
+import sys
+import asyncio
+import logging
+import socket
+from network.tor_manager import TorManager
+from network.mesh import MeshNetwork
+from ui.chat_window import ChatWindow
+from security.memory import SecureMemory
+from security.crypto import CryptoManager
+
+class MeshChat:
+ def __init__(self):
+ # Configure logging
+ logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+ )
+ self.logger = logging.getLogger('MeshChat')
+
+ # Initialize components
+ self.tor_manager = TorManager()
+ self.mesh_network = MeshNetwork(base_port=12345, max_retry_ports=5)
+ self.crypto = CryptoManager()
+ self.secure_memory = SecureMemory()
+
+ async def _verify_network(self) -> bool:
+ """Verify network components are running"""
+ try:
+ # Try connecting to health check endpoint
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect(('127.0.0.1', 12345))
+ sock.close()
+ self.logger.info("Network connectivity verified")
+ return True
+ except Exception as e:
+ self.logger.error(f"Network verification failed: {str(e)}")
+ return False
+
+ async def start(self):
+ """Start application with robust error handling"""
+ try:
+ # Start Tor
+ self.logger.info("Starting Tor...")
+ await self.tor_manager.start()
+ self.logger.info(f"Tor started with onion address: {self.tor_manager.onion_address}")
+
+ # Initialize mesh network with retries
+ max_retries = 3
+ retry_delay = 2
+ last_error = None
+
+ for attempt in range(max_retries):
+ try:
+ self.logger.info(f"Starting mesh network (attempt {attempt + 1}/{max_retries})...")
+ await self.mesh_network.start()
+
+ # Verify network is running
+ if await self._verify_network():
+ self.logger.info("Mesh network started and verified")
+ break
+ else:
+ raise Exception("Network verification failed")
+
+ except Exception as e:
+ last_error = e
+ self.logger.warning(f"Attempt {attempt + 1} failed: {str(e)}")
+ if attempt < max_retries - 1:
+ await asyncio.sleep(retry_delay)
+ else:
+ raise Exception(f"Failed to start mesh network after {max_retries} attempts: {str(last_error)}")
+
+ # Start GUI
+ self.logger.info("Initializing GUI...")
+ self.window = ChatWindow(
+ self.mesh_network,
+ self.tor_manager,
+ self.crypto,
+ self.secure_memory
+ )
+
+ self.logger.info("Application fully started")
+ await self.window.run()
+
+ except Exception as e:
+ self.logger.error(f"Fatal error: {str(e)}")
+ await self.cleanup()
+ sys.exit(1)
+
+ async def cleanup(self):
+ """Cleanup with proper error handling"""
+ self.logger.info("Starting cleanup...")
+
+ cleanup_tasks = []
+
+ # Stop Tor
+ if hasattr(self, 'tor_manager'):
+ cleanup_tasks.append(self.tor_manager.stop())
+
+ # Stop mesh network
+ if hasattr(self, 'mesh_network'):
+ cleanup_tasks.append(self.mesh_network.stop())
+
+ # Wipe sensitive data
+ if hasattr(self, 'secure_memory'):
+ self.secure_memory.wipe_all()
+
+ # Wait for all cleanup tasks
+ if cleanup_tasks:
+ await asyncio.gather(*cleanup_tasks, return_exceptions=True)
+
+ self.logger.info("Cleanup completed")
+
+if __name__ == "__main__":
+ app = MeshChat()
+ try:
+ asyncio.run(app.start())
+ except KeyboardInterrupt:
+ app.logger.info("Received shutdown signal")
+ asyncio.run(app.cleanup())
diff --git a/network/__init__.py b/network/__init__.py
new file mode 100644
index 0000000..5df22ef
--- /dev/null
+++ b/network/__init__.py
@@ -0,0 +1,9 @@
+"""
+Network package for P2P/Mesh chat application.
+Contains Tor and mesh networking functionality.
+"""
+
+from .tor_manager import TorManager
+from .mesh import MeshNetwork
+
+__all__ = ['TorManager', 'MeshNetwork']
diff --git a/network/__pycache__/__init__.cpython-313.pyc b/network/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..b5f78e8
--- /dev/null
+++ b/network/__pycache__/__init__.cpython-313.pyc
Binary files differ
diff --git a/network/__pycache__/mesh.cpython-313.pyc b/network/__pycache__/mesh.cpython-313.pyc
new file mode 100644
index 0000000..a563aad
--- /dev/null
+++ b/network/__pycache__/mesh.cpython-313.pyc
Binary files differ
diff --git a/network/__pycache__/tor_manager.cpython-313.pyc b/network/__pycache__/tor_manager.cpython-313.pyc
new file mode 100644
index 0000000..fa8b4cc
--- /dev/null
+++ b/network/__pycache__/tor_manager.cpython-313.pyc
Binary files differ
diff --git a/network/mesh.py b/network/mesh.py
new file mode 100644
index 0000000..f1f65ab
--- /dev/null
+++ b/network/mesh.py
@@ -0,0 +1,304 @@
+import asyncio
+from kademlia.network import Server
+import socket
+import zstandard as zstd
+from typing import List, Dict, Optional, Set
+import time
+import logging
+from dataclasses import dataclass
+from contextlib import asynccontextmanager
+from aiohttp import web
+import threading
+
+@dataclass
+class PeerState:
+ last_seen: float
+ failed_attempts: int = 0
+ is_active: bool = True
+
+class MeshNetwork:
+ def __init__(self, base_port: int = 12345, max_retry_ports: int = 5):
+ self.base_port = base_port
+ self.max_retry_ports = max_retry_ports
+ self.port: Optional[int] = None
+ self.dht = Server()
+ self.peers: Dict[str, PeerState] = {}
+ self.message_buffer: List[Dict] = []
+ self.is_running = False
+ self.known_messages: Set[str] = set()
+ self.retry_interval = 30
+ self.peer_timeout = 300
+ self.logger = logging.getLogger('MeshNetwork')
+ self._http_runner = None
+
+ async def _start_http_server(self):
+ """Start minimal HTTP server for health checks"""
+ if self._http_runner:
+ return True
+
+ app = web.Application()
+ app.router.add_get('/', lambda _: web.Response(text="Mesh network running"))
+ app.router.add_get('/health', lambda _: web.Response(text="OK"))
+
+ self._http_runner = web.AppRunner(app)
+ try:
+ await self._http_runner.setup()
+ site = web.TCPSite(self._http_runner, '0.0.0.0', self.base_port)
+ await site.start()
+ self.logger.info(f"✅ HTTP health check server started on port {self.base_port}")
+ return True
+ except Exception as e:
+ self.logger.error(f"❌ Failed to start HTTP server: {str(e)}")
+ if self._http_runner:
+ await self._http_runner.cleanup()
+ self._http_runner = None
+ return False
+
+ async def _verify_port_active(self, port: int, timeout: int = 5) -> bool:
+ """Wait for port to become active with shorter timeout"""
+ end_time = time.time() + timeout
+ while time.time() < end_time:
+ try:
+ # Use the DHT's bootstrap functionality to verify
+ await self.dht.bootstrap([('127.0.0.1', port)])
+ self.logger.info(f"✅ Port {port} is active and responding")
+ return True
+ except Exception as e:
+ self.logger.debug(f"Port verification attempt failed: {str(e)}")
+ await asyncio.sleep(0.5)
+ self.logger.error(f"❌ Port {port} did not become active within {timeout} seconds")
+ return False
+
+ async def _cleanup_port(self, port: int):
+ """Cleanup a port before trying to use it"""
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind(('0.0.0.0', port))
+ sock.close()
+ except Exception as e:
+ self.logger.debug(f"Port cleanup failed: {str(e)}")
+ await asyncio.sleep(1)
+
+ async def start(self) -> None:
+ """Start mesh network with robust port handling"""
+ # First try to start HTTP server on base port
+ self.logger.info(f"🚀 Starting HTTP health check server on port {self.base_port}")
+ if not await self._start_http_server():
+ self.logger.warning("⚠️ Could not start HTTP server on base port")
+
+ # Try ports for DHT
+ for port_offset in range(self.max_retry_ports):
+ test_port = self.base_port + port_offset + 1 # Start from base_port + 1
+ self.logger.info(f"🔍 Attempting to start DHT on port {test_port}")
+
+ # Cleanup port before use
+ await self._cleanup_port(test_port)
+
+ try:
+ await self.dht.listen(test_port)
+ self.port = test_port
+ self.is_running = True
+
+ # Start maintenance tasks
+ asyncio.create_task(self._peer_maintenance())
+ asyncio.create_task(self._handle_message_buffer())
+ asyncio.create_task(self._heartbeat())
+
+ # Short wait before verification
+ await asyncio.sleep(1)
+
+ # Verify DHT is listening
+ if await self._verify_port_active(test_port):
+ self.logger.info(f"✅ DHT started successfully on port {test_port}")
+ return
+ else:
+ raise RuntimeError(f"DHT failed to bind to port {test_port}")
+
+ except Exception as e:
+ self.logger.error(f"❌ Failed to start on port {test_port}: {str(e)}")
+ if self.dht:
+ try:
+ await self.dht.stop()
+ except:
+ pass
+ self.is_running = False
+ continue
+
+ raise RuntimeError(f"Could not find available port in range {self.base_port+1}-{self.base_port + self.max_retry_ports}")
+
+ async def stop(self) -> None:
+ """Stop mesh network gracefully"""
+ self.logger.info("Stopping mesh network")
+ self.is_running = False
+
+ # Stop HTTP server
+ if self._http_runner:
+ await self._http_runner.cleanup()
+ self._http_runner = None
+
+ # Stop DHT
+ if self.dht:
+ await self.dht.stop()
+
+ await asyncio.sleep(1) # Allow tasks to complete
+ self.logger.info("Mesh network stopped")
+
+ async def broadcast_message(self, message: str) -> None:
+ """Broadcast message to all peers with deduplication"""
+ msg_hash = hash(message)
+ if msg_hash in self.known_messages:
+ return
+
+ self.known_messages.add(msg_hash)
+ compressed = self._compress_message(message)
+
+ failed_peers = []
+ for peer, state in self.peers.items():
+ if not state.is_active:
+ continue
+
+ try:
+ async with self._peer_connection(peer) as conn:
+ await self._send_message(conn, compressed)
+ except Exception as e:
+ self.logger.warning(f"Failed to send to {peer}: {str(e)}")
+ state.failed_attempts += 1
+ if state.failed_attempts >= 3:
+ state.is_active = False
+ failed_peers.append((peer, compressed))
+
+ # Add failed deliveries to buffer
+ for peer, msg in failed_peers:
+ self.message_buffer.append({
+ "peer": peer,
+ "message": msg,
+ "attempts": 0,
+ "timestamp": time.time()
+ })
+
+ @asynccontextmanager
+ async def _peer_connection(self, peer: str):
+ """Context manager for peer connections with timeout"""
+ reader, writer = await asyncio.wait_for(
+ asyncio.open_connection(peer, self.port),
+ timeout=10
+ )
+ try:
+ yield (reader, writer)
+ finally:
+ writer.close()
+ await writer.wait_closed()
+
+ async def _send_message(self, conn, data: bytes) -> None:
+ """Send message with length prefix"""
+ reader, writer = conn
+ # Send length prefix
+ writer.write(len(data).to_bytes(4, 'big'))
+ writer.write(data)
+ await writer.drain()
+
+ async def _peer_maintenance(self) -> None:
+ """Maintain peer list and handle failures"""
+ while self.is_running:
+ current_time = time.time()
+ # Remove stale peers
+ self.peers = {
+ peer: state
+ for peer, state in self.peers.items()
+ if current_time - state.last_seen < self.peer_timeout
+ }
+
+ # Reset failed attempts periodically
+ for state in self.peers.values():
+ if not state.is_active and state.failed_attempts > 0:
+ state.failed_attempts = max(0, state.failed_attempts - 1)
+ if state.failed_attempts == 0:
+ state.is_active = True
+
+ await asyncio.sleep(60)
+
+ async def _handle_message_buffer(self) -> None:
+ """Process buffered messages with exponential backoff"""
+ while self.is_running:
+ current_time = time.time()
+ for msg in self.message_buffer[:]: # Copy to allow modification
+ if msg["attempts"] < 5: # Try 5 times with increasing delays
+ try:
+ await self._send_to_peer(msg["peer"], msg["message"])
+ self.message_buffer.remove(msg)
+ except Exception:
+ msg["attempts"] += 1
+ # Exponential backoff
+ await asyncio.sleep(2 ** msg["attempts"])
+ elif current_time - msg["timestamp"] > 3600: # Remove after 1 hour
+ self.message_buffer.remove(msg)
+
+ await asyncio.sleep(self.retry_interval)
+
+ async def _heartbeat(self) -> None:
+ """Send periodic heartbeats to peers"""
+ while self.is_running:
+ for peer, state in self.peers.items():
+ if not state.is_active:
+ continue
+
+ try:
+ async with self._peer_connection(peer) as conn:
+ await self._send_message(conn, b"PING")
+ state.last_seen = time.time()
+ state.failed_attempts = 0
+ except Exception:
+ state.failed_attempts += 1
+ if state.failed_attempts >= 3:
+ state.is_active = False
+
+ await asyncio.sleep(30) # Heartbeat every 30 seconds
+
+ def _compress_message(self, message: str) -> bytes:
+ """Compress message using zstd with error handling"""
+ try:
+ compressor = zstd.ZstdCompressor(level=3) # Balanced compression
+ return compressor.compress(message.encode())
+ except Exception as e:
+ self.logger.error(f"Compression failed: {str(e)}")
+ # Fallback to uncompressed
+ return message.encode()
+
+ def _decompress_message(self, data: bytes) -> str:
+ """Decompress message with error handling"""
+ try:
+ decompressor = zstd.ZstdDecompressor()
+ return decompressor.decompress(data).decode()
+ except Exception as e:
+ self.logger.error(f"Decompression failed: {str(e)}")
+ # Try to return as-is if decompression fails
+ return data.decode(errors='replace')
+
+ async def _send_to_peer(self, peer: str, data: bytes):
+ async with self._peer_connection(peer) as conn:
+ await self._send_message(conn, data)
+ self.logger.info(f"Message sent to {peer}") #Added logging
+
+ async def _verify_port_available(self, port: int) -> bool:
+ """Verify if a port is available"""
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.bind(('0.0.0.0', port))
+ sock.close()
+ return True
+ except:
+ return False
+
+ async def _wait_for_port_active(self, port: int, timeout: int = 30) -> bool:
+ """Wait for port to become active"""
+ end_time = time.time() + timeout
+ while time.time() < end_time:
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect(('127.0.0.1', port))
+ sock.close()
+ return True
+ except:
+ await asyncio.sleep(1)
+ return False
diff --git a/network/tor_manager.py b/network/tor_manager.py
new file mode 100644
index 0000000..8f91b68
--- /dev/null
+++ b/network/tor_manager.py
@@ -0,0 +1,196 @@
+import os
+import subprocess
+import asyncio
+import socket
+import socks
+from typing import Optional
+import shutil
+import platform
+import sys
+import stat
+
+class TorManager:
+ def __init__(self):
+ self.tor_process: Optional[subprocess.Popen] = None
+ self.onion_address: Optional[str] = None
+ self.socks_port = 9052
+ self.control_port = 9053
+ self.base_dir = self._get_base_dir()
+
+ def _get_base_dir(self) -> str:
+ """Get the portable base directory for Tor files"""
+ if getattr(sys, 'frozen', False):
+ # If running as compiled executable
+ base_path = os.path.dirname(sys.executable)
+ else:
+ # If running from source
+ base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+ # Use a hidden directory in the user's home
+ return os.path.join(os.path.expanduser("~"), ".tormesh")
+
+ def _secure_dir_permissions(self, path: str):
+ """Set secure permissions on directory"""
+ os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) # 700
+
+ def _get_tor_path(self) -> str:
+ """Get platform-specific Tor binary path with fallbacks"""
+ if platform.system() == 'Windows':
+ tor_names = ['tor.exe']
+ else:
+ tor_names = ['tor']
+
+ # Search in system paths first
+ search_paths = [
+ "/usr/bin",
+ "/usr/local/bin",
+ "/opt/homebrew/bin", # For MacOS
+ os.path.join(self.base_dir, "bin")
+ ]
+
+ # Add PATH directories
+ if os.environ.get('PATH'):
+ search_paths.extend(os.environ['PATH'].split(os.pathsep))
+
+ for path in search_paths:
+ for name in tor_names:
+ tor_path = os.path.join(path, name)
+ if os.path.exists(tor_path) and os.access(tor_path, os.X_OK):
+ return tor_path
+
+ raise FileNotFoundError(
+ "Tor binary not found. Please ensure Tor is installed."
+ )
+
+ async def start(self):
+ """Start Tor with robust error handling and retries"""
+ # Create base directory with secure permissions
+ os.makedirs(self.base_dir, exist_ok=True)
+ self._secure_dir_permissions(self.base_dir)
+
+ # Create necessary subdirectories
+ data_dir = os.path.join(self.base_dir, "data")
+ hidden_service_dir = os.path.join(self.base_dir, "hidden_service")
+
+ for directory in [data_dir, hidden_service_dir]:
+ os.makedirs(directory, exist_ok=True)
+ self._secure_dir_permissions(directory)
+
+ # Create torrc configuration
+ torrc_path = os.path.join(self.base_dir, "torrc")
+ with open(torrc_path, "w") as f:
+ f.write(f"""
+SocksPort 127.0.0.1:{self.socks_port}
+ControlPort 127.0.0.1:{self.control_port}
+DataDirectory {data_dir}
+HiddenServiceDir {hidden_service_dir}
+HiddenServicePort 12345 127.0.0.1:12345
+# Improve anonymity
+UseEntryGuards 1
+NumEntryGuards 4
+# Improve resilience
+CircuitBuildTimeout 60
+LearnCircuitBuildTimeout 1
+MaxCircuitDirtiness 600
+NewCircuitPeriod 300
+""")
+ self._secure_dir_permissions(os.path.dirname(torrc_path))
+
+ # Start Tor process with retries
+ max_retries = 3
+ retry_delay = 2
+
+ for attempt in range(max_retries):
+ try:
+ tor_path = self._get_tor_path()
+ print(f"Starting Tor from: {tor_path}")
+
+ self.tor_process = subprocess.Popen(
+ [tor_path, "-f", torrc_path],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+
+ # Wait for Tor to start and verify it's running
+ if await self._wait_for_tor():
+ print(f"Tor started successfully on attempt {attempt + 1}")
+ return
+
+ except Exception as e:
+ print(f"Tor start attempt {attempt + 1} failed: {str(e)}")
+ if self.tor_process:
+ self.tor_process.terminate()
+ await asyncio.sleep(1)
+
+ if attempt < max_retries - 1:
+ await asyncio.sleep(retry_delay)
+ else:
+ raise Exception(f"Failed to start Tor after {max_retries} attempts")
+
+ async def _wait_for_tor(self) -> bool:
+ """Wait for Tor to start with improved verification"""
+ hostname_file = os.path.join(
+ self.base_dir, "hidden_service", "hostname"
+ )
+
+ for _ in range(30): # Wait up to 30 seconds
+ # Check if process is still running
+ if self.tor_process and self.tor_process.poll() is not None:
+ if self.tor_process.stderr:
+ stderr = self.tor_process.stderr.read().decode()
+ raise Exception(f"Tor process died: {stderr}")
+ raise Exception("Tor process died unexpectedly")
+
+ # Check for hostname file
+ if os.path.exists(hostname_file):
+ try:
+ with open(hostname_file, "r") as f:
+ self.onion_address = f.read().strip()
+
+ # Verify SOCKS port is listening
+ sock = socket.socket()
+ try:
+ sock.connect(("127.0.0.1", self.socks_port))
+ sock.close()
+ return True
+ except:
+ pass
+ except:
+ pass
+
+ await asyncio.sleep(1)
+
+ return False
+
+ async def stop(self):
+ """Stop Tor with graceful shutdown"""
+ if self.tor_process:
+ try:
+ self.tor_process.terminate()
+ try:
+ await asyncio.wait_for(
+ asyncio.create_task(self.tor_process.wait()),
+ timeout=5
+ )
+ except asyncio.TimeoutError:
+ self.tor_process.kill()
+
+ # Clear sensitive data
+ if self.onion_address:
+ self.onion_address = None
+
+ # Clean up temporary files securely
+ if os.path.exists(self.base_dir):
+ try:
+ # Securely delete sensitive files
+ for root, dirs, files in os.walk(self.base_dir):
+ for f in files:
+ path = os.path.join(root, f)
+ with open(path, 'wb') as fd:
+ fd.write(os.urandom(os.path.getsize(path)))
+ # Remove directory tree
+ shutil.rmtree(self.base_dir)
+ except:
+ pass
+ except:
+ pass # Ensure cleanup doesn't raise errors
diff --git a/security/__init__.py b/security/__init__.py
new file mode 100644
index 0000000..8b5131e
--- /dev/null
+++ b/security/__init__.py
@@ -0,0 +1,9 @@
+"""
+Security package for P2P/Mesh chat application.
+Handles cryptography and secure memory operations.
+"""
+
+from .crypto import CryptoManager
+from .memory import SecureMemory
+
+__all__ = ['CryptoManager', 'SecureMemory']
diff --git a/security/__pycache__/__init__.cpython-313.pyc b/security/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..45ee6b0
--- /dev/null
+++ b/security/__pycache__/__init__.cpython-313.pyc
Binary files differ
diff --git a/security/__pycache__/crypto.cpython-313.pyc b/security/__pycache__/crypto.cpython-313.pyc
new file mode 100644
index 0000000..3cba246
--- /dev/null
+++ b/security/__pycache__/crypto.cpython-313.pyc
Binary files differ
diff --git a/security/__pycache__/memory.cpython-313.pyc b/security/__pycache__/memory.cpython-313.pyc
new file mode 100644
index 0000000..ec12fd7
--- /dev/null
+++ b/security/__pycache__/memory.cpython-313.pyc
Binary files differ
diff --git a/security/crypto.py b/security/crypto.py
new file mode 100644
index 0000000..f961a21
--- /dev/null
+++ b/security/crypto.py
@@ -0,0 +1,37 @@
+from pyspx.shake_256f import generate_keypair, sign, verify
+import hashlib
+from typing import Tuple, Optional
+import os
+
+class CryptoManager:
+ def __init__(self):
+ self.signing_key: Optional[bytes] = None
+ self.verify_key: Optional[bytes] = None
+
+ def generate_keys(self) -> Tuple[bytes, bytes]:
+ """Generate new signing keypair"""
+ self.signing_key, self.verify_key = generate_keypair()
+ return self.signing_key, self.verify_key
+
+ def sign_message(self, message: bytes) -> bytes:
+ """Sign a message using the signing key"""
+ if not self.signing_key:
+ raise ValueError("No signing key available")
+ return sign(message, self.signing_key)
+
+ def verify_signature(self, message: bytes, signature: bytes,
+ public_key: bytes) -> bool:
+ """Verify a message signature"""
+ try:
+ verify(message, signature, public_key)
+ return True
+ except:
+ return False
+
+ def hash_data(self, data: bytes) -> str:
+ """Create SHA-256 hash of data"""
+ return hashlib.sha256(data).hexdigest()
+
+ def generate_nonce(self) -> bytes:
+ """Generate a random nonce"""
+ return os.urandom(32)
diff --git a/security/memory.py b/security/memory.py
new file mode 100644
index 0000000..6a6e96e
--- /dev/null
+++ b/security/memory.py
@@ -0,0 +1,38 @@
+
+import nacl.bindings
+import ctypes
+import platform
+
+class SecureMemory:
+ def __init__(self):
+ self.protected_blocks = []
+
+ def protect_memory(self, data: bytes) -> bytearray:
+ """Protect memory block from being swapped to disk"""
+ protected = bytearray(data)
+ self.protected_blocks.append(protected)
+
+ if platform.system() != 'Windows':
+ try:
+ # Lock memory to prevent swapping
+ libc = ctypes.CDLL('libc.so.6')
+ libc.mlock(
+ ctypes.c_void_p(protected.__array_interface__['data'][0]),
+ len(protected)
+ )
+ except:
+ pass
+
+ return protected
+
+ def secure_wipe(self, data: bytearray):
+ """Securely wipe memory block"""
+ if data in self.protected_blocks:
+ self.protected_blocks.remove(data)
+
+ nacl.bindings.sodium_memzero(data)
+
+ def wipe_all(self):
+ """Wipe all protected memory blocks"""
+ for block in self.protected_blocks[:]:
+ self.secure_wipe(block)
diff --git a/test_health.py b/test_health.py
new file mode 100644
index 0000000..23ac584
--- /dev/null
+++ b/test_health.py
@@ -0,0 +1,61 @@
+
+# Secure P2P Chat Application
+
+A secure P2P chat application with advanced mesh networking capabilities, designed for resilient and private communication.
+
+## Key Features
+- Mesh Networking Protocol
+- Tor Integration
+- Distributed Hash Table (DHT)
+- Secure Communication Channels
+- Health Monitoring Services
+
+## Project Structure
+```
+.
+├── network/
+│ ├── __init__.py
+│ ├── mesh.py
+│ └── tor_manager.py
+├── security/
+│ ├── __init__.py
+│ ├── crypto.py
+│ └── memory.py
+├── ui/
+│ ├── __init__.py
+│ └── chat_window.py
+├── mesh_chat.py
+├── test_health.py
+└── libraries.html
+```
+
+## Required Dependencies
+```python
+# Install using pip
+aiohttp
+kademlia
+pynacl
+pysocks
+pyspx
+zstandard
+```
+
+## Running the Application
+1. Install dependencies:
+```bash
+pip install aiohttp kademlia pynacl pysocks pyspx zstandard
+```
+
+2. Start the application:
+```bash
+python mesh_chat.py
+```
+
+3. Test health check:
+```bash
+python test_health.py
+```
+
+## Testing
+- HTTP Health Check: Visit http://localhost:12345
+- Health Status: Visit http://localhost:12345/health
diff --git a/tor/torrc b/tor/torrc
new file mode 100644
index 0000000..26377be
--- /dev/null
+++ b/tor/torrc
@@ -0,0 +1,5 @@
+SocksPort 127.0.0.1:9052
+ControlPort 127.0.0.1:9053
+DataDirectory /home/runner/workspace/tor/data
+HiddenServiceDir /home/runner/workspace/tor/hidden_service
+HiddenServicePort 12345 127.0.0.1:12345
diff --git a/tor_data/torrc b/tor_data/torrc
new file mode 100644
index 0000000..46c9498
--- /dev/null
+++ b/tor_data/torrc
@@ -0,0 +1,13 @@
+SocksPort 127.0.0.1:9052
+ControlPort 127.0.0.1:9053
+DataDirectory /home/runner/workspace/tor_data/data
+HiddenServiceDir /home/runner/workspace/tor_data/hidden_service
+HiddenServicePort 12345 127.0.0.1:12345
+# Improve anonymity
+UseEntryGuards 1
+NumEntryGuards 4
+# Improve resilience
+CircuitBuildTimeout 60
+LearnCircuitBuildTimeout 1
+MaxCircuitDirtiness 600
+NewCircuitPeriod 300
diff --git a/ui/__init__.py b/ui/__init__.py
new file mode 100644
index 0000000..89a4640
--- /dev/null
+++ b/ui/__init__.py
@@ -0,0 +1,8 @@
+"""
+UI package for P2P/Mesh chat application.
+Contains chat window and user interface components.
+"""
+
+from .chat_window import ChatWindow
+
+__all__ = ['ChatWindow']
diff --git a/ui/__pycache__/__init__.cpython-313.pyc b/ui/__pycache__/__init__.cpython-313.pyc
new file mode 100644
index 0000000..dda4418
--- /dev/null
+++ b/ui/__pycache__/__init__.cpython-313.pyc
Binary files differ
diff --git a/ui/__pycache__/chat_window.cpython-313.pyc b/ui/__pycache__/chat_window.cpython-313.pyc
new file mode 100644
index 0000000..49785e2
--- /dev/null
+++ b/ui/__pycache__/chat_window.cpython-313.pyc
Binary files differ
diff --git a/ui/chat_window.py b/ui/chat_window.py
new file mode 100644
index 0000000..c341cf1
--- /dev/null
+++ b/ui/chat_window.py
@@ -0,0 +1,159 @@
+import tkinter as tk
+from tkinter import scrolledtext, messagebox
+import asyncio
+from typing import Callable, Optional
+from network.mesh import MeshNetwork
+from network.tor_manager import TorManager
+from security.crypto import CryptoManager
+from security.memory import SecureMemory
+
+class ChatWindow:
+ def __init__(
+ self,
+ mesh_network: MeshNetwork,
+ tor_manager: TorManager,
+ crypto: CryptoManager,
+ secure_memory: SecureMemory
+ ):
+ self.mesh_network = mesh_network
+ self.tor_manager = tor_manager
+ self.crypto = crypto
+ self.secure_memory = secure_memory
+ self.root: Optional[tk.Tk] = None
+ self.is_running = True
+
+ async def run(self):
+ self.root = tk.Tk()
+ self.root.title("Secure Mesh Chat")
+ self.root.geometry("600x800")
+
+ # Status frame
+ status_frame = tk.Frame(self.root)
+ status_frame.pack(fill=tk.X, padx=5, pady=5)
+
+ # Onion address
+ onion_label = tk.Label(
+ status_frame,
+ text=f"Onion: {self.tor_manager.onion_address}",
+ fg="green"
+ )
+ onion_label.pack(side=tk.LEFT)
+
+ # Peer count
+ peer_label = tk.Label(
+ status_frame,
+ text=f"Peers: {len(self.mesh_network.peers)}"
+ )
+ peer_label.pack(side=tk.RIGHT)
+
+ # Chat area
+ self.chat_area = scrolledtext.ScrolledText(
+ self.root,
+ wrap=tk.WORD,
+ height=30
+ )
+ self.chat_area.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
+
+ # Input area
+ input_frame = tk.Frame(self.root)
+ input_frame.pack(fill=tk.X, padx=5, pady=5)
+
+ self.msg_entry = tk.Entry(input_frame)
+ self.msg_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
+
+ send_btn = tk.Button(
+ input_frame,
+ text="Send",
+ command=self._send_message
+ )
+ send_btn.pack(side=tk.RIGHT, padx=5)
+
+ # Control buttons
+ control_frame = tk.Frame(self.root)
+ control_frame.pack(fill=tk.X, padx=5, pady=5)
+
+ tk.Button(
+ control_frame,
+ text="Clear Chat",
+ command=self._clear_chat
+ ).pack(side=tk.LEFT)
+
+ tk.Button(
+ control_frame,
+ text="Network Status",
+ command=self._show_status
+ ).pack(side=tk.RIGHT)
+
+ # Set up close handler
+ self.root.protocol("WM_DELETE_WINDOW", self._on_close)
+
+ # Start message receiver
+ asyncio.create_task(self._receive_messages())
+
+ # Start periodic UI updates
+ asyncio.create_task(self._update_ui())
+
+ while self.is_running:
+ try:
+ self.root.update()
+ await asyncio.sleep(0.1)
+ except tk.TclError: # Window was closed
+ break
+
+ def _send_message(self):
+ msg = self.msg_entry.get()
+ if msg:
+ asyncio.create_task(self.mesh_network.broadcast_message(msg))
+ self.chat_area.insert(tk.END, f"You: {msg}\n")
+ self.msg_entry.delete(0, tk.END)
+
+ def _clear_chat(self):
+ if messagebox.askyesno("Confirm", "Clear all messages?"):
+ # Get current content before clearing
+ content = self.chat_area.get("1.0", tk.END).encode()
+
+ # Clear the chat area first
+ self.chat_area.delete("1.0", tk.END)
+
+ # Then securely wipe the content
+ protected = self.secure_memory.protect_memory(content)
+ self.secure_memory.secure_wipe(protected)
+
+ def _show_status(self):
+ status = f"""
+Network Status:
+--------------
+Tor: {'Running' if self.tor_manager.tor_process else 'Stopped'}
+Onion: {self.tor_manager.onion_address}
+Active Peers: {len(self.mesh_network.peers)}
+Buffered Messages: {len(self.mesh_network.message_buffer)}
+"""
+ messagebox.showinfo("Status", status)
+
+ def _on_close(self):
+ if messagebox.askyesno("Quit", "Are you sure you want to quit?"):
+ self.is_running = False # Stop the main loop
+ if self.root:
+ self.root.quit() # Stop Tkinter
+ self.root.destroy() # Destroy the window
+
+ async def _receive_messages(self):
+ while self.is_running:
+ # Process received messages
+ await asyncio.sleep(0.1)
+
+ async def _update_ui(self):
+ while self.is_running:
+ if not self.root:
+ break
+
+ try:
+ # Update peer count
+ peer_count = len(self.mesh_network.peers)
+ self.root.nametowidget(".!frame.!label2").configure(
+ text=f"Peers: {peer_count}"
+ )
+ except tk.TclError: # Window was closed
+ break
+
+ await asyncio.sleep(1)