From 4193629c2ce0ed4eb84ffb09995af504db95dd91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 17 Jul 2023 16:53:58 +0200 Subject: [PATCH] Add E2EE password prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/livekit/options.ts | 11 ---------- src/livekit/useLiveKit.ts | 39 +++++++++++++++++++++++++++++++---- src/room/GroupCallView.tsx | 12 +++++++---- src/room/InCallView.tsx | 9 ++++++-- src/room/LobbyView.module.css | 6 ++++++ src/room/LobbyView.tsx | 32 ++++++++++++++++++++++++---- 6 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/livekit/options.ts b/src/livekit/options.ts index e808149b..5115f213 100644 --- a/src/livekit/options.ts +++ b/src/livekit/options.ts @@ -6,9 +6,7 @@ import { TrackPublishDefaults, VideoPreset, VideoPresets, - ExternalE2EEKeyProvider, } from "livekit-client"; -import E2EEWorker from "livekit-client/e2ee-worker?worker"; const defaultLiveKitPublishOptions: TrackPublishDefaults = { audioPreset: AudioPresets.music, @@ -24,16 +22,7 @@ const defaultLiveKitPublishOptions: TrackPublishDefaults = { backupCodec: { codec: "vp8", encoding: VideoPresets.h720.encoding }, } as const; -const e2eeWorker = new E2EEWorker(); -const e2eeKeyProvider = new ExternalE2EEKeyProvider(); -e2eeKeyProvider.setKey("not secret password"); - export const defaultLiveKitOptions: RoomOptions = { - e2ee: { - keyProvider: e2eeKeyProvider, - worker: e2eeWorker, - }, - // automatically manage subscribed video quality adaptiveStream: true, diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index 6b862a66..aa8d1e42 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -1,6 +1,12 @@ -import { Room, RoomOptions } from "livekit-client"; +import { + E2EEOptions, + ExternalE2EEKeyProvider, + Room, + RoomOptions, +} from "livekit-client"; import { useLiveKitRoom } from "@livekit/components-react"; -import { useMemo } from "react"; +import { useEffect, useMemo } from "react"; +import E2EEWorker from "livekit-client/e2ee-worker?worker"; import { defaultLiveKitOptions } from "./options"; import { SFUConfig } from "./openIDSFU"; @@ -15,10 +21,32 @@ export type DeviceChoices = { enabled: boolean; }; +export type E2EEConfig = { + sharedKey: string; +}; + export function useLiveKit( userChoices: UserChoices, - sfuConfig?: SFUConfig + sfuConfig?: SFUConfig, + e2eeConfig?: E2EEConfig ): Room | undefined { + const e2eeOptions = useMemo(() => { + if (!e2eeConfig?.sharedKey) return undefined; + + return { + keyProvider: new ExternalE2EEKeyProvider(), + worker: new E2EEWorker(), + } as E2EEOptions; + }, [e2eeConfig]); + + useEffect(() => { + if (!e2eeConfig?.sharedKey || !e2eeOptions) return; + + (e2eeOptions.keyProvider as ExternalE2EEKeyProvider).setKey( + e2eeConfig?.sharedKey + ); + }, [e2eeOptions, e2eeConfig?.sharedKey]); + const roomOptions = useMemo((): RoomOptions => { const options = defaultLiveKitOptions; options.videoCaptureDefaults = { @@ -29,8 +57,11 @@ export function useLiveKit( ...options.audioCaptureDefaults, deviceId: userChoices.audio?.selectedId, }; + + options.e2ee = e2eeOptions; + return options; - }, [userChoices.video, userChoices.audio]); + }, [userChoices.video, userChoices.audio, e2eeOptions]); const roomWithoutProps = useMemo(() => new Room(roomOptions), [roomOptions]); diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 9afc2ec3..1ec959bc 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -32,7 +32,7 @@ import { CallEndedView } from "./CallEndedView"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { PosthogAnalytics } from "../analytics/PosthogAnalytics"; import { useProfile } from "../profile/useProfile"; -import { UserChoices } from "../livekit/useLiveKit"; +import { E2EEConfig, UserChoices } from "../livekit/useLiveKit"; import { findDeviceByName } from "../media-utils"; import { OpenIDLoader } from "../livekit/OpenIDLoader"; import { ActiveCall } from "./InCallView"; @@ -218,10 +218,12 @@ export function GroupCallView({ const [userChoices, setUserChoices] = useState( undefined ); + const [e2eeConfig, setE2EEConfig] = useState( + undefined + ); const livekitServiceURL = - groupCall.foci[0]?.livekitServiceUrl ?? - Config.get().livekit?.livekit_service_url; + groupCall.livekitServiceURL ?? Config.get().livekit?.livekit_service_url; if (!livekitServiceURL) { return ; } @@ -243,6 +245,7 @@ export function GroupCallView({ unencryptedEventsFromUsers={unencryptedEventsFromUsers} hideHeader={hideHeader} userChoices={userChoices} + e2eeConfig={e2eeConfig} otelGroupCallMembership={otelGroupCallMembership} /> @@ -283,8 +286,9 @@ export function GroupCallView({ return ( { + onEnter={(choices: UserChoices, e2eeConfig?: E2EEConfig) => { setUserChoices(choices); + setE2EEConfig(e2eeConfig); enter(); }} isEmbedded={isEmbedded} diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 0140061d..0889a883 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -77,7 +77,7 @@ import { SettingsModal } from "../settings/SettingsModal"; import { InviteModal } from "./InviteModal"; import { useRageshakeRequestModal } from "../settings/submit-rageshake"; import { RageshakeRequestModal } from "./RageshakeRequestModal"; -import { UserChoices, useLiveKit } from "../livekit/useLiveKit"; +import { E2EEConfig, UserChoices, useLiveKit } from "../livekit/useLiveKit"; import { useMediaDevicesSwitcher } from "../livekit/useMediaDevicesSwitcher"; import { useFullscreen } from "./useFullscreen"; import { useLayoutStates } from "../video-grid/Layout"; @@ -92,11 +92,16 @@ const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); export interface ActiveCallProps extends Omit { userChoices: UserChoices; + e2eeConfig?: E2EEConfig; } export function ActiveCall(props: ActiveCallProps) { const sfuConfig = useSFUConfig(); - const livekitRoom = useLiveKit(props.userChoices, sfuConfig); + const livekitRoom = useLiveKit( + props.userChoices, + sfuConfig, + props.e2eeConfig + ); if (!livekitRoom) { return null; diff --git a/src/room/LobbyView.module.css b/src/room/LobbyView.module.css index 55b7b5b0..3bb0162e 100644 --- a/src/room/LobbyView.module.css +++ b/src/room/LobbyView.module.css @@ -66,3 +66,9 @@ limitations under the License. .copyButton:last-child { margin-bottom: 0; } + +.passwordField { + width: 320px !important; + margin-bottom: 20px; + flex: 0; +} diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx index 0f07ae7d..e52835ae 100644 --- a/src/room/LobbyView.tsx +++ b/src/room/LobbyView.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { useRef, useEffect, useState } from "react"; +import { useRef, useEffect, useState, useCallback, ChangeEvent } from "react"; import { Trans, useTranslation } from "react-i18next"; import styles from "./LobbyView.module.css"; @@ -25,12 +25,13 @@ import { UserMenuContainer } from "../UserMenuContainer"; import { Body, Link } from "../typography/Typography"; import { useLocationNavigation } from "../useLocationNavigation"; import { MatrixInfo, VideoPreview } from "./VideoPreview"; -import { UserChoices } from "../livekit/useLiveKit"; +import { E2EEConfig, UserChoices } from "../livekit/useLiveKit"; +import { InputField } from "../input/Input"; interface Props { matrixInfo: MatrixInfo; - onEnter: (userChoices: UserChoices) => void; + onEnter: (userChoices: UserChoices, e2eeConfig?: E2EEConfig) => void; isEmbedded: boolean; hideHeader: boolean; } @@ -49,6 +50,17 @@ export function LobbyView(props: Props) { const [userChoices, setUserChoices] = useState( undefined ); + const [e2eeSharedKey, setE2EESharedKey] = useState( + undefined + ); + + const onE2EESharedKeyChanged = useCallback( + (event: ChangeEvent) => { + const value = event.target.value; + setE2EESharedKey(value === "" ? undefined : value); + }, + [setE2EESharedKey] + ); return (
@@ -68,12 +80,24 @@ export function LobbyView(props: Props) { matrixInfo={props.matrixInfo} onUserChoicesChanged={setUserChoices} /> +