From cc78b34d2ee04eb9f980832219a92a059c1d1665 Mon Sep 17 00:00:00 2001 From: schlagmichdoch Date: Mon, 6 Mar 2023 00:06:57 +0100 Subject: [PATCH] Revert making peerId ephemeral to prevent duplication of shown peers on reconnect. Implement peerIdHash to prevent rogue users from overtaking peerIds --- README.md | 1 + index.js | 87 +++++++++++++++---- public/scripts/network.js | 8 ++ .../scripts/network.js | 8 ++ 4 files changed, 88 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 63a807f..7f192b0 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Developed based on [Snapdrop](https://github.com/RobinLinus/snapdrop) * [Progressive Web App](https://wikipedia.org/wiki/Progressive_Web_App) * [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) * [zip.js](https://gildas-lormeau.github.io/zip.js/) +* [cyrb53](https://github.com/bryc) super fast hash function Have any questions? Read our [FAQ](/docs/faq.md). diff --git a/index.js b/index.js index 9e7fd71..d7298a7 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,11 @@ const crypto = require('crypto') const {spawn} = require('child_process') const WebSocket = require('ws'); const fs = require('fs'); +const parser = require('ua-parser-js'); +const { uniqueNamesGenerator, animals, colors } = require('unique-names-generator'); +const express = require('express'); +const RateLimit = require('express-rate-limit'); +const http = require('http'); // Handle SIGINT process.on('SIGINT', () => { @@ -70,10 +75,6 @@ const rtcConfig = process.env.RTC_CONFIG ] }; -const express = require('express'); -const RateLimit = require('express-rate-limit'); -const http = require('http'); - const app = express(); if (process.argv.includes('--rate-limit')) { @@ -114,9 +115,6 @@ if (process.argv.includes('--localhost-only')) { server.listen(port); } -const parser = require('ua-parser-js'); -const { uniqueNamesGenerator, animals, colors } = require('unique-names-generator'); - class PairDropServer { constructor() { @@ -145,7 +143,8 @@ class PairDropServer { message: { displayName: peer.name.displayName, deviceName: peer.name.deviceName, - peerId: peer.id + peerId: peer.id, + peerIdHash: peer.id.hashCode128BitSalted() } }); } @@ -477,7 +476,7 @@ class Peer { this._setIP(request); // set peer id - this.id = crypto.randomUUID(); + this._setPeerId(request) // is WebRTC supported ? this.rtcSupported = request.url.indexOf('webrtc') > -1; @@ -549,6 +548,17 @@ class Peer { return false; } + _setPeerId(request) { + const searchParams = new URL(request.url, "http://server").searchParams; + let peerId = searchParams.get("peer_id"); + let peerIdHash = searchParams.get("peer_id_hash"); + if (peerId && Peer.isValidUuid(peerId) && this.isPeerIdHashValid(peerId, peerIdHash)) { + this.id = peerId; + } else { + this.id = crypto.randomUUID(); + } + } + toString() { return `` } @@ -602,6 +612,10 @@ class Peer { return /^([0-9]|[a-f]){8}-(([0-9]|[a-f]){4}-){3}([0-9]|[a-f]){12}$/.test(uuid); } + isPeerIdHashValid(peerId, peerIdHash) { + return peerIdHash === peerId.hashCode128BitSalted(); + } + addRoomSecret(roomSecret) { if (!(roomSecret in this.roomSecrets)) { this.roomSecrets.push(roomSecret); @@ -617,14 +631,55 @@ class Peer { Object.defineProperty(String.prototype, 'hashCode', { value: function() { - var hash = 0, i, chr; - for (i = 0; i < this.length; i++) { - chr = this.charCodeAt(i); - hash = ((hash << 5) - hash) + chr; - hash |= 0; // Convert to 32bit integer - } - return hash; + return cyrb53(this); } }); +Object.defineProperty(String.prototype, 'hashCode128BitSalted', { + value: function() { + return hasher.hashCode128BitSalted(this); + } +}); + +const hasher = (() => { + let seeds; + return { + hashCode128BitSalted(str) { + if (!seeds) { + // seeds are created on first call to salt hash. + seeds = [4]; + for (let i=0; i<4; i++) { + const randomBuffer = new Uint32Array(1); + crypto.webcrypto.getRandomValues(randomBuffer); + seeds[i] = randomBuffer[0]; + } + } + let hashCode = ""; + for (let i=0; i<4; i++) { + hashCode += cyrb53(str, seeds[i]); + } + return hashCode; + } + } + +})() + +/* + cyrb53 (c) 2018 bryc (github.com/bryc) + A fast and simple hash function with decent collision resistance. + Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity. + Public domain. Attribution appreciated. +*/ +const cyrb53 = function(str, seed = 0) { + let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; + for (let i = 0, ch; i < str.length; i++) { + ch = str.charCodeAt(i); + h1 = Math.imul(h1 ^ ch, 2654435761); + h2 = Math.imul(h2 ^ ch, 1597334677); + } + h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909); + h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909); + return 4294967296 * (2097151 & h2) + (h1>>>0); +}; + new PairDropServer(); diff --git a/public/scripts/network.js b/public/scripts/network.js index 7f9bc44..55fdc89 100644 --- a/public/scripts/network.js +++ b/public/scripts/network.js @@ -118,6 +118,8 @@ class ServerConnection { } _onDisplayName(msg) { + sessionStorage.setItem("peerId", msg.message.peerId); + sessionStorage.setItem("peerIdHash", msg.message.peerIdHash); Events.fire('display-name', msg); } @@ -126,6 +128,12 @@ class ServerConnection { const protocol = location.protocol.startsWith('https') ? 'wss' : 'ws'; const webrtc = window.isRtcSupported ? '/webrtc' : '/fallback'; let ws_url = new URL(protocol + '://' + location.host + location.pathname + 'server' + webrtc); + const peerId = sessionStorage.getItem("peerId"); + const peerIdHash = sessionStorage.getItem("peerIdHash"); + if (peerId && peerIdHash) { + ws_url.searchParams.append('peer_id', peerId); + ws_url.searchParams.append('peer_id_hash', peerIdHash); + } return ws_url.toString(); } diff --git a/public_included_ws_fallback/scripts/network.js b/public_included_ws_fallback/scripts/network.js index 58e0019..4c5b255 100644 --- a/public_included_ws_fallback/scripts/network.js +++ b/public_included_ws_fallback/scripts/network.js @@ -129,6 +129,8 @@ class ServerConnection { } _onDisplayName(msg) { + sessionStorage.setItem("peerId", msg.message.peerId); + sessionStorage.setItem("peerIdHash", msg.message.peerIdHash); Events.fire('display-name', msg); } @@ -137,6 +139,12 @@ class ServerConnection { const protocol = location.protocol.startsWith('https') ? 'wss' : 'ws'; const webrtc = window.isRtcSupported ? '/webrtc' : '/fallback'; let ws_url = new URL(protocol + '://' + location.host + location.pathname + 'server' + webrtc); + const peerId = sessionStorage.getItem("peerId"); + const peerIdHash = sessionStorage.getItem("peerIdHash"); + if (peerId && peerIdHash) { + ws_url.searchParams.append('peer_id', peerId); + ws_url.searchParams.append('peer_id_hash', peerIdHash); + } return ws_url.toString(); }