summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client-web/source/logger.ts2
-rw-r--r--client-web/source/protocol/crypto.ts10
-rw-r--r--client-web/source/protocol/mod.ts1
-rw-r--r--client-web/source/room.ts13
-rw-r--r--client-web/source/user/local.ts11
-rw-r--r--client-web/source/user/mod.ts21
-rw-r--r--client-web/source/user/remote.ts23
-rw-r--r--common/packets.d.ts2
-rw-r--r--readme.md3
-rw-r--r--server/src/main.rs3
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,
diff --git a/readme.md b/readme.md
index c3ae098..bbab7d4 100644
--- a/readme.md
+++ b/readme.md
@@ -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;