diff options
author | metamuffin <metamuffin@disroot.org> | 2022-09-09 15:35:56 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2022-09-09 15:35:56 +0200 |
commit | de8e2d40ed2744c4c25ec7cdbe778e1723dbb830 (patch) | |
tree | 11d7e40b6c74946f2c0a3fa6a416b729b2582146 | |
parent | 8aaf7e201e58ec9ecb431a6ac05e07d0078b12b0 (diff) | |
download | keks-meet-de8e2d40ed2744c4c25ec7cdbe778e1723dbb830.tar keks-meet-de8e2d40ed2744c4c25ec7cdbe778e1723dbb830.tar.bz2 keks-meet-de8e2d40ed2744c4c25ec7cdbe778e1723dbb830.tar.zst |
refactor + identify
-rw-r--r-- | client-web/source/logger.ts | 2 | ||||
-rw-r--r-- | client-web/source/protocol/crypto.ts | 10 | ||||
-rw-r--r-- | client-web/source/protocol/mod.ts | 1 | ||||
-rw-r--r-- | client-web/source/room.ts | 13 | ||||
-rw-r--r-- | client-web/source/user/local.ts | 11 | ||||
-rw-r--r-- | client-web/source/user/mod.ts | 21 | ||||
-rw-r--r-- | client-web/source/user/remote.ts | 23 | ||||
-rw-r--r-- | common/packets.d.ts | 2 | ||||
-rw-r--r-- | readme.md | 3 | ||||
-rw-r--r-- | server/src/main.rs | 3 |
10 files changed, 57 insertions, 32 deletions
diff --git a/client-web/source/logger.ts b/client-web/source/logger.ts index 23f2cf6..935eb20 100644 --- a/client-web/source/logger.ts +++ b/client-web/source/logger.ts @@ -40,7 +40,7 @@ export function log(k: LogScope | LogDesc, message: string, ...data: unknown[]) setTimeout(() => { e.remove() }, 1000 + 500) - }, (d.error || d.warn) ? 20000 : 3000) + }, (d.error || d.warn) ? 30000 : 3000) } } diff --git a/client-web/source/protocol/crypto.ts b/client-web/source/protocol/crypto.ts index 742f22c..79b7e1d 100644 --- a/client-web/source/protocol/crypto.ts +++ b/client-web/source/protocol/crypto.ts @@ -2,6 +2,8 @@ import { log } from "../logger.ts"; //! I am not a crypto expert at all! Please read carefully and report any issues to me. +const IV_LENGTH = 12 + export async function crypto_seeded_key(seed: string): Promise<CryptoKey> { log("crypto", "importing seed…") const seed_key = await window.crypto.subtle.importKey( @@ -11,7 +13,7 @@ export async function crypto_seeded_key(seed: string): Promise<CryptoKey> { false, ["deriveKey"] ) - //? TODO is it possible to use a unique seed per session here? + //? TODO is it possible to use a unique seed per session here? // const salt = window.crypto.getRandomValues(new Uint8Array(16)); const salt = base64_to_buf("thisisagoodsaltAAAAAAA==") // valid "unique" 16-byte base-64 string log("crypto", "deriving key…") @@ -40,7 +42,7 @@ export async function crypt_hash(input: string): Promise<string> { } export async function crypto_encrypt(key: CryptoKey, data: string): Promise<string> { - const iv = window.crypto.getRandomValues(new Uint8Array(12)); + const iv = window.crypto.getRandomValues(new Uint8Array(IV_LENGTH)); const ciphertext = new Uint8Array(await window.crypto.subtle.encrypt( { name: "AES-GCM", iv }, key, @@ -55,8 +57,8 @@ export async function crypto_encrypt(key: CryptoKey, data: string): Promise<stri export async function crypt_decrypt(key: CryptoKey, data: string): Promise<string> { const buf = base64_to_buf(data); - const iv = buf.slice(0, 12); - const ciphertext = buf.slice(12); + const iv = buf.slice(0, IV_LENGTH); + const ciphertext = buf.slice(IV_LENGTH); const decryptedContent = await window.crypto.subtle.decrypt( { name: "AES-GCM", iv }, key, diff --git a/client-web/source/protocol/mod.ts b/client-web/source/protocol/mod.ts index f976f23..03afa8e 100644 --- a/client-web/source/protocol/mod.ts +++ b/client-web/source/protocol/mod.ts @@ -19,6 +19,7 @@ export class SignalingConnection { const ws_url = new URL(`${window.location.protocol.endsWith("s:") ? "wss" : "ws"}://${window.location.host}/signaling/${encodeURIComponent(this.signaling_id)}`) this.websocket = new WebSocket(ws_url) this.websocket.onerror = () => this.on_error() + this.websocket.onclose = () => this.on_close() this.websocket.onmessage = e => { if (typeof e.data == "string") this.on_message(e.data) } diff --git a/client-web/source/room.ts b/client-web/source/room.ts index 3268510..5813faa 100644 --- a/client-web/source/room.ts +++ b/client-web/source/room.ts @@ -33,26 +33,21 @@ export class Room { } else { const ru = new RemoteUser(this, p.id) this.local_user.add_initial_to_remote(ru) + this.local_user.identify(ru.id) ru.offer() - this.users.set(p.id, ru) - this.remote_users.set(p.id, ru) } } else if (packet.client_leave) { const p = packet.client_leave; log("*", `${p.id} left`); - this.remote_users.get(p.id)!.leave() - this.users.delete(p.id) - this.remote_users.delete(p.id) + this.users.get(p.id)!.leave() } } relay_handler(sender_id: number, message: RelayMessage) { const sender = this.users.get(sender_id) if (sender instanceof RemoteUser) { - if (message.ice_candidate) sender.add_ice_candidate(message.ice_candidate) - if (message.offer) sender.on_offer(message.offer) - if (message.answer) sender.on_answer(message.answer) + sender.on_relay(message) } else { - console.log("!", message, sender); + console.warn("we received a message for ourselves, the server might be broken"); } } }
\ No newline at end of file diff --git a/client-web/source/user/local.ts b/client-web/source/user/local.ts index a741726..2ea1ea6 100644 --- a/client-web/source/user/local.ts +++ b/client-web/source/user/local.ts @@ -7,7 +7,7 @@ import { get_rnnoise_node } from "../rnnoise.ts"; import { Room } from "../room.ts"; import { TrackHandle } from "../track_handle.ts"; import { User } from "./mod.ts"; -import { BOTTOM_CONTAINER } from "../index.ts"; +import { BOTTOM_CONTAINER, ROOM_CONTAINER } from "../index.ts"; export class LocalUser extends User { mic_gain?: GainNode @@ -17,10 +17,16 @@ export class LocalUser extends User { super(room, id) this.el.classList.add("local") this.local = true + this.name = PREFS.username this.create_controls() this.add_initial_tracks() log("usermodel", `added local user: ${this.display_name}`) } + leave() { // we might never need this but ok + this.room.local_user = undefined as unknown as LocalUser + super.leave() + ROOM_CONTAINER.removeChild(this.el) + } async add_initial_tracks() { if (PREFS.microphone_enabled) this.publish_track(await this.create_mic_track()) @@ -43,6 +49,9 @@ export class LocalUser extends User { add_initial_to_remote(u: RemoteUser) { this.tracks.forEach(t => u.peer.addTrack(t.track)) } + identify(recipient?: number) { + if (this.name) this.room.signaling.send_relay({ identify: { username: this.name } }, recipient) + } create_controls() { const mic_toggle = document.createElement("input") diff --git a/client-web/source/user/mod.ts b/client-web/source/user/mod.ts index c0aa6be..cbe9468 100644 --- a/client-web/source/user/mod.ts +++ b/client-web/source/user/mod.ts @@ -9,15 +9,25 @@ import { TrackHandle } from "../track_handle.ts"; export abstract class User { protected el: HTMLElement public local = false - public name?: string protected tracks: Set<TrackHandle> = new Set() + private name_el = document.createElement("span") + private _name?: string + get name() { return this._name } + set name(n: string | undefined) { this._name = n; this.name_el.textContent = this.display_name } + get display_name() { return this.name ?? `guest (${this.id})` } + constructor(public room: Room, public id: number) { + room.users.set(this.id, this) + this.el = document.createElement("div") this.el.classList.add("user") ROOM_CONTAINER.append(this.el) this.setup_view() } + leave() { + this.room.users.delete(this.id) + } add_track(t: TrackHandle) { this.tracks.add(t) @@ -34,15 +44,12 @@ export abstract class User { }) } - get display_name() { return this.name ?? `guest (${this.id})` } - setup_view() { const info_el = document.createElement("div") info_el.classList.add("info") - const name_el = document.createElement("span") - name_el.textContent = this.display_name - name_el.classList.add("name") - info_el.append(name_el) + this.name_el.textContent = this.display_name + this.name_el.classList.add("name") + info_el.append(this.name_el) this.el.append(info_el) } diff --git a/client-web/source/user/remote.ts b/client-web/source/user/remote.ts index ced8482..dbae3db 100644 --- a/client-web/source/user/remote.ts +++ b/client-web/source/user/remote.ts @@ -1,5 +1,6 @@ /// <reference lib="dom" /> +import { RelayMessage } from "../../../common/packets.d.ts"; import { ROOM_CONTAINER, RTC_CONFIG } from "../index.ts" import { log } from "../logger.ts" import { Room } from "../room.ts" @@ -12,6 +13,8 @@ export class RemoteUser extends User { constructor(room: Room, id: number) { super(room, id) + room.remote_users.set(this.id, this) + log("usermodel", `added remote user: ${id}`) this.peer = new RTCPeerConnection(RTC_CONFIG) this.peer.onicecandidate = ev => { @@ -31,6 +34,20 @@ export class RemoteUser extends User { this.offer() } } + leave() { + log("usermodel", `remove remote user: ${this.display_name}`) + this.peer.close() + this.room.remote_users.delete(this.id) + super.leave() + ROOM_CONTAINER.removeChild(this.el) + } + + on_relay(message: RelayMessage) { + if (message.ice_candidate) this.add_ice_candidate(message.ice_candidate) + if (message.offer) this.on_offer(message.offer) + if (message.answer) this.on_answer(message.answer) + if (message.identify) this.name = message.identify.username + } async offer() { this.negotiation_busy = true @@ -65,10 +82,4 @@ export class RemoteUser extends User { add_ice_candidate(candidate: RTCIceCandidateInit) { this.peer.addIceCandidate(new RTCIceCandidate(candidate)) } - - leave() { - log("usermodel", `remove remote user: ${this.display_name}`) - this.peer.close() - ROOM_CONTAINER.removeChild(this.el) - } }
\ No newline at end of file diff --git a/common/packets.d.ts b/common/packets.d.ts index fe3be33..3e5b612 100644 --- a/common/packets.d.ts +++ b/common/packets.d.ts @@ -17,7 +17,7 @@ export interface ServerboundPacket { } export interface RelayMessage { - identify?: { name: string } + identify?: { username: string } offer?: F_RTCSessionDescriptionInit, answer?: F_RTCSessionDescriptionInit, ice_candidate?: F_RTCIceCandidateInit, @@ -11,6 +11,7 @@ a web conferencing application - Screen capture - Multiple streams - Noise suppression (rnnoise) +- End-to-end-encryption ## Todo-List @@ -23,6 +24,7 @@ a web conferencing application - Test some options like `camera_facing_mode` - Signing key for each user - Built-in storage for known keys +- Prevent a client from sendin differing user names to other clients ## Security @@ -31,7 +33,6 @@ keks-meet _tries_ to be secure. However I am not a security expert. The current - The room name is set in the section of the URL (-> not sent to the server) - The server receives a salted hash of the room name to group client of a room - We use PBKDF2 (constant salt; 250000 iterations) to derive a 256-bit AES-GCM key -- - All relayed message contents are encrypted with this key. - Message recipient is visible to the server - The server assigns user ids diff --git a/server/src/main.rs b/server/src/main.rs index dae6662..57e12f6 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,13 +1,12 @@ pub mod protocol; pub mod room; -use hyper::{header, StatusCode, Uri}; +use hyper::{header, StatusCode}; use listenfd::ListenFd; use log::error; use room::Room; use std::collections::HashMap; use std::convert::Infallible; -use std::str::FromStr; use std::sync::Arc; use tokio::sync::RwLock; use warp::hyper::Server; |