Skip to content

Commit

Permalink
Re-send MatrixRTC media encryption keys for a new joiner even if a ro…
Browse files Browse the repository at this point in the history
…tation is in progress (#4561)
  • Loading branch information
hughns authored Nov 28, 2024
1 parent 8fc77c5 commit 3781b6e
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 4 deletions.
59 changes: 58 additions & 1 deletion spec/unit/matrixrtc/MatrixRTCSession.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -864,7 +864,64 @@ describe("MatrixRTCSession", () => {
expect(client.cancelPendingEvent).toHaveBeenCalledWith(eventSentinel);
});

it("Re-sends key if a new member joins", async () => {
it("re-sends key if a new member joins even if a key rotation is in progress", async () => {
jest.useFakeTimers();
try {
// session with two members
const member2 = Object.assign({}, membershipTemplate, {
device_id: "BBBBBBB",
});
const mockRoom = makeMockRoom([membershipTemplate, member2]);
sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom);

// joining will trigger an initial key send
const keysSentPromise1 = new Promise<EncryptionKeysEventContent>((resolve) => {
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
});
sess.joinRoomSession([mockFocus], mockFocus, {
manageMediaKeys: true,
updateEncryptionKeyThrottle: 1000,
makeKeyDelay: 3000,
});
await keysSentPromise1;
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(1);

// member2 leaves triggering key rotation
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([membershipTemplate], mockRoom.roomId));
sess.onMembershipUpdate();

// member2 re-joins which should trigger an immediate re-send
const keysSentPromise2 = new Promise<EncryptionKeysEventContent>((resolve) => {
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
});
mockRoom.getLiveTimeline().getState = jest
.fn()
.mockReturnValue(makeMockRoomState([membershipTemplate, member2], mockRoom.roomId));
sess.onMembershipUpdate();
// but, that immediate resend is throttled so we need to wait a bit
jest.advanceTimersByTime(1000);
const { keys } = await keysSentPromise2;
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(2);
// key index should still be the original: 0
expect(keys[0].index).toEqual(0);

// check that the key rotation actually happens
const keysSentPromise3 = new Promise<EncryptionKeysEventContent>((resolve) => {
sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload));
});
jest.advanceTimersByTime(2000);
const { keys: rotatedKeys } = await keysSentPromise3;
expect(sess!.statistics.counters.roomEventEncryptionKeysSent).toEqual(3);
// key index should now be the rotated one: 1
expect(rotatedKeys[0].index).toEqual(1);
} finally {
jest.useRealTimers();
}
});

it("re-sends key if a new member joins", async () => {
jest.useFakeTimers();
try {
const mockRoom = makeMockRoom([membershipTemplate]);
Expand Down
10 changes: 7 additions & 3 deletions src/matrixrtc/MatrixRTCSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
}
}

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

if (anyLeft) {
logger.debug(`Member(s) have left: queueing sender key rotation`);
this.makeNewKeyTimeout = setTimeout(this.onRotateKeyTimeout, this.makeKeyDelay);
if (this.makeNewKeyTimeout) {
// existing rotation in progress, so let it complete
} else {
logger.debug(`Member(s) have left: queueing sender key rotation`);
this.makeNewKeyTimeout = setTimeout(this.onRotateKeyTimeout, this.makeKeyDelay);
}
} else if (anyJoined) {
logger.debug(`New member(s) have joined: re-sending keys`);
this.requestSendCurrentKey();
Expand Down

0 comments on commit 3781b6e

Please sign in to comment.