diff --git a/Client/tauri-client/src/lib/e2eeCrypto.ts b/Client/tauri-client/src/lib/e2eeCrypto.ts index c694e821..a09ff3cc 100644 --- a/Client/tauri-client/src/lib/e2eeCrypto.ts +++ b/Client/tauri-client/src/lib/e2eeCrypto.ts @@ -14,7 +14,9 @@ * - When a participant leaves, the key holder rotates the room key. */ -import { log } from "@lib/logger"; +import { createLogger } from "@lib/logger"; + +const log = createLogger("e2eeCrypto"); // ── WebCrypto availability check ─────────────────────────────────────────── if (typeof crypto === "undefined" || !crypto.subtle) { @@ -27,9 +29,11 @@ const ECDH_CURVE = "P-256"; // UTF-8 bytes of "owncord-voice-e2ee-v1" const HKDF_SALT = new Uint8Array([ 111, 119, 110, 99, 111, 114, 100, 45, 118, 111, 105, 99, 101, 45, 101, 50, 101, 101, 45, 118, 49, -]); +]) as Uint8Array; // UTF-8 bytes of "room-key-wrap" -const HKDF_INFO = new Uint8Array([114, 111, 111, 109, 45, 107, 101, 121, 45, 119, 114, 97, 112]); +const HKDF_INFO = new Uint8Array([ + 114, 111, 111, 109, 45, 107, 101, 121, 45, 119, 114, 97, 112, +]) as Uint8Array; const ROOM_KEY_BYTES = 32; // 256-bit AES key for LiveKit SFrame // ── Key pair generation ───────────────────────────────────────────────────── @@ -100,7 +104,11 @@ export async function wrapRoomKey( const wrapKey = await deriveWrappingKey(myPrivateKey, peerPublicKey); const iv = crypto.getRandomValues(new Uint8Array(12)); // 96-bit GCM nonce - const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, wrapKey, roomKey); + const ciphertext = await crypto.subtle.encrypt( + { name: "AES-GCM", iv }, + wrapKey, + roomKey as Uint8Array, + ); return { encryptedKey: uint8ToBase64(new Uint8Array(ciphertext)), @@ -168,14 +176,14 @@ function uint8ToBase64(bytes: Uint8Array): string { return btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join("")); } -function base64ToUint8(base64: string): Uint8Array { +function base64ToUint8(base64: string): Uint8Array { let binary: string; try { binary = atob(base64); } catch { throw new Error("E2EE: invalid base64 input"); } - const bytes = new Uint8Array(binary.length); + const bytes = new Uint8Array(binary.length) as Uint8Array; for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } diff --git a/Client/tauri-client/src/lib/types.ts b/Client/tauri-client/src/lib/types.ts index a7795643..d2db6df1 100644 --- a/Client/tauri-client/src/lib/types.ts +++ b/Client/tauri-client/src/lib/types.ts @@ -280,6 +280,7 @@ export interface VoiceTokenPayload { readonly token: string; readonly url: string; readonly direct_url?: string; + readonly is_key_holder?: boolean; } // ── Voice E2EE (client-side ECDH key exchange) ───────────────────────────── diff --git a/Client/tauri-client/tests/unit/e2eeCrypto.test.ts b/Client/tauri-client/tests/unit/e2eeCrypto.test.ts index dc24e279..7df433e7 100644 --- a/Client/tauri-client/tests/unit/e2eeCrypto.test.ts +++ b/Client/tauri-client/tests/unit/e2eeCrypto.test.ts @@ -54,7 +54,7 @@ describe("e2eeCrypto", () => { // Decode, flip the first byte, re-encode const bytes = Uint8Array.from(atob(encryptedKey), (c) => c.charCodeAt(0)); - bytes[0] ^= 0xff; + bytes[0] = bytes[0]! ^ 0xff; const tampered = btoa(String.fromCharCode(...bytes)); await expect(unwrapRoomKey(bob.privateKey, alice.publicKey, tampered, iv)).rejects.toThrow();