Skip to content

Commit 3781b6e

Browse files
authored
Re-send MatrixRTC media encryption keys for a new joiner even if a rotation is in progress (#4561)
1 parent 8fc77c5 commit 3781b6e

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

spec/unit/matrixrtc/MatrixRTCSession.spec.ts

+58-1
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,64 @@ describe("MatrixRTCSession", () => {
864864
expect(client.cancelPendingEvent).toHaveBeenCalledWith(eventSentinel);
865865
});
866866

867-
it("Re-sends key if a new member joins", async () => {
867+
it("re-sends key if a new member joins even if a key rotation is in progress", async () => {
868+
jest.useFakeTimers();
869+
try {
870+
// session with two members
871+
const member2 = Object.assign({}, membershipTemplate, {
872+
device_id: "BBBBBBB",
873+
});
874+
const mockRoom = makeMockRoom([membershipTemplate, member2]);
875+
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);
876+
877+
// joining will trigger an initial key send
878+
const keysSentPromise1 = new Promise<EncryptionKeysEventContent>((resolve) => {
879+
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
880+
});
881+
sess.joinRoomSession([mockFocus], mockFocus, {
882+
manageMediaKeys: true,
883+
updateEncryptionKeyThrottle: 1000,
884+
makeKeyDelay: 3000,
885+
});
886+
await keysSentPromise1;
887+
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);
888+
889+
// member2 leaves triggering key rotation
890+
mockRoom.getLiveTimeline().getState = jest
891+
.fn()
892+
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId));
893+
sess.onMembershipUpdate();
894+
895+
// member2 re-joins which should trigger an immediate re-send
896+
const keysSentPromise2 = new Promise<EncryptionKeysEventContent>((resolve) => {
897+
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
898+
});
899+
mockRoom.getLiveTimeline().getState = jest
900+
.fn()
901+
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
902+
sess.onMembershipUpdate();
903+
// but, that immediate resend is throttled so we need to wait a bit
904+
jest.advanceTimersByTime(1000);
905+
const { keys } = await keysSentPromise2;
906+
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
907+
// key index should still be the original: 0
908+
expect(keys[0].index).toEqual(0);
909+
910+
// check that the key rotation actually happens
911+
const keysSentPromise3 = new Promise<EncryptionKeysEventContent>((resolve) => {
912+
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
913+
});
914+
jest.advanceTimersByTime(2000);
915+
const { keys: rotatedKeys } = await keysSentPromise3;
916+
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(3);
917+
// key index should now be the rotated one: 1
918+
expect(rotatedKeys[0].index).toEqual(1);
919+
} finally {
920+
jest.useRealTimers();
921+
}
922+
});
923+
924+
it("re-sends key if a new member joins", async () => {
868925
jest.useFakeTimers();
869926
try {
870927
const mockRoom = makeMockRoom([membershipTemplate]);

src/matrixrtc/MatrixRTCSession.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
878878
}
879879
}
880880

881-
if (this.manageMediaKeys && this.isJoined() && this.makeNewKeyTimeout === undefined) {
881+
if (this.manageMediaKeys && this.isJoined()) {
882882
const oldMembershipIds = new Set(
883883
oldMemberships.filter((m) => !this.isMyMembership(m)).map(getParticipantIdFromMembership),
884884
);
@@ -896,8 +896,12 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
896896
this.storeLastMembershipFingerprints();
897897

898898
if (anyLeft) {
899-
logger.debug(`Member(s) have left: queueing sender key rotation`);
900-
this.makeNewKeyTimeout = setTimeout(this.onRotateKeyTimeout, this.makeKeyDelay);
899+
if (this.makeNewKeyTimeout) {
900+
// existing rotation in progress, so let it complete
901+
} else {
902+
logger.debug(`Member(s) have left: queueing sender key rotation`);
903+
this.makeNewKeyTimeout = setTimeout(this.onRotateKeyTimeout, this.makeKeyDelay);
904+
}
901905
} else if (anyJoined) {
902906
logger.debug(`New member(s) have joined: re-sending keys`);
903907
this.requestSendCurrentKey();

0 commit comments

Comments
 (0)