Skip to content

Commit 9398271

Browse files
toger5robintown
andauthored
Check for unknown variant on to-device sending and fall back to room event encryption. (#4847)
* Check for `unknown variant` on to-device sending and fallback to room event encryption. * fix tests * fix error js-sdk api type * Change logger from debug to warn for unsupported to-device transport and improve error message comments * also add case for not supported This will be send by the driver in case we sent an encrypted to-device but do not have support of that. --------- Co-authored-by: Robin <robin@robin.town>
1 parent ef7a818 commit 9398271

File tree

5 files changed

+75
-4
lines changed

5 files changed

+75
-4
lines changed

spec/unit/matrixrtc/RoomAndToDeviceTransport.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,39 @@ describe("RoomAndToDeviceTransport", () => {
124124
expect(toDeviceSendKeySpy).toHaveBeenCalledTimes(0);
125125
expect(onTransportEnabled).toHaveBeenCalledWith({ toDevice: false, room: true });
126126
});
127+
128+
it("enables room transport and disables to device transport on widget driver error", async () => {
129+
mockClient.encryptAndSendToDevice.mockRejectedValue({
130+
message:
131+
"unknown variant `send_to_device`, expected one of `supported_api_versions`, `content_loaded`, `get_openid`, `org.matrix.msc2876.read_events`, `send_event`, `org.matrix.msc4157.update_delayed_event` at line 1 column 22",
132+
});
133+
134+
transport.start();
135+
const membership = mockCallMembership(membershipTemplate, roomId, "@alice:example.org");
136+
const onTransportEnabled = jest.fn();
137+
transport.on(RoomAndToDeviceEvents.EnabledTransportsChanged, onTransportEnabled);
138+
139+
// We start with toDevice transport enabled
140+
expect(transport.enabled.room).toBeFalsy();
141+
expect(transport.enabled.toDevice).toBeTruthy();
142+
143+
await transport.sendKey("1235", 0, [membership]);
144+
145+
// We switched transport, now room transport is enabled
146+
expect(onTransportEnabled).toHaveBeenCalledWith({ toDevice: false, room: true });
147+
expect(transport.enabled.room).toBeTruthy();
148+
expect(transport.enabled.toDevice).toBeFalsy();
149+
150+
// sanity check that we called the failang to device send key.
151+
expect(toDeviceKeyTransport.sendKey).toHaveBeenCalledWith("1235", 0, [membership]);
152+
expect(toDeviceKeyTransport.sendKey).toHaveBeenCalledTimes(1);
153+
// We re-sent the key via the room transport
154+
expect(roomKeyTransport.sendKey).toHaveBeenCalledWith("1235", 0, [membership]);
155+
expect(roomKeyTransport.sendKey).toHaveBeenCalledTimes(1);
156+
157+
mockClient.encryptAndSendToDevice.mockRestore();
158+
});
159+
127160
it("does log that it did nothing when disabled", () => {
128161
transport.start();
129162
const onNewKeyFromTransport = jest.fn();

spec/unit/matrixrtc/ToDeviceKeyTransport.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe("ToDeviceKeyTransport", () => {
3434

3535
beforeEach(() => {
3636
mockClient = getMockClientWithEventEmitter({
37-
encryptAndSendToDevice: jest.fn(),
37+
encryptAndSendToDevice: jest.fn().mockImplementation(() => Promise.resolve()),
3838
});
3939
mockLogger = {
4040
debug: jest.fn(),

src/matrixrtc/IKeyTransport.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ import { type CallMembership } from "./CallMembership.ts";
1818

1919
export enum KeyTransportEvents {
2020
ReceivedKeys = "received_keys",
21+
NotSupportedError = "not_supported_error",
2122
}
2223

2324
export type KeyTransportEventsHandlerMap = {
2425
[KeyTransportEvents.ReceivedKeys]: KeyTransportEventListener;
26+
[KeyTransportEvents.NotSupportedError]: () => void;
2527
};
2628

2729
export type KeyTransportEventListener = (

src/matrixrtc/RoomAndToDeviceKeyTransport.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { logger as rootLogger, type Logger } from "../logger.ts";
1818
import { KeyTransportEvents, type KeyTransportEventsHandlerMap, type IKeyTransport } from "./IKeyTransport.ts";
1919
import { type CallMembership } from "./CallMembership.ts";
2020
import type { RoomKeyTransport } from "./RoomKeyTransport.ts";
21-
import type { ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts";
21+
import { NotSupportedError, type ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts";
2222
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
2323

2424
// Deprecate RoomAndToDeviceTransport: This whole class is only a stop gap until we remove RoomKeyTransport.
@@ -114,6 +114,18 @@ export class RoomAndToDeviceTransport
114114
(this._enabled.toDevice ? "to device transport" : ""),
115115
);
116116
if (this._enabled.room) await this.roomKeyTransport.sendKey(keyBase64Encoded, index, members);
117-
if (this._enabled.toDevice) await this.toDeviceTransport.sendKey(keyBase64Encoded, index, members);
117+
if (this._enabled.toDevice) {
118+
try {
119+
await this.toDeviceTransport.sendKey(keyBase64Encoded, index, members);
120+
} catch (error) {
121+
if (error instanceof NotSupportedError && !this._enabled.room) {
122+
this.logger.warn(
123+
"To device is not supported enabling room key transport, disabling toDevice transport",
124+
);
125+
this.setEnabled({ toDevice: false, room: true });
126+
await this.sendKey(keyBase64Encoded, index, members);
127+
}
128+
}
129+
}
118130
}
119131
}

src/matrixrtc/ToDeviceKeyTransport.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
import { type WidgetApiResponseError } from "matrix-widget-api";
18+
1719
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
1820
import { type IKeyTransport, KeyTransportEvents, type KeyTransportEventsHandlerMap } from "./IKeyTransport.ts";
1921
import { type Logger, logger as rootLogger } from "../logger.ts";
@@ -23,6 +25,14 @@ import { ClientEvent, type MatrixClient } from "../client.ts";
2325
import type { MatrixEvent } from "../models/event.ts";
2426
import { EventType } from "../@types/event.ts";
2527

28+
export class NotSupportedError extends Error {
29+
public constructor(message?: string) {
30+
super(message);
31+
}
32+
public get name(): string {
33+
return "NotSupportedError";
34+
}
35+
}
2636
/**
2737
* ToDeviceKeyTransport is used to send MatrixRTC keys to other devices using the
2838
* to-device CS-API.
@@ -91,7 +101,21 @@ export class ToDeviceKeyTransport
91101
});
92102

93103
if (targets.length > 0) {
94-
await this.client.encryptAndSendToDevice(EventType.CallEncryptionKeysPrefix, targets, content);
104+
await this.client
105+
.encryptAndSendToDevice(EventType.CallEncryptionKeysPrefix, targets, content)
106+
.catch((error: WidgetApiResponseError) => {
107+
const msg: string = error.message;
108+
// This is not ideal. We would want to have a custom error type for unsupported actions.
109+
// This is not part of the widget API spec. Since as of now there are only two implementations:
110+
// Rust SDK + JS-SDK, and the JS-SDK does support to-device sending, we can assume that
111+
// this is a widget driver issue error message.
112+
if (
113+
(msg.includes("unknown variant") && msg.includes("send_to_device")) ||
114+
msg.includes("not supported")
115+
) {
116+
throw new NotSupportedError("The widget driver does not support to-device encryption");
117+
}
118+
});
95119
this.statistics.counters.roomEventEncryptionKeysSent += 1;
96120
} else {
97121
this.logger.warn("No targets found for sending key");

0 commit comments

Comments
 (0)