Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(EXPERIMENTAL) Prototyping with to-device sender key distribution #2572

Draft
wants to merge 25 commits into
base: livekit
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
497b38b
Prototyping for to-device key distribution
hughns Aug 19, 2024
4c68122
Allow for null MatrixClient
hughns Sep 2, 2024
d060f85
Use branch of js-sdk
hughns Sep 3, 2024
9987a45
Prototyping for to-device key distribution
hughns Aug 19, 2024
a33b40e
Allow for null MatrixClient
hughns Sep 2, 2024
79d09e1
Use branch of js-sdk
hughns Sep 3, 2024
efcabe9
Merge branch 'hughns/do-device-key-distribution' of https://github.co…
hughns Sep 3, 2024
c5f50e0
Update yarn.lock
hughns Sep 3, 2024
38384c2
Bump js-sdk
hughns Sep 4, 2024
2afe179
Merge branch 'livekit' into hughns/do-device-key-distribution
hughns Sep 4, 2024
7e1fbdb
Bump js-sdk
hughns Sep 4, 2024
0a3dae0
Upgrade js-sdk to get forward secrecy
hughns Sep 4, 2024
cd2937c
Bump js-sdk
hughns Sep 4, 2024
d219f32
Bump js-sdk
hughns Sep 4, 2024
a5ac5b2
Merge branch 'livekit' into hughns/to-device-key-distribution
hughns Sep 6, 2024
20e3f67
Bump js-sdk
hughns Sep 6, 2024
e502cfb
Bump js-sdk
hughns Sep 9, 2024
102399a
Include the hostname of where EC is running in rageshakes (#2616)
hughns Sep 9, 2024
4cfdd15
Give user feedback if rageshake submission fails
hughns Sep 11, 2024
0c762fd
Rageshake logging improvements
hughns Sep 11, 2024
8e38d66
Merge branch 'livekit' into hughns/to-device-key-distribution
hughns Sep 11, 2024
55ea373
Intercept matrix_sdk logging via console
hughns Sep 11, 2024
c1161ed
Bump js-sdk to fix embedded mode
hughns Sep 11, 2024
cb89bd0
Merge branch 'livekit' into hughns/do-device-key-distribution
hughns Sep 11, 2024
8f73f81
Request widget permission to send encryption key to device messages
hughns Sep 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"livekit-client": "^2.0.2",
"lodash": "^4.17.21",
"loglevel": "^1.9.1",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#169e8f86139111574a3738f8557c6fa4b2a199db",
"matrix-js-sdk": "matrix-org/matrix-js-sdk#b0174eccdb0e33f5df5d7b590938daf8ff5c7f7a",
"matrix-widget-api": "^1.8.2",
"normalize.css": "^8.0.1",
"observable-hooks": "^4.2.3",
Expand Down
4 changes: 4 additions & 0 deletions src/analytics/PosthogAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
appVersion: string;
matrixBackend: "embedded" | "jssdk";
callBackend: "livekit" | "full-mesh";
cryptoVersion?: string;
}

interface PosthogSettings {
Expand Down Expand Up @@ -184,6 +185,9 @@
appVersion,
matrixBackend: widget ? "embedded" : "jssdk",
callBackend: "livekit",
cryptoVersion: widget
? undefined
: window.matrixclient?.getCrypto()?.getVersion(),

Check warning on line 190 in src/analytics/PosthogAnalytics.ts

View check run for this annotation

Codecov / codecov/patch

src/analytics/PosthogAnalytics.ts#L188-L190

Added lines #L188 - L190 were not covered by tests
};
}

Expand Down
48 changes: 47 additions & 1 deletion src/analytics/PosthogEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,40 @@

import { DisconnectReason } from "livekit-client";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixRTCSession } from "matrix-js-sdk/src/matrixrtc";

import {
IPosthogEvent,
PosthogAnalytics,
RegistrationType,
} from "./PosthogAnalytics";
import { E2eeType } from "../e2ee/e2eeType";

