diff --git a/src/e2ee/matrixKeyProvider.ts b/src/e2ee/matrixKeyProvider.ts index c5f6c8797..43b5abcaf 100644 --- a/src/e2ee/matrixKeyProvider.ts +++ b/src/e2ee/matrixKeyProvider.ts @@ -5,7 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial Please see LICENSE in the repository root for full details. */ -import { BaseKeyProvider, createKeyMaterialFromBuffer } from "livekit-client"; +import { + BaseKeyProvider, + createKeyMaterialFromBuffer, + KeyProviderEvent, +} from "livekit-client"; import { logger } from "matrix-js-sdk/lib/logger"; import { type MatrixRTCSession, @@ -16,7 +20,11 @@ export class MatrixKeyProvider extends BaseKeyProvider { private rtcSession?: MatrixRTCSession; public constructor() { - super({ ratchetWindowSize: 0, keyringSize: 256 }); + // ratchetWindowSize the amount of ratchets we try consecutively before + // throwing an error log. After those attempts we will still try more retry batches. + // So there can be more than 10 ratchets in total. + super({ ratchetWindowSize: 10, keyringSize: 256 }); + this.on(KeyProviderEvent.KeyRatcheted, this.onKeyRatcheted); } public setRTCSession(rtcSession: MatrixRTCSession): void { @@ -60,4 +68,12 @@ export class MatrixKeyProvider extends BaseKeyProvider { }, ); }; + + public onKeyRatcheted = (material: CryptoKey, keyIndex?: number): void => { + logger.debug( + `Key ratcheted event received for livekit room=${this.rtcSession?.room.roomId} keyIndex=${keyIndex}`, + `material:`, + material, + ); + }; } diff --git a/src/livekit/useLiveKit.ts b/src/livekit/useLiveKit.ts index da180ab81..833735f21 100644 --- a/src/livekit/useLiveKit.ts +++ b/src/livekit/useLiveKit.ts @@ -13,7 +13,7 @@ import { type RoomOptions, Track, } from "livekit-client"; -import { useEffect, useMemo, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef } from "react"; import E2EEWorker from "livekit-client/e2ee-worker?worker"; import { logger } from "matrix-js-sdk/lib/logger"; import { type MatrixRTCSession } from "matrix-js-sdk/lib/matrixrtc"; @@ -37,6 +37,7 @@ import { type EncryptionSystem } from "../e2ee/sharedKeyManagement"; interface UseLivekitResult { livekitRoom?: Room; connState: ECConnectionState; + doKeyRatchet: () => void; } export function useLiveKit( @@ -331,8 +332,16 @@ export function useLiveKit( } }, [room, devices, connectionState]); + const doKeyRatchet = useCallback(() => { + // not providing a key index will default to the current key + e2eeOptions?.keyProvider.ratchetKey( + room.localParticipant.identity, + undefined, + ); + }, [e2eeOptions?.keyProvider, room.localParticipant.identity]); return { connState: connectionState, livekitRoom: room, + doKeyRatchet, }; } diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index b434a1da1..3fb3ad3bb 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -114,7 +114,7 @@ export interface ActiveCallProps export const ActiveCall: FC = (props) => { const sfuConfig = useOpenIDSFU(props.client, props.rtcSession); - const { livekitRoom, connState } = useLiveKit( + const { livekitRoom, connState, doKeyRatchet } = useLiveKit( props.rtcSession, props.muteStates, sfuConfig, @@ -164,6 +164,7 @@ export const ActiveCall: FC = (props) => { vm={vm} livekitRoom={livekitRoom} connState={connState} + doKeyRatchet={doKeyRatchet} /> @@ -184,6 +185,7 @@ export interface InCallViewProps { otelGroupCallMembership?: OTelGroupCallMembership; connState: ECConnectionState; onShareClick: (() => void) | null; + doKeyRatchet?: () => void; } export const InCallView: FC = ({ @@ -198,6 +200,7 @@ export const InCallView: FC = ({ hideHeader, connState, onShareClick, + doKeyRatchet, }) => { const { supportsReactions, sendReaction, toggleRaisedHand } = useReactionsSender(); @@ -710,6 +713,7 @@ export const InCallView: FC = ({ tab={settingsTab} onTabChange={setSettingsTab} livekitRoom={livekitRoom} + doKeyRatchet={doKeyRatchet} /> )} diff --git a/src/settings/DeveloperSettingsTab.tsx b/src/settings/DeveloperSettingsTab.tsx index 67de0e0d1..2993ec0dd 100644 --- a/src/settings/DeveloperSettingsTab.tsx +++ b/src/settings/DeveloperSettingsTab.tsx @@ -7,6 +7,7 @@ Please see LICENSE in the repository root for full details. import { type ChangeEvent, type FC, useCallback, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { Button } from "@vector-im/compound-web"; import { FieldRow, InputField } from "../input/Input"; import { @@ -25,9 +26,14 @@ import { useUrlParams } from "../UrlParams"; interface Props { client: MatrixClient; livekitRoom?: LivekitRoom; + doKeyRatchet?: () => void; } -export const DeveloperSettingsTab: FC = ({ client, livekitRoom }) => { +export const DeveloperSettingsTab: FC = ({ + client, + livekitRoom, + doKeyRatchet, +}) => { const { t } = useTranslation(); const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting); const [debugTileLayout, setDebugTileLayout] = useSetting( @@ -88,7 +94,13 @@ export const DeveloperSettingsTab: FC = ({ client, livekitRoom }) => { {t("developer_mode.device_id", { id: client.getDeviceId() || "unknown", })} -

+

{" "} + + {" "} + void; } export const defaultSettingsTab: SettingsTab = "audio"; @@ -61,6 +62,7 @@ export const SettingsModal: FC = ({ client, roomId, livekitRoom, + doKeyRatchet, }) => { const { t } = useTranslation(); @@ -144,7 +146,13 @@ export const SettingsModal: FC = ({ const developerTab: Tab = { key: "developer", name: t("settings.developer_tab_title"), - content: , + content: ( + + ), }; const tabs = [audioTab, videoTab];