diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index 31614010396..4599066c748 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -585,12 +585,15 @@ describe("MatrixRTCSession", () => { it("creates a key when joining", () => { sess!.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); - const keys = sess?.getKeysForParticipant("@alice:example.org", "AAAAAAA"); - expect(keys).toHaveLength(1); - - const allKeys = sess!.getEncryptionKeys(); - expect(allKeys).toBeTruthy(); - expect(Array.from(allKeys)).toHaveLength(1); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess?.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + expect.any(Uint8Array), + 0, + "@alice:example.org:AAAAAAA", + ); }); it("sends keys when joining", async () => { @@ -1197,9 +1200,16 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!; - expect(bobKeys).toHaveLength(1); - expect(bobKeys[0]).toEqual(Buffer.from("this is the key", "utf-8")); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("this is the key", "utf-8"), + 0, + "@bob:example.org:bobsphone", + ); + expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1); }); @@ -1222,13 +1232,16 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!; - expect(bobKeys).toHaveLength(5); - expect(bobKeys[0]).toBeFalsy(); - expect(bobKeys[1]).toBeFalsy(); - expect(bobKeys[2]).toBeFalsy(); - expect(bobKeys[3]).toBeFalsy(); - expect(bobKeys[4]).toEqual(Buffer.from("this is the key", "utf-8")); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("this is the key", "utf-8"), + 4, + "@bob:example.org:bobsphone", + ); + expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1); }); @@ -1251,9 +1264,16 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - let bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!; - expect(bobKeys).toHaveLength(1); - expect(bobKeys[0]).toEqual(Buffer.from("this is the key", "utf-8")); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("this is the key", "utf-8"), + 0, + "@bob:example.org:bobsphone", + ); + expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(1); sess.onCallEncryption({ @@ -1272,9 +1292,20 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!; - expect(bobKeys).toHaveLength(5); - expect(bobKeys[4]).toEqual(Buffer.from("this is the key", "utf-8")); + encryptionKeyChangedListener.mockClear(); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(2); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("this is the key", "utf-8"), + 0, + "@bob:example.org:bobsphone", + ); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("this is the key", "utf-8"), + 4, + "@bob:example.org:bobsphone", + ); + expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(2); }); @@ -1313,9 +1344,16 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(1000), // earlier timestamp than the newer key } as unknown as MatrixEvent); - const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!; - expect(bobKeys).toHaveLength(1); - expect(bobKeys[0]).toEqual(Buffer.from("newer key", "utf-8")); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("newer key", "utf-8"), + 0, + "@bob:example.org:bobsphone", + ); + expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(2); }); @@ -1354,9 +1392,15 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(1000), // same timestamp as the first key } as unknown as MatrixEvent); - const bobKeys = sess.getKeysForParticipant("@bob:example.org", "bobsphone")!; - expect(bobKeys).toHaveLength(1); - expect(bobKeys[0]).toEqual(Buffer.from("second key", "utf-8")); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(1); + expect(encryptionKeyChangedListener).toHaveBeenCalledWith( + Buffer.from("second key", "utf-8"), + 0, + "@bob:example.org:bobsphone", + ); }); it("ignores keys event for the local participant", () => { @@ -1378,8 +1422,11 @@ describe("MatrixRTCSession", () => { getTs: jest.fn().mockReturnValue(Date.now()), } as unknown as MatrixEvent); - const myKeys = sess.getKeysForParticipant(client.getUserId()!, client.getDeviceId()!)!; - expect(myKeys).toBeFalsy(); + const encryptionKeyChangedListener = jest.fn(); + sess!.on(MatrixRTCSessionEvent.EncryptionKeyChanged, encryptionKeyChangedListener); + sess!.reemitEncryptionKeys(); + expect(encryptionKeyChangedListener).toHaveBeenCalledTimes(0); + expect(sess!.statistics.counters.roomEventEncryptionKeysReceived).toEqual(0); }); diff --git a/src/matrixrtc/MatrixRTCSession.ts b/src/matrixrtc/MatrixRTCSession.ts index 1cf486062f7..eab4b919770 100644 --- a/src/matrixrtc/MatrixRTCSession.ts +++ b/src/matrixrtc/MatrixRTCSession.ts @@ -405,20 +405,40 @@ export class MatrixRTCSession extends TypedEventEmitter { + keys.forEach((key, index) => { + this.emit(MatrixRTCSessionEvent.EncryptionKeyChanged, key.key, index, participantId); + }); + }); + } + /** * Get the known encryption keys for a given participant device. * * @param userId the user ID of the participant * @param deviceId the device ID of the participant * @returns The encryption keys for the given participant, or undefined if they are not known. + * + * @deprecated This will be made private in a future release. */ public getKeysForParticipant(userId: string, deviceId: string): Array | undefined { + return this.getKeysForParticipantInternal(userId, deviceId); + } + + private getKeysForParticipantInternal(userId: string, deviceId: string): Array | undefined { return this.encryptionKeys.get(getParticipantId(userId, deviceId))?.map((entry) => entry.key); } /** * A map of keys used to encrypt and decrypt (we are using a symmetric * cipher) given participant's media. This also includes our own key + * + * @deprecated This will be made private in a future release. */ public getEncryptionKeys(): IterableIterator<[string, Array]> { // the returned array doesn't contain the timestamps @@ -434,7 +454,7 @@ export class MatrixRTCSession extends TypedEventEmitter