diff --git a/src/config/constants.js b/src/config/constants.js index 2f1663b..028b8bf 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -1,6 +1,6 @@ const crypto = require("crypto"); -const TOPIC_NAME = process.env.TOPIC_NAME || "hypermind-lklynet-v1"; +const TOPIC_NAME = process.env.TOPIC_NAME || "hypermind-lklynet-v2"; const TOPIC = crypto.createHash("sha256").update(TOPIC_NAME).digest(); /** diff --git a/src/p2p/messaging.js b/src/p2p/messaging.js index 5597b71..2a03e3b 100644 --- a/src/p2p/messaging.js +++ b/src/p2p/messaging.js @@ -3,6 +3,7 @@ const { verifySignature, createPublicKey, } = require("../core/security"); +const crypto = require("crypto"); const { MAX_RELAY_HOPS, ENABLE_CHAT, CHAT_RATE_LIMIT } = require("../config/constants"); const { BloomFilterManager } = require("../state/bloom"); @@ -177,6 +178,22 @@ class MessageHandler { } else if (scope === 'GLOBAL') { if (!sig || !id) return; + // Integrity Check: Ensure ID matches content/timestamp + // This binds the signature (which signs ID) to the content + const idBase = sender + msg.content + msg.timestamp; + const computedId = crypto.createHash('sha256').update(idBase).digest('hex'); + + if (computedId !== id) { + this.diagnostics.increment("invalidSig"); // Reuse metric for integrity failure + return; + } + + // Timestamp Check: Prevent replays beyond Bloom filter window + // Allow 1 minute drift/delay + if (Math.abs(now - msg.timestamp) > 60000) { + return; + } + // Check signature const key = createPublicKey(sender); if (!verifySignature(`chat:${id}`, sig, key)) { diff --git a/src/p2p/swarm.js b/src/p2p/swarm.js index 2757eff..9ae8ae3 100644 --- a/src/p2p/swarm.js +++ b/src/p2p/swarm.js @@ -47,18 +47,25 @@ class SwarmManager { socket.write(hello); this.broadcastFn(); + socket.buffer = ""; + socket.on("data", (data) => { this.diagnostics.increment("bytesReceived", data.length); - try { - const msgs = data - .toString() - .split("\n") - .filter((x) => x.trim()); - for (const msgStr of msgs) { + socket.buffer += data.toString(); + + const lines = socket.buffer.split("\n"); + // The last element is either an empty string (if data ended with \n) + // or the incomplete part of the next message. + socket.buffer = lines.pop(); + + for (const msgStr of lines) { + if (!msgStr.trim()) continue; + try { const msg = JSON.parse(msgStr); this.messageHandler.handleMessage(msg, socket); + } catch (e) { + // Invalid JSON or partial message (shouldn't happen with buffering logic unless data is corrupted) } - } catch (e) { } });