aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--client-web/source/protocol/crypto.ts34
-rw-r--r--client-web/source/protocol/mod.ts17
-rw-r--r--common/packets.d.ts9
-rw-r--r--readme.md5
4 files changed, 55 insertions, 10 deletions
diff --git a/client-web/source/protocol/crypto.ts b/client-web/source/protocol/crypto.ts
index 654e80b..76f48c0 100644
--- a/client-web/source/protocol/crypto.ts
+++ b/client-web/source/protocol/crypto.ts
@@ -73,6 +73,40 @@ export async function crypt_decrypt(key: CryptoKey, data: string): Promise<strin
}
}
+//* Code that might be useful in the future for signing
+// const ECDSA_PARAMS = { name: "ECDSA", namedCurve: "P-521", hash: { name: "SHA-384" } }
+// export async function crypto_sign(key: CryptoKey, message: string): Promise<string> {
+// const signature = await crypto.subtle.sign(
+// ECDSA_PARAMS,
+// key,
+// new TextEncoder().encode(message)
+// )
+// return buf_to_base64(new Uint8Array(signature))
+// }
+// export async function crypto_generate_signing_key(): Promise<CryptoKeyPair> {
+// return await crypto.subtle.generateKey(
+// ECDSA_PARAMS,
+// false,
+// ["sign", "verify"]
+// )
+// }
+// export async function crypto_verify(key: CryptoKey, message: string, signature: string): Promise<boolean> {
+// return await crypto.subtle.verify(
+// ECDSA_PARAMS,
+// key,
+// base64_to_buf(signature).buffer,
+// new TextEncoder().encode(message)
+// )
+// }
+// export async function export_public_signing_key(key: CryptoKey): Promise<string> {
+// const buf = await crypto.subtle.exportKey("spki", key)
+// return buf_to_base64(new Uint8Array(buf))
+// }
+// export async function import_public_signing_key(der: string): Promise<CryptoKey | undefined> {
+// const bin_der = base64_to_buf(der).buffer; // TODO safety
+// return await crypto.subtle.importKey("spki", bin_der, ECDSA_PARAMS, true, ["verify"]) // TODO safety
+// }
+
export function base64_to_buf(data: string): Uint8Array {
const binary_string = globalThis.atob(data);
const bytes = new Uint8Array(binary_string.length);
diff --git a/client-web/source/protocol/mod.ts b/client-web/source/protocol/mod.ts
index b225ced..c756742 100644
--- a/client-web/source/protocol/mod.ts
+++ b/client-web/source/protocol/mod.ts
@@ -1,4 +1,4 @@
-import { ClientboundPacket, RelayMessage, ServerboundPacket } from "../../../common/packets.d.ts"
+import { ClientboundPacket, RelayMessage, RelayMessageWrapper, ServerboundPacket } from "../../../common/packets.d.ts"
import { log } from "../logger.ts"
import { crypto_encrypt, crypto_seeded_key, crypt_decrypt, crypt_hash } from "./crypto.ts"
@@ -7,6 +7,7 @@ export class SignalingConnection {
websocket!: WebSocket
signaling_id!: string
key!: CryptoKey
+ my_id?: number // needed for outgoing relay messages
control_handler: (_packet: ClientboundPacket) => void = () => { }
relay_handler: (_sender: number, _message: RelayMessage) => void = () => { }
@@ -47,10 +48,15 @@ export class SignalingConnection {
async on_message(data: string) {
const packet: ClientboundPacket = JSON.parse(data) // TODO dont crash if invalid
this.control_handler(packet)
+ if (packet.init) this.my_id = packet.init.your_id;
if (packet.message) {
- const inner_json = await crypt_decrypt(this.key, packet.message.message)
- const inner: RelayMessage = JSON.parse(inner_json) // TODO make sure that protocol spec is met
- this.relay_handler(packet.message.sender, inner)
+ const plain_json = await crypt_decrypt(this.key, packet.message.message)
+ const plain: RelayMessageWrapper = JSON.parse(plain_json) // TODO make sure that protocol spec is met
+ if (plain.sender == packet.message.sender)
+ this.relay_handler(packet.message.sender, plain.inner)
+ else {
+ log({ scope: "crypto", warn: true }, `message dropped: sender inconsistent (${plain.sender} != ${packet.message.sender})`)
+ }
}
}
@@ -59,7 +65,8 @@ export class SignalingConnection {
}
async send_relay(data: RelayMessage, recipient?: number | null) {
recipient ??= undefined // null -> undefined
- const message = await crypto_encrypt(this.key, JSON.stringify(data))
+ const packet: RelayMessageWrapper = { inner: data, sender: this.my_id! }
+ const message = await crypto_encrypt(this.key, JSON.stringify(packet))
this.send_control({ relay: { recipient, message } })
}
}
diff --git a/common/packets.d.ts b/common/packets.d.ts
index 058c248..d7fe5b1 100644
--- a/common/packets.d.ts
+++ b/common/packets.d.ts
@@ -8,12 +8,17 @@ export interface ClientboundPacket {
init?: { your_id: number, version: string },
client_join?: { id: number },
client_leave?: { id: number },
- message?: { sender: number, message: string },
+ message?: { sender: number, message: string /* encrypted RelayMessageWrapper */ },
}
export interface ServerboundPacket {
ping?: null,
- relay?: { recipient?: number, message: string },
+ relay?: { recipient?: number, message: string /* encrypted RelayMessageWrapper */ },
+}
+
+export interface RelayMessageWrapper {
+ sender: number, // redundant, but ensures the server didnt cheat
+ inner: RelayMessage
}
export interface RelayMessage {
diff --git a/readme.md b/readme.md
index 9133c9c..4b34a00 100644
--- a/readme.md
+++ b/readme.md
@@ -75,11 +75,10 @@ Booleans can be either `1`, `true`, `yes` or their opposites.
- Have a security professional look at the code
- 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
-- Fix chat CSS (impossibleā„¢)
+ - Built-in storage for known keys
- Relay RTC when there are a lot of clients
- Mitigate security issues caused by `*_enabled` params
+- Images in chat
## Protocol