type EncryptionScheme = "none" | "shared" | "per_sender";

function mapE2eeType(type: E2eeType): EncryptionScheme {
switch (type) {
case E2eeType.NONE:
return "none";
case E2eeType.SHARED_KEY:
return "shared";

Check warning on line 26 in src/analytics/PosthogEvents.ts

View check run for this annotation

Codecov / codecov/patch

src/analytics/PosthogEvents.ts#L26

Added line #L26 was not covered by tests
case E2eeType.PER_PARTICIPANT:
return "per_sender";

Check warning on line 28 in src/analytics/PosthogEvents.ts

View check run for this annotation

Codecov / codecov/patch

src/analytics/PosthogEvents.ts#L28

Added line #L28 was not covered by tests
}
}
interface CallEnded extends IPosthogEvent {
eventName: "CallEnded";
callId: string;
callParticipantsOnLeave: number;
callParticipantsMax: number;
callDuration: number;
encryption: EncryptionScheme;
toDeviceEncryptionKeysSent: number;
toDeviceEncryptionKeysReceived: number;
toDeviceEncryptionKeysReceivedAverageAge: number;
roomEventEncryptionKeysSent: number;
roomEventEncryptionKeysReceived: number;
roomEventEncryptionKeysReceivedAverageAge: number;
}

