Skip to content

Commit aa0d3bd

Browse files
authored
Handle other members having no e2e keys (#2383)
Fetch the device info once at the start of the cal and cache it rather than fetching every time, and throw if we're supposed to be using e2e but the other end has no e2e keys.
1 parent 942a28d commit aa0d3bd

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

src/webrtc/call.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { CallFeed } from './callFeed';
4747
import { MatrixClient } from "../client";
4848
import { ISendEventResponse } from "../@types/requests";
4949
import { EventEmitterEvents, TypedEventEmitter } from "../models/typed-event-emitter";
50+
import { DeviceInfo } from '../crypto/deviceinfo';
5051

5152
// events: hangup, error(err), replaced(call), state(state, oldState)
5253

@@ -343,6 +344,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
343344
private callLength = 0;
344345

345346
private opponentDeviceId: string;
347+
private opponentDeviceInfo: DeviceInfo;
346348
private opponentSessionId: string;
347349
public groupCallId: string;
348350

@@ -508,6 +510,17 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
508510
return this.feeds.filter((feed) => !feed.isLocal());
509511
}
510512

513+
private async initOpponentCrypto() {
514+
if (!this.opponentDeviceId) return;
515+
516+
const userId = this.invitee || this.getOpponentMember().userId;
517+
const deviceInfoMap = await this.client.crypto.deviceList.downloadKeys([userId], false);
518+
this.opponentDeviceInfo = deviceInfoMap[userId][this.opponentDeviceId];
519+
if (this.opponentDeviceInfo === undefined) {
520+
throw new Error(`No keys found for opponent device ${this.opponentDeviceId}!`);
521+
}
522+
}
523+
511524
/**
512525
* Generates and returns localSDPStreamMetadata
513526
* @returns {SDPStreamMetadata} localSDPStreamMetadata
@@ -792,6 +805,7 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
792805
// handler will start giving us more call events (eg. candidates) so if
793806
// we haven't set the party ID, we'll ignore them.
794807
this.chooseOpponent(event);
808+
await this.initOpponentCrypto();
795809
try {
796810
await this.peerConn.setRemoteDescription(invite.offer);
797811
await this.addBufferedIceCandidates();
@@ -2052,9 +2066,10 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
20522066
},
20532067
};
20542068
const userId = this.invitee || this.getOpponentMember().userId;
2055-
const deviceInfoMap = await this.client.crypto.deviceList.downloadKeys([userId], false);
2056-
const deviceInfo = deviceInfoMap[userId][this.opponentDeviceId];
2057-
return this.client.crypto.encryptAndSendToDevices([{ userId, deviceInfo }], payload);
2069+
return this.client.crypto.encryptAndSendToDevices([{
2070+
userId,
2071+
deviceInfo: this.opponentDeviceInfo,
2072+
}], payload);
20582073
} else {
20592074
this.emit(CallEvent.SendVoipEvent, {
20602075
type: "sendEvent",
@@ -2351,6 +2366,8 @@ export class MatrixCall extends TypedEventEmitter<CallEvent, CallEventHandlerMap
23512366
this.checkForErrorListener();
23522367
this.direction = CallDirection.Outbound;
23532368

2369+
await this.initOpponentCrypto();
2370+
23542371
// XXX Find a better way to do this
23552372
this.client.callEventHandler.calls.set(this.callId, this);
23562373

src/webrtc/groupCall.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export type GroupCallEventHandlerMap = {
5858

5959
export enum GroupCallErrorCode {
6060
NoUserMedia = "no_user_media",
61-
UnknownDevice = "unknown_device"
61+
UnknownDevice = "unknown_device",
62+
PlaceCallFailed = "place_call_failed"
6263
}
6364

6465
export class GroupCallError extends Error {
@@ -653,7 +654,7 @@ export class GroupCall extends TypedEventEmitter<GroupCallEvent, GroupCallEventH
653654
return this.client.sendStateEvent(this.room.roomId, EventType.GroupCallMemberPrefix, content, localUserId);
654655
}
655656

656-
public onMemberStateChanged = (event: MatrixEvent) => {
657+
public onMemberStateChanged = async (event: MatrixEvent) => {
657658
// The member events may be received for another room, which we will ignore.
658659
if (event.getRoomId() !== this.room.roomId) {
659660
return;
@@ -751,8 +752,23 @@ export class GroupCall extends TypedEventEmitter<GroupCallEvent, GroupCallEventH
751752
const requestScreenshareFeed = opponentDevice.feeds.some(
752753
(feed) => feed.purpose === SDPStreamMetadataPurpose.Screenshare);
753754

754-
// Safari can't send a MediaStream to multiple sources, so clone it
755-
newCall.placeCallWithCallFeeds(this.getLocalFeeds().map(feed => feed.clone()), requestScreenshareFeed);
755+
try {
756+
// Safari can't send a MediaStream to multiple sources, so clone it
757+
await newCall.placeCallWithCallFeeds(
758+
this.getLocalFeeds().map(feed => feed.clone()),
759+
requestScreenshareFeed,
760+
);
761+
} catch (e) {
762+
logger.warn(`Failed to place call to ${member.userId}!`, e);
763+
this.emit(
764+
GroupCallEvent.Error,
765+
new GroupCallError(
766+
GroupCallErrorCode.PlaceCallFailed,
767+
`Failed to place call to ${member.userId}.`,
768+
),
769+
);
770+
return;
771+
}
756772

757773
if (this.dataChannelsEnabled) {
758774
newCall.createDataChannel("datachannel", this.dataChannelOptions);

0 commit comments

Comments
 (0)