From 834267399f9ce66362d73e0bcf94da2eee893273 Mon Sep 17 00:00:00 2001 From: Sam Hadow Date: Tue, 4 Mar 2025 18:58:43 +0100 Subject: [PATCH] ECDH key exchange --- src/app.js | 22 +++++++++++++++++++--- src/db.js | 30 ++++++++++++++++++++++++++++++ src/public/chat.js | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/app.js b/src/app.js index bc0a682..bb8c46c 100644 --- a/src/app.js +++ b/src/app.js @@ -40,16 +40,32 @@ app.use('/', express.static(__dirname + '/public')); // socket.io io.engine.use(sessionMiddleware); -io.on('connection', (socket) => { +io.on('connection', async (socket) => { const session = socket.request.session; + socket.join(session.publicKey); console.log('A user connected'); + const peers = await database.getPeers(session.publicKey); + peers.forEach(peer => { + socket.to(peer).emit('connected', session.publicKey); + }); socket.on('disconnect', () => { console.log('User disconnected'); }); - socket.on('chat message', (msg, room) => { - console.log('message: ' + msg + ', sender: ' + session.id + 'room: ' + room.substring(5)); + socket.on('chat message', async (msg, room) => { + const roomid = room.substring(5); + const members = await database.getRoomMembers(roomid); + members.forEach(memberRoom => { + socket.to(memberRoom).emit('chat message', msg, roomid, session.publicKey); + }); + console.log('message: ' + msg + ', sender: ' + session.id); console.log(session.publicKey); }); + socket.on('key exchange', (user_pubkey, pubkey) => { + socket.to(user_pubkey).emit('key exchange', session.publicKey, pubkey); + }); + socket.on('key exchange 2', (user_pubkey, pubkey) => { + socket.to(user_pubkey).emit('key exchange 2', session.publicKey, pubkey); + }); }); diff --git a/src/db.js b/src/db.js index 7a0a58b..34d7ecb 100644 --- a/src/db.js +++ b/src/db.js @@ -143,6 +143,36 @@ const database = { throw err; } }, + getRoomMembers: async (roomid) => { + try { + const members = await pool.query( + 'SELECT u.pubkey FROM room_members r, users u WHERE r.user_uuid = u.uuid AND r.room_uuid = $1', + [roomid] + ); + return members.rows.map(row => row.pubkey); + } catch (err) { + console.error('Error retrieving rooms:', err); + throw err; + } + }, + getPeers: async (pubkey) => { + try { + const peers = await pool.query( + `SELECT u1.pubkey + FROM room_members r1, room_members r2, users u1, users u2 + WHERE r1.user_uuid = u1.uuid + AND r2.user_uuid = u2.uuid + AND r1.room_uuid = r2.room_uuid + AND u2.pubkey != u1.pubkey + AND u2.pubkey = $1`, + [pubkey] + ); + return peers.rows.map(row => row.pubkey); + } catch (err) { + console.error('Error retrieving peers:', err); + throw err; + } + }, getPublicKeys: async () => { try { const result = await pool.query('SELECT pubkey FROM users'); diff --git a/src/public/chat.js b/src/public/chat.js index 26abc79..e393d96 100644 --- a/src/public/chat.js +++ b/src/public/chat.js @@ -1,4 +1,37 @@ +import { genKeys, sharedKey } from "./ecdh.js"; + const socket = io(); +let secret = null; +let sharedsecret = {}; + +socket.on('chat message', (msg, room) => { + console.log(`received: ${msg}`); + console.log(sharedsecret); + const item = document.createElement('li'); + item.textContent = msg; + const messages = document.getElementById(`messages-${room}`); + messages.appendChild(item); + window.scrollTo(0, document.body.scrollHeight); +}); + +socket.on('connected', (user_pubkey) => { + const keys = genKeys(); + secret = keys.privkey; + socket.emit('key exchange', user_pubkey, toHexString(keys.pubkey)); +}); + +socket.on('key exchange', (user_pubkey, pubkey) => { + const keys = genKeys(); + secret = keys.privkey + sharedsecret[user_pubkey] = sharedKey(secret, fromHexString(pubkey)); + socket.emit('key exchange 2', user_pubkey, toHexString(keys.pubkey)); + console.log(`shared secret: ${toHexString(sharedsecret[user_pubkey])}`); +}); + +socket.on('key exchange 2', (user_pubkey, pubkey) => { + sharedsecret[user_pubkey] = sharedKey(secret, fromHexString(pubkey)); + console.log(`shared secret: ${toHexString(sharedsecret[user_pubkey])}`); +}); export function create_listener(form, input) { form.addEventListener('submit', function(e) { @@ -9,3 +42,9 @@ export function create_listener(form, input) { } }); } + +const fromHexString = (hexString) => + Uint8Array.from(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))); + +const toHexString = (bytes) => + bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');