export class CallEndedTracker {
Expand All @@ -42,6 +63,8 @@
public track(
callId: string,
callParticipantsNow: number,
e2eeType: E2eeType,
rtcSession: MatrixRTCSession,

Check warning on line 67 in src/analytics/PosthogEvents.ts

View check run for this annotation

Codecov / codecov/patch

src/analytics/PosthogEvents.ts#L66-L67

Added lines #L66 - L67 were not covered by tests
sendInstantly: boolean,
): void {
PosthogAnalytics.instance.trackEvent<CallEnded>(
Expand All @@ -51,6 +74,27 @@
callParticipantsMax: this.cache.maxParticipantsCount,
callParticipantsOnLeave: callParticipantsNow,
callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000,
encryption: mapE2eeType(e2eeType),
toDeviceEncryptionKeysSent:
rtcSession.statistics.counters.toDeviceEncryptionKeysSent,
toDeviceEncryptionKeysReceived:
rtcSession.statistics.counters.toDeviceEncryptionKeysReceived,
toDeviceEncryptionKeysReceivedAverageAge:
rtcSession.statistics.counters.toDeviceEncryptionKeysReceived > 0
? rtcSession.statistics.totals
.toDeviceEncryptionKeysReceivedTotalAge /
rtcSession.statistics.counters.toDeviceEncryptionKeysReceived
: 0,
roomEventEncryptionKeysSent:
rtcSession.statistics.counters.roomEventEncryptionKeysSent,
roomEventEncryptionKeysReceived:
rtcSession.statistics.counters.roomEventEncryptionKeysReceived,
roomEventEncryptionKeysReceivedAverageAge:
rtcSession.statistics.counters.roomEventEncryptionKeysReceived > 0
? rtcSession.statistics.totals
.roomEventEncryptionKeysReceivedTotalAge /
rtcSession.statistics.counters.roomEventEncryptionKeysReceived
: 0,

Check warning on line 97 in src/analytics/PosthogEvents.ts

View check run for this annotation

Codecov / codecov/patch

src/analytics/PosthogEvents.ts#L77-L97

Added lines #L77 - L97 were not covered by tests
},
{ send_instantly: sendInstantly },
);
Expand All @@ -60,13 +104,15 @@
interface CallStarted extends IPosthogEvent {
eventName: "CallStarted";
callId: string;
encryption: EncryptionScheme;
}

export class CallStartedTracker {
public track(callId: string): void {
public track(callId: string, e2eeType: E2eeType): void {
PosthogAnalytics.instance.trackEvent<CallStarted>({
eventName: "CallStarted",
callId: callId,
encryption: mapE2eeType(e2eeType),
});
}
}
Expand Down
45 changes: 32 additions & 13 deletions src/home/RegisteredView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { useHistory } from "react-router-dom";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next";
import { Heading } from "@vector-im/compound-web";
import { Dropdown, Heading } from "@vector-im/compound-web";

Check warning on line 12 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L12

Added line #L12 was not covered by tests
import { logger } from "matrix-js-sdk/src/logger";
import { Button } from "@vector-im/compound-web";

Expand All @@ -36,6 +36,17 @@
interface Props {
client: MatrixClient;
}
const encryptionOptions = {
shared: {
label: "Shared key",
e2eeType: E2eeType.SHARED_KEY,
},
sender: {
label: "Per-participant key",
e2eeType: E2eeType.PER_PARTICIPANT,
},
none: { label: "None", e2eeType: E2eeType.NONE },
};

Check warning on line 49 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L39-L49

Added lines #L39 - L49 were not covered by tests

export const RegisteredView: FC<Props> = ({ client }) => {
const [loading, setLoading] = useState(false);
Expand All @@ -50,6 +61,9 @@
[setJoinExistingCallModalOpen],
);

const [encryption, setEncryption] =
useState<keyof typeof encryptionOptions>("shared");

Check warning on line 65 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L64-L65

Added lines #L64 - L65 were not covered by tests

const onSubmit: FormEventHandler<HTMLFormElement> = useCallback(
(e: FormEvent) => {
e.preventDefault();
Expand All @@ -64,21 +78,13 @@
setError(undefined);
setLoading(true);

const createRoomResult = await createRoom(
const { roomId, encryptionSystem } = await createRoom(

Check warning on line 81 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L81

Added line #L81 was not covered by tests
client,
roomName,
E2eeType.SHARED_KEY,
encryptionOptions[encryption].e2eeType,

Check warning on line 84 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L84

Added line #L84 was not covered by tests
);
if (!createRoomResult.password)
throw new Error("Failed to create room with shared secret");

history.push(
getRelativeRoomUrl(
createRoomResult.roomId,
{ kind: E2eeType.SHARED_KEY, secret: createRoomResult.password },
roomName,
),
);
history.push(getRelativeRoomUrl(roomId, encryptionSystem, roomName));

Check warning on line 87 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L87

Added line #L87 was not covered by tests
}

submit().catch((error) => {
Expand All @@ -94,7 +100,7 @@
}
});
},
[client, history, setJoinExistingCallModalOpen],
[client, history, setJoinExistingCallModalOpen, encryption],

Check warning on line 103 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L103

Added line #L103 was not covered by tests
);

const recentRooms = useGroupCallRooms(client);
Expand Down Expand Up @@ -133,6 +139,19 @@
data-testid="home_callName"
/>

<Dropdown
label="Encryption"
defaultValue={encryption}
onValueChange={(x) =>
setEncryption(x as keyof typeof encryptionOptions)

Check warning on line 146 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L142-L146

Added lines #L142 - L146 were not covered by tests
}
values={Object.keys(encryptionOptions).map((value) => [
value,
encryptionOptions[value as keyof typeof encryptionOptions]
.label,
])}
placeholder=""
/>

Check warning on line 154 in src/home/RegisteredView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/RegisteredView.tsx#L148-L154

Added lines #L148 - L154 were not covered by tests
<Button
type="submit"
size="lg"
Expand Down
53 changes: 40 additions & 13 deletions src/home/UnauthenticatedView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import { useHistory } from "react-router-dom";
import { randomString } from "matrix-js-sdk/src/randomstring";
import { Trans, useTranslation } from "react-i18next";
import { Button, Heading } from "@vector-im/compound-web";
import { Button, Dropdown, Heading } from "@vector-im/compound-web";

Check warning on line 12 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L12

Added line #L12 was not covered by tests
import { logger } from "matrix-js-sdk/src/logger";

import { useClient } from "../ClientContext";
Expand All @@ -35,6 +35,18 @@
import { E2eeType } from "../e2ee/e2eeType";
import { useOptInAnalytics } from "../settings/settings";

const encryptionOptions = {
shared: {
label: "Shared key",
e2eeType: E2eeType.SHARED_KEY,
},
sender: {
label: "Per-participant key",
e2eeType: E2eeType.PER_PARTICIPANT,
},
none: { label: "None", e2eeType: E2eeType.NONE },
};

Check warning on line 48 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L38-L48

Added lines #L38 - L48 were not covered by tests

export const UnauthenticatedView: FC = () => {
const { setClient } = useClient();
const [loading, setLoading] = useState(false);
Expand All @@ -43,6 +55,9 @@
const { recaptchaKey, register } = useInteractiveRegistration();
const { execute, reset, recaptchaId } = useRecaptcha(recaptchaKey);

const [encryption, setEncryption] =
useState<keyof typeof encryptionOptions>("shared");

Check warning on line 59 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L58-L59

Added lines #L58 - L59 were not covered by tests

const [joinExistingCallModalOpen, setJoinExistingCallModalOpen] =
useState(false);
const onDismissJoinExistingCallModal = useCallback(
Expand Down Expand Up @@ -73,13 +88,16 @@
true,
);

let createRoomResult;
let roomId;
let encryptionSystem;

Check warning on line 92 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L91-L92

Added lines #L91 - L92 were not covered by tests
try {
createRoomResult = await createRoom(
const res = await createRoom(

Check warning on line 94 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L94

Added line #L94 was not covered by tests
client,
roomName,
E2eeType.SHARED_KEY,
encryptionOptions[encryption].e2eeType,

Check warning on line 97 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L97

Added line #L97 was not covered by tests
);
roomId = res.roomId;
encryptionSystem = res.encryptionSystem;

Check warning on line 100 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L99-L100

Added lines #L99 - L100 were not covered by tests
} catch (error) {
if (!setClient) {
throw error;
Expand All @@ -106,17 +124,11 @@
if (!setClient) {
throw new Error("setClient is undefined");
}
if (!createRoomResult.password)
throw new Error("Failed to create room with shared secret");
// if (!createRoomResult.password)
// throw new Error("Failed to create room with shared secret");

setClient({ client, session });
history.push(
getRelativeRoomUrl(
createRoomResult.roomId,
{ kind: E2eeType.SHARED_KEY, secret: createRoomResult.password },
roomName,
),
);
history.push(getRelativeRoomUrl(roomId, encryptionSystem, roomName));

Check warning on line 131 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L131

Added line #L131 was not covered by tests
}

submit().catch((error) => {
Expand All @@ -133,6 +145,7 @@
history,
setJoinExistingCallModalOpen,
setClient,
encryption,

Check warning on line 148 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L148

Added line #L148 was not covered by tests
],
);

Expand Down Expand Up @@ -195,6 +208,20 @@
<ErrorMessage error={error} />
</FieldRow>
)}
<Dropdown
label="Encryption"
defaultValue={encryption}
onValueChange={(x) =>
setEncryption(x as keyof typeof encryptionOptions)

Check warning on line 215 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L211-L215

Added lines #L211 - L215 were not covered by tests
}
values={Object.keys(encryptionOptions).map((value) => [
value,
encryptionOptions[value as keyof typeof encryptionOptions]
.label,
])}
placeholder=""
/>

Check warning on line 223 in src/home/UnauthenticatedView.tsx

View check run for this annotation

Codecov / codecov/patch

src/home/UnauthenticatedView.tsx#L217-L223

Added lines #L217 - L223 were not covered by tests

<Button
type="submit"
size="lg"
Expand Down
18 changes: 10 additions & 8 deletions src/room/GroupCallView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
const { displayName, avatarUrl } = useProfile(client);
const roomName = useRoomName(rtcSession.room);
const roomAvatar = useRoomAvatar(rtcSession.room);
const { perParticipantE2EE, returnToLobby } = useUrlParams();
const { returnToLobby } = useUrlParams();

Check warning on line 99 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L99

Added line #L99 was not covered by tests
const e2eeSystem = useRoomEncryptionSystem(rtcSession.room.roomId);

const matrixInfo = useMemo((): MatrixInfo => {
Expand Down Expand Up @@ -181,7 +181,7 @@
const onJoin = (ev: CustomEvent<IWidgetApiRequest>): void => {
(async (): Promise<void> => {
await defaultDeviceSetup(ev.detail.data as unknown as JoinCallData);
await enterRTCSession(rtcSession, perParticipantE2EE);
await enterRTCSession(rtcSession, e2eeSystem.kind);

Check warning on line 184 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L184

Added line #L184 was not covered by tests
widget!.api.transport.reply(ev.detail, {});
})().catch((e) => {
logger.error("Error joining RTC session", e);
Expand All @@ -195,12 +195,12 @@
// No lobby and no preload: we enter the rtc session right away
(async (): Promise<void> => {
await defaultDeviceSetup({ audioInput: null, videoInput: null });
await enterRTCSession(rtcSession, perParticipantE2EE);
await enterRTCSession(rtcSession, e2eeSystem.kind);

Check warning on line 198 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L198

Added line #L198 was not covered by tests
})().catch((e) => {
logger.error("Error joining RTC session", e);
});
}
}, [rtcSession, preload, skipLobby, perParticipantE2EE]);
}, [rtcSession, preload, skipLobby, e2eeSystem]);

Check warning on line 203 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L203

Added line #L203 was not covered by tests

const [left, setLeft] = useState(false);
const [leaveError, setLeaveError] = useState<Error | undefined>(undefined);
Expand All @@ -217,6 +217,8 @@
PosthogAnalytics.instance.eventCallEnded.track(
rtcSession.room.roomId,
rtcSession.memberships.length,
matrixInfo.e2eeSystem.kind,
rtcSession,

Check warning on line 221 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L220-L221

Added lines #L220 - L221 were not covered by tests
sendInstantly,
);

Expand All @@ -235,7 +237,7 @@
logger.error("Error leaving RTC session", e);
});
},
[rtcSession, isPasswordlessUser, confineToRoom, history],
[rtcSession, isPasswordlessUser, confineToRoom, history, matrixInfo],

