diff --git a/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts b/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts index d2f578c0c1..963b1e250d 100644 --- a/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts +++ b/spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts @@ -16,7 +16,7 @@ limitations under the License. import { type Mocked } from "vitest"; -import { makeMockEvent } from "./mocks.ts"; +import { makeMatrixEvent } from "./mocks.ts"; import { ClientEvent, EventType, type MatrixClient } from "../../../src"; import { ToDeviceKeyTransport } from "../../../src/matrixrtc/ToDeviceKeyTransport.ts"; import { getMockClientWithEventEmitter } from "../../test-utils/client.ts"; @@ -122,24 +122,23 @@ describe("ToDeviceKeyTransport", () => { const testEncoded = "ABCDEDF"; const testKeyIndex = 2; - mockClient.emit( - ClientEvent.ToDeviceEvent, - makeMockEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, { - keys: { - index: testKeyIndex, - key: testEncoded, - }, - member: { - claimed_device_id: "BOBDEVICE", - }, - room_id: roomId, - session: { - application: "m.call", - call_id: "", - scope: "m.room", - }, - }), - ); + const mockEvent = makeMatrixEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, { + keys: { + index: testKeyIndex, + key: testEncoded, + }, + member: { + claimed_device_id: "BOBDEVICE", + }, + room_id: roomId, + session: { + application: "m.call", + call_id: "", + scope: "m.room", + }, + }); + mockEvent.makeEncrypted(EventType.RoomMessageEncrypted, {}, "", ""); + mockClient.emit(ClientEvent.ToDeviceEvent, mockEvent); const { userId, deviceId, keyBase64Encoded, index } = await receivedKeyResolvers.promise; expect(userId).toBe("@bob:example.org"); @@ -150,6 +149,41 @@ describe("ToDeviceKeyTransport", () => { expect(statistics.counters.roomEventEncryptionKeysReceived).toBe(1); }); + it("should drop non-encrypted/clear to-device events", () => { + const receivedKeyResolvers = vi.fn(); + transport.on(KeyTransportEvents.ReceivedKeys, (membership, keyBase64Encoded, index, _timestamp) => { + receivedKeyResolvers(); + }); + transport.start(); + + const testEncoded = "ABCDEDF"; + const testKeyIndex = 2; + + const clearEvent = makeMatrixEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, { + keys: { + index: testKeyIndex, + key: testEncoded, + }, + member: { + claimed_device_id: "BOBDEVICE", + }, + room_id: roomId, + session: { + application: "m.call", + call_id: "", + scope: "m.room", + }, + }); + mockClient.emit(ClientEvent.ToDeviceEvent, clearEvent); + + expect(receivedKeyResolvers).toHaveBeenCalledTimes(0); + + clearEvent.makeEncrypted(EventType.RoomMessageEncrypted, {}, "", ""); + mockClient.emit(ClientEvent.ToDeviceEvent, clearEvent); + + expect(receivedKeyResolvers).toHaveBeenCalledTimes(1); + }); + it("should not sent to ourself", async () => { const keyBase64Encoded = "ABCDEDF"; const keyIndex = 2; @@ -168,24 +202,24 @@ describe("ToDeviceKeyTransport", () => { const testEncoded = "ABCDEDF"; const testKeyIndex = 2; - mockClient.emit( - ClientEvent.ToDeviceEvent, - makeMockEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, { - keys: { - index: testKeyIndex, - key: testEncoded, - }, - member: { - claimed_device_id: "BOBDEVICE", - }, - room_id: "!anotherroom:id", - session: { - application: "m.call", - call_id: "", - scope: "m.room", - }, - }), - ); + const keyEvent = makeMatrixEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, { + keys: { + index: testKeyIndex, + key: testEncoded, + }, + member: { + claimed_device_id: "BOBDEVICE", + }, + room_id: "!anotherroom:id", + session: { + application: "m.call", + call_id: "", + scope: "m.room", + }, + }); + + keyEvent.makeEncrypted(EventType.RoomMessageEncrypted, {}, "", ""); + mockClient.emit(ClientEvent.ToDeviceEvent, keyEvent); expect(mockLogger.warn).toHaveBeenCalledWith("Malformed Event: Mismatch roomId"); expect(statistics.counters.roomEventEncryptionKeysReceived).toBe(0); @@ -240,7 +274,7 @@ describe("ToDeviceKeyTransport", () => { mockClient.emit( ClientEvent.ToDeviceEvent, - makeMockEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, event), + makeMatrixEvent(EventType.CallEncryptionKeysPrefix, "@bob:example.org", undefined, event), ); expect(mockLogger.warn).toHaveBeenCalled(); diff --git a/spec/unit/matrixrtc/mocks.ts b/spec/unit/matrixrtc/mocks.ts index 79fa82791e..3f750081e7 100644 --- a/spec/unit/matrixrtc/mocks.ts +++ b/spec/unit/matrixrtc/mocks.ts @@ -17,7 +17,7 @@ limitations under the License. import { EventEmitter } from "node:stream"; import { type Mocked, type MockedObject } from "vitest"; -import { EventType, type Room, RoomEvent, type MatrixClient, type MatrixEvent } from "../../../src"; +import { EventType, type Room, RoomEvent, type MatrixClient, MatrixEvent } from "../../../src"; import { CallMembership } from "../../../src/matrixrtc"; import { secureRandomString } from "../../../src/randomstring"; import { type RtcMembershipData, type SessionMembershipData } from "../../../src/matrixrtc/membershipData"; @@ -108,6 +108,7 @@ export type MockClient = MockedObject< | "cancelPendingEvent" > >; + /** * Mocks a object that has all required methods for a MatrixRTC session client. */ @@ -232,6 +233,24 @@ export function makeMockEvent( } as unknown as MatrixEvent; } +export function makeMatrixEvent( + type: string, + sender: string, + roomId: string | undefined, + content: any, + timestamp?: number, + stateKey?: string, +): MatrixEvent { + return new MatrixEvent({ + type, + sender, + room_id: roomId, + content, + state_key: stateKey, + origin_server_ts: timestamp, + }); +} + export function mockRTCEvent( { user_id: sender, ...membershipData }: MembershipData, roomId: string, diff --git a/src/matrixrtc/ToDeviceKeyTransport.ts b/src/matrixrtc/ToDeviceKeyTransport.ts index 0544accad1..9c0662f0e2 100644 --- a/src/matrixrtc/ToDeviceKeyTransport.ts +++ b/src/matrixrtc/ToDeviceKeyTransport.ts @@ -152,13 +152,13 @@ export class ToDeviceKeyTransport return; } - // TODO: Not possible to check if the event is encrypted or not - // see https://github.com/matrix-org/matrix-rust-sdk/issues/4883 - // if (evnt.getWireType() != EventType.RoomMessageEncrypted) { - // // WARN: The call keys were sent in clear. Ignore them - // logger.warn(`Call encryption keys sent in clear from: ${event.getSender()}`); - // return; - // } + // NB: When received via the widget driver, the to-device events + // are properly reconstructed as if they are encrypted (see MatrixEvent#makeEncrypted). + if (event.getWireType() != EventType.RoomMessageEncrypted) { + // WARN: The call keys were sent in clear. Ignore them + this.logger.warn(`Call encryption keys sent in clear from: ${event.getSender()}`); + return; + } const content = this.getValidEventContent(event); if (!content) return;