diff --git a/spec/unit/matrixrtc/RTCEncryptionManager.spec.ts b/spec/unit/matrixrtc/RTCEncryptionManager.spec.ts index 70e53993d..de88d47f6 100644 --- a/spec/unit/matrixrtc/RTCEncryptionManager.spec.ts +++ b/spec/unit/matrixrtc/RTCEncryptionManager.spec.ts @@ -448,6 +448,23 @@ describe("RTCEncryptionManager", () => { expect(statistics.counters.roomEventEncryptionKeysSent).toBe(2); }); + + it("Should not distribute keys if encryption is disabled", async () => { + jest.useFakeTimers(); + const members = [ + aCallMembership("@bob:example.org", "BOBDEVICE"), + aCallMembership("@bob:example.org", "BOBDEVICE2"), + aCallMembership("@carl:example.org", "CARLDEVICE"), + ]; + getMembershipMock.mockReturnValue(members); + + encryptionManager.join({ manageMediaKeys: false }); + encryptionManager.onMembershipsUpdate(); + await jest.runOnlyPendingTimersAsync(); + + expect(mockTransport.sendKey).not.toHaveBeenCalled(); + expect(onEncryptionKeysChanged).not.toHaveBeenCalled(); + }); }); describe("Receiving Keys", () => { @@ -471,6 +488,29 @@ describe("RTCEncryptionManager", () => { ); }); + it("should not accept keys when manageMediaKeys is disabled", async () => { + jest.useFakeTimers(); + + const members = [aCallMembership("@bob:example.org", "BOBDEVICE")]; + getMembershipMock.mockReturnValue(members); + + encryptionManager.join({ manageMediaKeys: false }); + encryptionManager.onMembershipsUpdate(); + await jest.advanceTimersByTimeAsync(10); + + mockTransport.emit( + KeyTransportEvents.ReceivedKeys, + "@bob:example.org", + "BOBDEVICE", + "AAAAAAAAAAA", + 0 /* KeyId */, + 0 /* Timestamp */, + ); + + expect(onEncryptionKeysChanged).not.toHaveBeenCalled(); + expect(statistics.counters.roomEventEncryptionKeysReceived).toBe(0); + }); + it("should accept keys from transport", async () => { jest.useFakeTimers(); diff --git a/src/matrixrtc/RTCEncryptionManager.ts b/src/matrixrtc/RTCEncryptionManager.ts index f59d046f7..0fc6b8799 100644 --- a/src/matrixrtc/RTCEncryptionManager.ts +++ b/src/matrixrtc/RTCEncryptionManager.ts @@ -46,6 +46,11 @@ import { * XXX In the future we want to distribute a ratcheted key not the current one for new joiners. */ export class RTCEncryptionManager implements IEncryptionManager { + // This is a stop-gap solution for now. The preferred way to handle this case would be instead + // to create a NoOpEncryptionManager that does nothing and use it for the session. + // This will be done when removing the legacy EncryptionManager. + private manageMediaKeys = false; + /** * Store the key rings for each participant. * The encryption manager stores the keys because the application layer might not be ready yet to handle the keys. @@ -126,6 +131,8 @@ export class RTCEncryptionManager implements IEncryptionManager { } public join(joinConfig: EncryptionConfig | undefined): void { + this.manageMediaKeys = joinConfig?.manageMediaKeys ?? true; // default to true + this.logger?.info(`Joining room`); this.useKeyDelay = joinConfig?.useKeyDelay ?? 1000; this.keyRotationGracePeriodMs = joinConfig?.keyRotationGracePeriodMs ?? 10_000; @@ -174,6 +181,10 @@ export class RTCEncryptionManager implements IEncryptionManager { * the calls will be coalesced to a single new distribution (that will start just after the current one has completed). */ private ensureKeyDistribution(): void { + // `manageMediaKeys` is a stop-gap solution for now. The preferred way to handle this case would be instead + // to create a NoOpEncryptionManager that does nothing and use it for the session. + // This will be done when removing the legacy EncryptionManager. + if (!this.manageMediaKeys) return; if (this.currentKeyDistributionPromise == null) { this.logger?.debug(`No active rollout, start a new one`); // start a rollout @@ -196,6 +207,15 @@ export class RTCEncryptionManager implements IEncryptionManager { } public onNewKeyReceived: KeyTransportEventListener = (userId, deviceId, keyBase64Encoded, index, timestamp) => { + // `manageMediaKeys` is a stop-gap solution for now. The preferred way to handle this case would be instead + // to create a NoOpEncryptionManager that does nothing and use it for the session. + // This will be done when removing the legacy EncryptionManager. + if (!this.manageMediaKeys) { + this.logger?.warn( + `Received key over transport ${userId}:${deviceId} at index ${index} but media keys are disabled`, + ); + return; + } this.logger?.debug(`Received key over transport ${userId}:${deviceId} at index ${index}`); // We received a new key, notify the video layer of this new key so that it can decrypt the frames properly.