Skip to content

Commit c8e6279

Browse files
committed
update IKeyTransport interface to event emitter.
1 parent af828c6 commit c8e6279

File tree

5 files changed

+90
-104
lines changed

5 files changed

+90
-104
lines changed

src/matrixrtc/EncryptionManager.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { secureRandomBase64Url } from "../randomstring.ts";
55
import { decodeBase64, encodeUnpaddedBase64 } from "../base64.ts";
66
import { safeGetRetryAfterMs } from "../http-api/errors.ts";
77
import { type CallMembership } from "./CallMembership.ts";
8-
import { type IKeyTransport } from "./IKeyTransport.ts";
8+
import { KeyTransportEventListener, type IKeyTransport } from "./IKeyTransport.ts";
99

1010
const logger = rootLogger.getChild("MatrixRTCSession");
1111

@@ -43,15 +43,6 @@ export interface IEncryptionManager {
4343

4444
onMembershipsUpdate(oldMemberships: CallMembership[]): void;
4545

46-
/**
47-
* Process `m.call.encryption_keys` events to track the encryption keys for call participants.
48-
* This should be called each time the relevant event is received from a room timeline.
49-
* If the event is malformed then it will be logged and ignored.
50-
*
51-
* @param event the event to process
52-
*/
53-
onCallEncryptionEventReceived(event: MatrixEvent): void;
54-
5546
getEncryptionKeys(): Map<string, Array<{ key: Uint8Array; timestamp: number }>>;
5647

5748
statistics: Statistics;
@@ -303,14 +294,8 @@ export class EncryptionManager implements IEncryptionManager {
303294
}
304295
};
305296

306-
public onCallEncryptionEventReceived = (event: MatrixEvent): void => {
307-
this.transport.receiveRoomEvent(
308-
event,
309-
this.statistics,
310-
(userId, deviceId, encryptionKeyIndex, encryptionKeyString, timestamp) => {
311-
this.setEncryptionKey(userId, deviceId, encryptionKeyIndex, encryptionKeyString, timestamp);
312-
},
313-
);
297+
public onNewKeyReceived: KeyTransportEventListener = (userId, deviceId, keyBase64Encoded, index, timestamp) => {
298+
this.setEncryptionKey(userId, deviceId, index, keyBase64Encoded, timestamp);
314299
};
315300

316301
private storeLastMembershipFingerprints(): void {

src/matrixrtc/IKeyTransport.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,24 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import { type MatrixEvent } from "../models/event.ts";
18-
import { type Statistics } from "./EncryptionManager.ts";
1917
import { type CallMembership } from "./CallMembership.ts";
2018

19+
export enum KeyTransportEvents {
20+
ReceivedKeys = "received_keys",
21+
}
22+
23+
export type KeyTransportEventListener = (
24+
userId: string,
25+
deviceId: string,
26+
keyBase64Encoded: string,
27+
index: number,
28+
timestamp: number,
29+
) => void;
30+
31+
export type KeyTransportEventsHandlerMap = {
32+
[KeyTransportEvents.ReceivedKeys]: KeyTransportEventListener;
33+
};
34+
2135
/**
2236
* Generic interface for the transport used to share room keys.
2337
* Keys can be shared using different transports, e.g. to-device messages or room messages.
@@ -31,21 +45,9 @@ export interface IKeyTransport {
3145
*/
3246
sendKey(keyBase64Encoded: string, index: number, members: CallMembership[]): Promise<void>;
3347

34-
/**
35-
* Takes an incoming event from the transport and extracts the key information.
36-
* @param event
37-
* @param statistics
38-
* @param callback
39-
*/
40-
receiveRoomEvent(
41-
event: MatrixEvent,
42-
statistics: Statistics,
43-
callback: (
44-
userId: string,
45-
deviceId: string,
46-
encryptionKeyIndex: number,
47-
encryptionKeyString: string,
48-
timestamp: number,
49-
) => void,
50-
): void;
48+
on(event: KeyTransportEvents.ReceivedKeys, listener: KeyTransportEventListener): this;
49+
off(event: KeyTransportEvents.ReceivedKeys, listener: KeyTransportEventListener): this;
50+
51+
start(): void;
52+
stop(): void;
5153
}

src/matrixrtc/MatrixRTCSession.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import { CallMembership } from "./CallMembership.ts";
2424
import { RoomStateEvent } from "../models/room-state.ts";
2525
import { type Focus } from "./focus.ts";
2626
import { KnownMembership } from "../@types/membership.ts";
27-
import { type MatrixEvent } from "../models/event.ts";
2827
import { MembershipManager } from "./NewMembershipManager.ts";
2928
import { EncryptionManager, type IEncryptionManager, type Statistics } from "./EncryptionManager.ts";
3029
import { LegacyMembershipManager } from "./LegacyMembershipManager.ts";
@@ -298,7 +297,10 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
298297
| "sendEvent"
299298
| "cancelPendingEvent"
300299
>,
301-
private roomSubset: Pick<Room, "getLiveTimeline" | "roomId" | "getVersion" | "hasMembershipState">,
300+
private roomSubset: Pick<
301+
Room,
302+
"getLiveTimeline" | "roomId" | "getVersion" | "hasMembershipState" | "on" | "off"
303+
>,
302304
public memberships: CallMembership[],
303305
) {
304306
super();
@@ -308,7 +310,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
308310
roomState?.on(RoomStateEvent.Members, this.onRoomMemberUpdate);
309311
this.setExpiryTimer();
310312

311-
const transport = new RoomKeyTransport(this.roomSubset.roomId, this.client);
313+
const transport = new RoomKeyTransport(this.roomSubset, this.client, this.statistics);
312314
this.encryptionManager = new EncryptionManager(
313315
this.client.getUserId()!,
314316
this.client.getDeviceId()!,
@@ -488,17 +490,6 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
488490
}
489491
}
490492

