diff options
| author | metamuffin <metamuffin@disroot.org> | 2022-09-09 11:42:06 +0200 | 
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2022-09-09 11:42:06 +0200 | 
| commit | b6d93e0f322901dfc1fee23f3d396a68e61e1b29 (patch) | |
| tree | 01ba855cf41fb53f84cd6eb504c4f4ba0134fdfa /client-web | |
| parent | afed94bb4609bd796102c9184f13fa29c5f92a48 (diff) | |
| download | keks-meet-b6d93e0f322901dfc1fee23f3d396a68e61e1b29.tar keks-meet-b6d93e0f322901dfc1fee23f3d396a68e61e1b29.tar.bz2 keks-meet-b6d93e0f322901dfc1fee23f3d396a68e61e1b29.tar.zst | |
crypto stuff
Diffstat (limited to 'client-web')
| -rw-r--r-- | client-web/source/index.ts | 6 | ||||
| -rw-r--r-- | client-web/source/logger.ts | 14 | ||||
| -rw-r--r-- | client-web/source/protocol/crypto.ts | 80 | ||||
| -rw-r--r-- | client-web/source/protocol/mod.ts | 15 | ||||
| -rw-r--r-- | client-web/source/room.ts | 36 | 
5 files changed, 113 insertions, 38 deletions
| diff --git a/client-web/source/index.ts b/client-web/source/index.ts index 670cb46..21bf4b8 100644 --- a/client-web/source/index.ts +++ b/client-web/source/index.ts @@ -2,6 +2,7 @@  import { log } from "./logger.ts"  import { create_menu } from "./menu.ts"; +import { SignalingConnection } from "./protocol/mod.ts";  import { Room } from "./room.ts"  export const servers: RTCConfiguration = { @@ -17,11 +18,12 @@ export interface User {  window.onload = () => main() -export function main() { +export async function main() {      document.body.querySelector("p")?.remove()      log("*", "starting up")      const room_name = window.location.pathname.substring("/".length) -    const room = new Room(room_name) +    const conn = await (new SignalingConnection().connect(room_name)) +    const room = new Room(conn)      create_menu()      document.body.append(room.el)  } diff --git a/client-web/source/logger.ts b/client-web/source/logger.ts index e00b1d0..952355e 100644 --- a/client-web/source/logger.ts +++ b/client-web/source/logger.ts @@ -1,14 +1,16 @@  /// <reference lib="dom" />  const log_tag_color = { -    "*": "#FF4444", -    webrtc: "#FF44FF", -    media: "#FFFF44", -    ws: "#44FFFF", -    rnnoise: "#2222FF", -    usermodel: "#44FF44", +    "*": "#ff4a7c", +    crypto: "#c14aff", +    webrtc: "#ff4ade", +    ws: "#544aff", +    media: "#4af5ff", +    rnnoise: "#4aff7e", +    usermodel: "#a6ff4a",      error: "#FF0000",  } +  export type LogTag = keyof typeof log_tag_color  let logger_container: HTMLDivElement diff --git a/client-web/source/protocol/crypto.ts b/client-web/source/protocol/crypto.ts new file mode 100644 index 0000000..6cd2ba3 --- /dev/null +++ b/client-web/source/protocol/crypto.ts @@ -0,0 +1,80 @@ +import { log } from "../logger.ts"; + +//! I am not a crypto expert at all! Please read carefully and report any issues to me.  + +export async function crypto_seeded_key(seed: string): Promise<CryptoKey> { +    log("crypto", "importing seed…") +    const seed_key = await window.crypto.subtle.importKey( +        "raw", +        new TextEncoder().encode(seed), +        "PBKDF2", +        false, +        ["deriveKey"] +    ) +    //? 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…") +    const key = await window.crypto.subtle.deriveKey( +        { +            name: "PBKDF2", +            salt, +            iterations: 250000, +            hash: "SHA-256", +        }, +        seed_key, +        { name: "AES-GCM", length: 256 }, +        false, +        ["encrypt", "decrypt"] +    ) +    console.log(key); +    log("crypto", "ready") +    return key +} + +export async function crypto_encrypt(key: CryptoKey, data: string): Promise<string> { +    const iv = window.crypto.getRandomValues(new Uint8Array(12)); +    const ciphertext = new Uint8Array(await window.crypto.subtle.encrypt( +        { name: "AES-GCM", iv }, +        key, +        new TextEncoder().encode(data) +    )); +    const buf = new Uint8Array(iv.byteLength + ciphertext.byteLength); +    buf.set(iv, 0); +    buf.set(ciphertext, iv.byteLength); +    const b64 = buf_to_base64(buf); +    return b64; +} + +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 decryptedContent = await window.crypto.subtle.decrypt( +        { name: "AES-GCM", iv }, +        key, +        ciphertext +    ); +    const plain = new TextDecoder().decode(decryptedContent); +    return plain +} + +// const buf_to_base64 = (buf: Uint8Array) => btoa(String.fromCharCode.apply(null, buf)); +// const base64_to_buf = (b64: string) => Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); + +export function base64_to_buf(data: string): Uint8Array { +    const binary_string = globalThis.atob(data); +    const bytes = new Uint8Array(binary_string.length); +    for (let i = 0; i < binary_string.length; i++) { +        bytes[i] = binary_string.charCodeAt(i); +    } +    return bytes; +} + +export function buf_to_base64(bytes: Uint8Array): string { +    let binary = ''; +    for (let i = 0; i < bytes.byteLength; i++) { +        binary += String.fromCharCode(bytes[i]); +    } +    return globalThis.btoa(binary); +}
\ No newline at end of file diff --git a/client-web/source/protocol/mod.ts b/client-web/source/protocol/mod.ts index 76b1290..f86e96f 100644 --- a/client-web/source/protocol/mod.ts +++ b/client-web/source/protocol/mod.ts @@ -1,6 +1,17 @@ +import { crypto_seeded_key } from "./crypto.ts"  export class SignalingConnection { -    constructor(room: string) { -         +    room!: string +    websocket!: WebSocket +    signaling_id!: string +    key!: CryptoKey + +    constructor() { } +    async connect(room: string): Promise<SignalingConnection> { +        this.key = await crypto_seeded_key(room) +        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) +        await new Promise(r => this.websocket!.onopen = r) +        return this      }  } diff --git a/client-web/source/room.ts b/client-web/source/room.ts index 78454bb..6c9a7ba 100644 --- a/client-web/source/room.ts +++ b/client-web/source/room.ts @@ -5,43 +5,23 @@ import { RemoteUser } from "./remote_user.ts";  import { User } from "./user.ts";  import { LocalUser } from "./local_user.ts";  import { ServerboundPacket, ClientboundPacket } from "../../common/packets.d.ts"; -import { PREFS } from "./preferences.ts"; -import { ep } from "./helper.ts"; +import { SignalingConnection } from "./protocol/mod.ts";  export class Room {      el: HTMLElement -    name: string      users: Map<number, User> = new Map()      remote_users: Map<number, RemoteUser> = new Map()      local_user!: LocalUser      my_id!: number -    websocket: WebSocket -    constructor(name: string) { -        this.name = name +    constructor(public signaling: SignalingConnection) {          this.el = document.createElement("div")          this.el.classList.add("room") - -        const ws_url = new URL(`${window.location.protocol.endsWith("s:") ? "wss" : "ws"}://${window.location.host}/${encodeURIComponent(name)}/signaling`) -        ws_url.searchParams.set("username", PREFS.username) -        this.websocket = new WebSocket(ws_url) -        this.websocket.onclose = () => this.websocket_close() - -        // const connecting_text = ep("Upgrading to a websocket connection…") -        // this.el.append(connecting_text) - -        this.websocket.onopen = () => { -            // connecting_text.remove() -            this.websocket_open() -        } -        this.websocket.onmessage = (ev) => { -            this.websocket_message(JSON.parse(ev.data)) -        }      }      websocket_send(data: ServerboundPacket) {          log("ws", `-> ${data.relay?.recipient ?? "*"}`, data) -        this.websocket.send(JSON.stringify(data)) +        // this.websocket.send(JSON.stringify(data))      }      websocket_message(packet: ClientboundPacket) {          log("ws", `<- ${packet.message?.sender ?? "control packet"}: `, packet); @@ -51,7 +31,7 @@ export class Room {              log("*", `server: ${packet.init.version}`)          } else if (packet.client_join) {              const p = packet.client_join -            log("*", `${this.name} ${p.id} joined`); +            log("*", `${p.id} joined`);              if (p.id == this.my_id) {                  this.local_user = new LocalUser(this, p.id, p.name);              } else { @@ -63,7 +43,7 @@ export class Room {              }          } else if (packet.client_leave) {              const p = packet.client_leave; -            log("*", `${this.name} ${p.id} left`); +            log("*", `${p.id} left`);              this.remote_users.get(p.id)!.leave()              this.users.delete(p.id)              this.remote_users.delete(p.id) @@ -72,9 +52,9 @@ export class Room {              const p = packet.message;              const sender = this.users.get(p.sender)              if (sender instanceof RemoteUser) { -                if (p.message.ice_candidate) sender.add_ice_candidate(p.message.ice_candidate) -                if (p.message.offer) sender.on_offer(p.message.offer) -                if (p.message.answer) sender.on_answer(p.message.answer) +                // if (p.message.ice_candidate) sender.add_ice_candidate(p.message.ice_candidate) +                // if (p.message.offer) sender.on_offer(p.message.offer) +                // if (p.message.answer) sender.on_answer(p.message.answer)              } else {                  console.log("!", p, sender);              } | 