Check warning on line 240 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L240

Added line #L240 was not covered by tests
);

useEffect(() => {
Expand All @@ -262,10 +264,10 @@
const onReconnect = useCallback(() => {
setLeft(false);
setLeaveError(undefined);
enterRTCSession(rtcSession, perParticipantE2EE).catch((e) => {
enterRTCSession(rtcSession, e2eeSystem.kind).catch((e) => {

Check warning on line 267 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L267

Added line #L267 was not covered by tests
logger.error("Error re-entering RTC session on reconnect", e);
});
}, [rtcSession, perParticipantE2EE]);
}, [rtcSession, e2eeSystem]);

Check warning on line 270 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L270

Added line #L270 was not covered by tests

const joinRule = useJoinRule(rtcSession.room);

Expand Down Expand Up @@ -318,7 +320,7 @@
client={client}
matrixInfo={matrixInfo}
muteStates={muteStates}
onEnter={() => void enterRTCSession(rtcSession, perParticipantE2EE)}
onEnter={() => void enterRTCSession(rtcSession, e2eeSystem.kind)}

Check warning on line 323 in src/room/GroupCallView.tsx

View check run for this annotation

Codecov / codecov/patch

src/room/GroupCallView.tsx#L323

Added line #L323 was not covered by tests
confineToRoom={confineToRoom}
hideHeader={hideHeader}
participantCount={participantCount}
Expand Down
Loading
Loading