491-
/**
492-
* Process `m.call.encryption_keys` events to track the encryption keys for call participants.
493-
* This should be called each time the relevant event is received from a room timeline.
494-
* If the event is malformed then it will be logged and ignored.
495-
*
496-
* @param event the event to process
497-
*/
498-
public onCallEncryption = (event: MatrixEvent): void => {
499-
this.encryptionManager.onCallEncryptionEventReceived(event);
500-
};
501-
502493
/**
503494
* @deprecated use onRoomMemberUpdate or onRTCSessionMemberUpdate instead. this should be called when any membership in the call is updated
504495
* the old name might have implied to only need to call this when your own membership changes.

src/matrixrtc/MatrixRTCSessionManager.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
6565
}
6666

6767
this.client.on(ClientEvent.Room, this.onRoom);
68-
this.client.on(RoomEvent.Timeline, this.onTimeline);
6968
this.client.on(RoomStateEvent.Events, this.onRoomState);
7069
}
7170

@@ -76,7 +75,6 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
7675
this.roomSessions.clear();
7776

7877
this.client.off(ClientEvent.Room, this.onRoom);
79-
this.client.off(RoomEvent.Timeline, this.onTimeline);
8078
this.client.off(RoomStateEvent.Events, this.onRoomState);
8179
}
8280

@@ -100,37 +98,6 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
10098
return this.roomSessions.get(room.roomId)!;
10199
}
102100

103-
private async consumeCallEncryptionEvent(event: MatrixEvent, isRetry = false): Promise<void> {
104-
await this.client.decryptEventIfNeeded(event);
105-
if (event.isDecryptionFailure()) {
106-
if (!isRetry) {
107-
logger.warn(
108-
`Decryption failed for event ${event.getId()}: ${event.decryptionFailureReason} will retry once only`,
109-
);
110-
// retry after 1 second. After this we give up.
111-
setTimeout(() => void this.consumeCallEncryptionEvent(event, true), 1000);
112-
} else {
113-
logger.warn(`Decryption failed for event ${event.getId()}: ${event.decryptionFailureReason}`);
114-
}
115-
return;
116-
} else if (isRetry) {
117-
logger.info(`Decryption succeeded for event ${event.getId()} after retry`);
118-
}
119-
120-
if (event.getType() !== EventType.CallEncryptionKeysPrefix) return Promise.resolve();
121-
122-
const room = this.client.getRoom(event.getRoomId());
123-
if (!room) {
124-
logger.error(`Got room state event for unknown room ${event.getRoomId()}!`);
125-
return Promise.resolve();
126-
}
127-
128-
this.getRoomSession(room).onCallEncryption(event);
129-
}
130-
private onTimeline = (event: MatrixEvent): void => {
131-
void this.consumeCallEncryptionEvent(event);
132-
};
133-
134101
private onRoom = (room: Room): void => {
135102
this.refreshRoom(room);
136103
};
@@ -152,7 +119,8 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
152119
const sess = this.getRoomSession(room);
153120

154121
const wasActiveAndKnown = sess.memberships.length > 0 && !isNewSession;
155-
122+
// TODO remove this and make the session subscribe to this manually.
123+
// move away from magically called methods. Prefer explicit subscriptions.
156124
sess.onRTCSessionMemberUpdate();
157125

158126
const nowActive = sess.memberships.length > 0;

src/matrixrtc/RoomKeyTransport.ts

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,64 @@ import type { EncryptionKeysEventContent } from "./types.ts";
1919
import { EventType } from "../@types/event.ts";
2020
import { type MatrixError } from "../http-api/errors.ts";
2121
import { logger, type Logger } from "../logger.ts";
22-
import { type IKeyTransport } from "./IKeyTransport.ts";
22+
import { KeyTransportEvents, KeyTransportEventsHandlerMap, type IKeyTransport } from "./IKeyTransport.ts";
2323
import { type MatrixEvent } from "../models/event.ts";
2424
import { type Statistics } from "./EncryptionManager.ts";
2525
import { type CallMembership } from "./CallMembership.ts";
26+
import { Room, RoomEvent, TypedEventEmitter } from "../matrix.ts";
2627

27-
export class RoomKeyTransport implements IKeyTransport {
28+
export class RoomKeyTransport
29+
extends TypedEventEmitter<KeyTransportEvents, KeyTransportEventsHandlerMap>
30+
implements IKeyTransport
31+
{
2832
private readonly prefixedLogger: Logger;
2933

3034
public constructor(
31-
private roomId: string,
35+
private room: Pick<Room, "on" | "off" | "roomId">,
3236
private client: Pick<MatrixClient, "sendEvent" | "getDeviceId" | "getUserId" | "cancelPendingEvent">,
37+
private statistics: Statistics,
3338
) {
34-
this.prefixedLogger = logger.getChild(`[RTC: ${roomId} RoomKeyTransport]`);
39+
super();
40+
this.prefixedLogger = logger.getChild(`[RTC: ${room.roomId} RoomKeyTransport]`);
41+
}
42+
public start(): void {
43+
this.room.on(RoomEvent.Timeline, this.onTimeline);
44+
}
45+
public stop(): void {
46+
this.room.off(RoomEvent.Timeline, this.onTimeline);
3547
}
3648

49+
private async consumeCallEncryptionEvent(event: MatrixEvent, isRetry = false): Promise<void> {
50+
// we should not need this
51+
// await this.client.decryptEventIfNeeded(event);
52+
if (event.isDecryptionFailure()) {
53+
if (!isRetry) {
54+
logger.warn(
55+
`Decryption failed for event ${event.getId()}: ${event.decryptionFailureReason} will retry once only`,
56+
);
57+
// retry after 1 second. After this we give up.
58+
setTimeout(() => void this.consumeCallEncryptionEvent(event, true), 1000);
59+
} else {
60+
logger.warn(`Decryption failed for event ${event.getId()}: ${event.decryptionFailureReason}`);
61+
}
62+
return;
63+
} else if (isRetry) {
64+
logger.info(`Decryption succeeded for event ${event.getId()} after retry`);
65+
}
66+
67+
if (event.getType() !== EventType.CallEncryptionKeysPrefix) return Promise.resolve();
68+
69+
if (!this.room) {
70+
logger.error(`Got room state event for unknown room ${event.getRoomId()}!`);
71+
return Promise.resolve();
72+
}
73+
74+
this.onEncryptionEvent(event);
75+
}
76+
private onTimeline = (event: MatrixEvent): void => {
77+
void this.consumeCallEncryptionEvent(event);
78+
};
79+
3780
/** implements {@link IKeyTransport#sendKey} */
3881
public async sendKey(keyBase64Encoded: string, index: number, members: CallMembership[]): Promise<void> {
3982
// members not used in room transports as the keys are sent to all room members
@@ -50,7 +93,7 @@ export class RoomKeyTransport implements IKeyTransport {
5093
};
5194

5295
try {
53-
await this.client.sendEvent(this.roomId, EventType.CallEncryptionKeysPrefix, content);
96+
await this.client.sendEvent(this.room.roomId, EventType.CallEncryptionKeysPrefix, content);
5497
} catch (error) {
5598
this.prefixedLogger.error("Failed to send call encryption keys", error);
5699
const matrixError = error as MatrixError;
@@ -63,17 +106,7 @@ export class RoomKeyTransport implements IKeyTransport {
63106
}
64107
}
65108

66-
public receiveRoomEvent(
67-
event: MatrixEvent,
68-
statistics: Statistics,
69-
callback: (
70-
userId: string,
71-
deviceId: string,
72-
encryptionKeyIndex: number,
73-
encryptionKeyString: string,
74-
timestamp: number,
75-
) => void,
76-
): void {
109+
public onEncryptionEvent(event: MatrixEvent): void {
77110
const userId = event.getSender();
78111
const content = event.getContent<EncryptionKeysEventContent>();
79112

@@ -106,9 +139,9 @@ export class RoomKeyTransport implements IKeyTransport {
106139
return;
107140
}
108141

109-
statistics.counters.roomEventEncryptionKeysReceived += 1;
142+
this.statistics.counters.roomEventEncryptionKeysReceived += 1;
110143
const age = Date.now() - (typeof content.sent_ts === "number" ? content.sent_ts : event.getTs());
111-
statistics.totals.roomEventEncryptionKeysReceivedTotalAge += age;
144+
this.statistics.totals.roomEventEncryptionKeysReceivedTotalAge += age;
112145

113146
for (const key of content.keys) {
114147
if (!key) {
@@ -137,7 +170,14 @@ export class RoomKeyTransport implements IKeyTransport {
137170
logger.debug(
138171
`Embedded-E2EE-LOG onCallEncryption userId=${userId}:${deviceId} encryptionKeyIndex=${encryptionKeyIndex} age=${age}ms`,
139172
);
140-
callback(userId, deviceId, encryptionKeyIndex, encryptionKey, event.getTs());
173+
this.emit(
174+
KeyTransportEvents.ReceivedKeys,
175+
userId,
176+
deviceId,
177+
encryptionKey,
178+
encryptionKeyIndex,
179+
event.getTs(),
180+
);
141181
}
142182
}
143183
}

0 commit comments

Comments
 (0)