#!/usr/bin/env node /** * Mock SigSocket Server for Testing Browser Extension * * This is a simple WebSocket server that simulates the SigSocket protocol * for testing the browser extension functionality. * * Usage: * node mock_sigsocket_server.js * * The server will listen on ws://localhost:8080/ws */ const WebSocket = require('ws'); const http = require('http'); class MockSigSocketServer { constructor(port = 8080) { this.port = port; this.clients = new Map(); // clientId -> { ws, publicKey } this.requestCounter = 0; this.setupServer(); } setupServer() { // Create HTTP server this.httpServer = http.createServer(); // Create WebSocket server this.wss = new WebSocket.Server({ server: this.httpServer, path: '/ws' }); this.wss.on('connection', (ws, req) => { console.log('New WebSocket connection from:', req.socket.remoteAddress); this.handleConnection(ws); }); this.httpServer.listen(this.port, () => { console.log(`Mock SigSocket Server listening on ws://localhost:${this.port}/ws`); console.log('Waiting for browser extension connections...'); }); } handleConnection(ws) { let clientId = null; let publicKey = null; ws.on('message', (data) => { try { const message = data.toString(); console.log('Received message:', message); // Check if this is a client introduction (hex-encoded public key) if (!clientId && this.isHexString(message)) { publicKey = message; clientId = this.generateClientId(); this.clients.set(clientId, { ws, publicKey }); console.log(`Client registered: ${clientId} with public key: ${publicKey.substring(0, 16)}...`); // Send welcome message ws.send(JSON.stringify({ type: 'welcome', clientId: clientId, message: 'Connected to Mock SigSocket Server' })); // Schedule a test sign request after 3 seconds setTimeout(() => { this.sendTestSignRequest(clientId); }, 3000); return; } // Try to parse as JSON (sign response) try { const jsonMessage = JSON.parse(message); this.handleSignResponse(clientId, jsonMessage); } catch (e) { console.log('Received non-JSON message:', message); } } catch (error) { console.error('Error handling message:', error); } }); ws.on('close', () => { if (clientId) { this.clients.delete(clientId); console.log(`Client disconnected: ${clientId}`); } }); ws.on('error', (error) => { console.error('WebSocket error:', error); }); } handleSignResponse(clientId, response) { console.log(`Received sign response from ${clientId}:`, response); if (response.id && response.signature) { console.log(`āœ… Sign request ${response.id} completed successfully`); console.log(` Signature: ${response.signature.substring(0, 32)}...`); // Send another test request after 10 seconds setTimeout(() => { this.sendTestSignRequest(clientId); }, 10000); } else { console.log('āŒ Invalid sign response format'); } } sendTestSignRequest(clientId) { const client = this.clients.get(clientId); if (!client) { console.log(`Client ${clientId} not found`); return; } this.requestCounter++; const requestId = `req_${this.requestCounter}_${Date.now()}`; const testMessage = `Test message ${this.requestCounter} - ${new Date().toISOString()}`; const messageBase64 = Buffer.from(testMessage).toString('base64'); const signRequest = { id: requestId, message: messageBase64 }; console.log(`šŸ“ Sending sign request to ${clientId}:`, requestId); console.log(` Message: "${testMessage}"`); try { client.ws.send(JSON.stringify(signRequest)); } catch (error) { console.error(`Failed to send sign request to ${clientId}:`, error); } } isHexString(str) { return /^[0-9a-fA-F]+$/.test(str) && str.length >= 32; // At least 16 bytes } generateClientId() { return `client_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`; } // Send a test request to all connected clients broadcastTestRequest() { console.log('\nšŸ“¢ Broadcasting test sign request to all clients...'); for (const [clientId] of this.clients) { this.sendTestSignRequest(clientId); } } // Get server status getStatus() { return { port: this.port, connectedClients: this.clients.size, clients: Array.from(this.clients.keys()) }; } } // Create and start the server const server = new MockSigSocketServer(); // Handle graceful shutdown process.on('SIGINT', () => { console.log('\nšŸ›‘ Shutting down Mock SigSocket Server...'); server.httpServer.close(() => { console.log('Server closed'); process.exit(0); }); }); // Add some interactive commands process.stdin.setEncoding('utf8'); console.log('\nšŸ“‹ Available commands:'); console.log(' "test" - Send test sign request to all clients'); console.log(' "status" - Show server status'); console.log(' "quit" - Shutdown server'); console.log(' Type a command and press Enter\n'); process.stdin.on('readable', () => { const chunk = process.stdin.read(); if (chunk !== null) { const command = chunk.trim().toLowerCase(); switch (command) { case 'test': server.broadcastTestRequest(); break; case 'status': const status = server.getStatus(); console.log('\nšŸ“Š Server Status:'); console.log(` Port: ${status.port}`); console.log(` Connected clients: ${status.connectedClients}`); if (status.clients.length > 0) { console.log(` Client IDs: ${status.clients.join(', ')}`); } console.log(''); break; case 'quit': case 'exit': process.emit('SIGINT'); break; case '': break; default: console.log(`Unknown command: ${command}`); break; } } }); // Export for testing module.exports = MockSigSocketServer;