matrix-js-sdk 41.0.0 → 41.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/lib/@types/event.d.ts +1 -1
- package/lib/@types/event.d.ts.map +1 -1
- package/lib/@types/event.js +1 -1
- package/lib/@types/event.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +250 -246
- package/lib/client.js.map +1 -1
- package/lib/crypto-api/index.d.ts +13 -2
- package/lib/crypto-api/index.d.ts.map +1 -1
- package/lib/crypto-api/index.js +11 -0
- package/lib/crypto-api/index.js.map +1 -1
- package/lib/logger.d.ts +5 -5
- package/lib/logger.d.ts.map +1 -1
- package/lib/logger.js.map +1 -1
- package/lib/matrixrtc/CallMembership.d.ts +49 -145
- package/lib/matrixrtc/CallMembership.d.ts.map +1 -1
- package/lib/matrixrtc/CallMembership.js +157 -265
- package/lib/matrixrtc/CallMembership.js.map +1 -1
- package/lib/matrixrtc/EncryptionManager.d.ts +1 -85
- package/lib/matrixrtc/EncryptionManager.d.ts.map +1 -1
- package/lib/matrixrtc/EncryptionManager.js +0 -317
- package/lib/matrixrtc/EncryptionManager.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.d.ts +18 -22
- package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.js +48 -76
- package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.d.ts +2 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.d.ts.map +1 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.js +3 -2
- package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
- package/lib/matrixrtc/MembershipManager.d.ts +10 -4
- package/lib/matrixrtc/MembershipManager.d.ts.map +1 -1
- package/lib/matrixrtc/MembershipManager.js +10 -4
- package/lib/matrixrtc/MembershipManager.js.map +1 -1
- package/lib/matrixrtc/RTCEncryptionManager.d.ts +6 -7
- package/lib/matrixrtc/RTCEncryptionManager.d.ts.map +1 -1
- package/lib/matrixrtc/RTCEncryptionManager.js +4 -7
- package/lib/matrixrtc/RTCEncryptionManager.js.map +1 -1
- package/lib/matrixrtc/index.d.ts +1 -0
- package/lib/matrixrtc/index.d.ts.map +1 -1
- package/lib/matrixrtc/index.js.map +1 -1
- package/lib/matrixrtc/membershipData/common.d.ts +8 -0
- package/lib/matrixrtc/membershipData/common.d.ts.map +1 -0
- package/lib/matrixrtc/membershipData/common.js +26 -0
- package/lib/matrixrtc/membershipData/common.js.map +1 -0
- package/lib/matrixrtc/membershipData/index.d.ts +4 -0
- package/lib/matrixrtc/membershipData/index.d.ts.map +1 -0
- package/lib/matrixrtc/membershipData/index.js +20 -0
- package/lib/matrixrtc/membershipData/index.js.map +1 -0
- package/lib/matrixrtc/membershipData/rtc.d.ts +33 -0
- package/lib/matrixrtc/membershipData/rtc.d.ts.map +1 -0
- package/lib/matrixrtc/membershipData/rtc.js +137 -0
- package/lib/matrixrtc/membershipData/rtc.js.map +1 -0
- package/lib/matrixrtc/membershipData/session.d.ts +77 -0
- package/lib/matrixrtc/membershipData/session.d.ts.map +1 -0
- package/lib/matrixrtc/membershipData/session.js +62 -0
- package/lib/matrixrtc/membershipData/session.js.map +1 -0
- package/lib/matrixrtc/types.d.ts +23 -0
- package/lib/matrixrtc/types.d.ts.map +1 -1
- package/lib/matrixrtc/types.js +9 -1
- package/lib/matrixrtc/types.js.map +1 -1
- package/lib/matrixrtc/utils.d.ts +11 -1
- package/lib/matrixrtc/utils.d.ts.map +1 -1
- package/lib/matrixrtc/utils.js +24 -1
- package/lib/matrixrtc/utils.js.map +1 -1
- package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
- package/lib/rust-crypto/rust-crypto.js +2 -2
- package/lib/rust-crypto/rust-crypto.js.map +1 -1
- package/package.json +5 -7
- package/src/@types/event.ts +2 -2
- package/src/client.ts +5 -3
- package/src/crypto-api/index.ts +17 -2
- package/src/logger.ts +5 -5
- package/src/matrixrtc/CallMembership.ts +159 -373
- package/src/matrixrtc/EncryptionManager.ts +1 -417
- package/src/matrixrtc/MatrixRTCSession.ts +82 -122
- package/src/matrixrtc/MatrixRTCSessionManager.ts +5 -3
- package/src/matrixrtc/MembershipManager.ts +14 -17
- package/src/matrixrtc/RTCEncryptionManager.ts +7 -10
- package/src/matrixrtc/index.ts +1 -0
- package/src/matrixrtc/membershipData/common.ts +27 -0
- package/src/matrixrtc/membershipData/index.ts +19 -0
- package/src/matrixrtc/membershipData/rtc.ts +156 -0
- package/src/matrixrtc/membershipData/session.ts +146 -0
- package/src/matrixrtc/types.ts +27 -1
- package/src/matrixrtc/utils.ts +24 -2
- package/src/rust-crypto/rust-crypto.ts +4 -1
- package/lib/matrixrtc/RoomKeyTransport.d.ts +0 -25
- package/lib/matrixrtc/RoomKeyTransport.d.ts.map +0 -1
- package/lib/matrixrtc/RoomKeyTransport.js +0 -152
- package/lib/matrixrtc/RoomKeyTransport.js.map +0 -1
- package/src/matrixrtc/RoomKeyTransport.ts +0 -189
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 The Matrix.org Foundation C.I.C.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { MXID_PATTERN } from "../../models/room-member.ts";
|
|
18
|
+
import type { IContent } from "../../models/event.ts";
|
|
19
|
+
import type { RelationType } from "../../types.ts";
|
|
20
|
+
import { type RtcSlotEventContent, type Transport } from "../types.ts";
|
|
21
|
+
import { MatrixRTCMembershipParseError } from "./common.ts";
|
|
22
|
+
import { sha256 } from "../../digest.ts";
|
|
23
|
+
import { encodeUnpaddedBase64Url } from "../../base64.ts";
|
|
24
|
+
import { slotIdToDescription } from "../utils.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Represents the current form of MSC4143, which uses sticky events to store membership.
|
|
28
|
+
*/
|
|
29
|
+
export interface RtcMembershipData {
|
|
30
|
+
"slot_id": string;
|
|
31
|
+
"member": {
|
|
32
|
+
user_id: string;
|
|
33
|
+
device_id: string;
|
|
34
|
+
id: string;
|
|
35
|
+
};
|
|
36
|
+
"m.relates_to"?: {
|
|
37
|
+
event_id: string;
|
|
38
|
+
rel_type: RelationType.Reference;
|
|
39
|
+
};
|
|
40
|
+
"application": RtcSlotEventContent["application"];
|
|
41
|
+
"rtc_transports": Transport[];
|
|
42
|
+
"versions": string[];
|
|
43
|
+
"msc4354_sticky_key"?: string;
|
|
44
|
+
"sticky_key"?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validates that `data` matches the format expected by MSC4143.
|
|
49
|
+
* @param data The event content.
|
|
50
|
+
* @param sender The sender of the event.
|
|
51
|
+
* @returns true if `data` is valid RtcMembershipData
|
|
52
|
+
* @throws {MatrixRTCMembershipParseError} if the content is not valid
|
|
53
|
+
*/
|
|
54
|
+
export const checkRtcMembershipData = (data: IContent, sender: string): data is RtcMembershipData => {
|
|
55
|
+
const errors: string[] = [];
|
|
56
|
+
const prefix = " - ";
|
|
57
|
+
const expectedSlotPrefix = `${data?.application?.type}#`;
|
|
58
|
+
|
|
59
|
+
// required fields
|
|
60
|
+
if (typeof data.slot_id !== "string") {
|
|
61
|
+
errors.push(prefix + "slot_id must be string");
|
|
62
|
+
} else if (!data.slot_id.startsWith(expectedSlotPrefix)) {
|
|
63
|
+
errors.push(prefix + `slot_id must start with ${expectedSlotPrefix}`);
|
|
64
|
+
} else {
|
|
65
|
+
try {
|
|
66
|
+
slotIdToDescription(data.slot_id);
|
|
67
|
+
} catch (ex) {
|
|
68
|
+
errors.push(prefix + `slot_id was badly formed${ex instanceof Error ? `: ${ex.message}` : ""}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof data.member !== "object" || data.member === null) {
|
|
73
|
+
errors.push(prefix + "member must be an object");
|
|
74
|
+
} else {
|
|
75
|
+
if (typeof data.member.user_id !== "string") {
|
|
76
|
+
errors.push(prefix + "member.user_id must be string");
|
|
77
|
+
} else if (!MXID_PATTERN.test(data.member.user_id)) {
|
|
78
|
+
errors.push(prefix + "member.user_id must be a valid mxid");
|
|
79
|
+
}
|
|
80
|
+
// This is not what the spec enforces but there currently are no rules what power levels are required to
|
|
81
|
+
// send a m.rtc.member event for a other user. So we add this check for simplicity and to avoid possible attacks until there
|
|
82
|
+
// is a proper definition when this is allowed.
|
|
83
|
+
else if (data.member.user_id !== sender) {
|
|
84
|
+
errors.push(prefix + "member.user_id must match the sender");
|
|
85
|
+
}
|
|
86
|
+
if (typeof data.member.device_id !== "string") {
|
|
87
|
+
errors.push(prefix + "member.device_id must be string");
|
|
88
|
+
}
|
|
89
|
+
if (typeof data.member.id !== "string") errors.push(prefix + "member.id must be string");
|
|
90
|
+
}
|
|
91
|
+
if (typeof data.application !== "object" || data.application === null) {
|
|
92
|
+
errors.push(prefix + "application must be an object");
|
|
93
|
+
} else {
|
|
94
|
+
if (typeof data.application.type !== "string") {
|
|
95
|
+
errors.push(prefix + "application.type must be a string");
|
|
96
|
+
} else {
|
|
97
|
+
if (data.application.type.includes("#")) errors.push(prefix + 'application.type must not include "#"');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (data.rtc_transports === undefined || !Array.isArray(data.rtc_transports)) {
|
|
101
|
+
errors.push(prefix + "rtc_transports must be an array");
|
|
102
|
+
} else {
|
|
103
|
+
// validate that each transport has at least a string 'type'
|
|
104
|
+
for (const t of data.rtc_transports) {
|
|
105
|
+
if (typeof t !== "object" || t === null || typeof (t as any).type !== "string") {
|
|
106
|
+
errors.push(prefix + "rtc_transports entries must be objects with a string type");
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (data.versions === undefined || !Array.isArray(data.versions)) {
|
|
112
|
+
errors.push(prefix + "versions must be an array");
|
|
113
|
+
} else if (!data.versions.every((v) => typeof v === "string")) {
|
|
114
|
+
errors.push(prefix + "versions must be an array of strings");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// optional fields
|
|
118
|
+
if ((data.sticky_key ?? data.msc4354_sticky_key) === undefined) {
|
|
119
|
+
errors.push(prefix + "sticky_key or msc4354_sticky_key must be a defined");
|
|
120
|
+
}
|
|
121
|
+
if (data.sticky_key !== undefined && typeof data.sticky_key !== "string") {
|
|
122
|
+
errors.push(prefix + "sticky_key must be a string");
|
|
123
|
+
}
|
|
124
|
+
if (data.msc4354_sticky_key !== undefined && typeof data.msc4354_sticky_key !== "string") {
|
|
125
|
+
errors.push(prefix + "msc4354_sticky_key must be a string");
|
|
126
|
+
}
|
|
127
|
+
if (
|
|
128
|
+
data.sticky_key !== undefined &&
|
|
129
|
+
data.msc4354_sticky_key !== undefined &&
|
|
130
|
+
data.sticky_key !== data.msc4354_sticky_key
|
|
131
|
+
) {
|
|
132
|
+
errors.push(prefix + "sticky_key and msc4354_sticky_key must be equal if both are defined");
|
|
133
|
+
}
|
|
134
|
+
if (data["m.relates_to"] !== undefined) {
|
|
135
|
+
const rel = data["m.relates_to"] as RtcMembershipData["m.relates_to"];
|
|
136
|
+
if (typeof rel !== "object" || rel === null) {
|
|
137
|
+
errors.push(prefix + "m.relates_to must be an object if provided");
|
|
138
|
+
} else {
|
|
139
|
+
if (typeof rel.event_id !== "string") errors.push(prefix + "m.relates_to.event_id must be a string");
|
|
140
|
+
if (rel.rel_type !== "m.reference") errors.push(prefix + "m.relates_to.rel_type must be m.reference");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (errors.length) {
|
|
145
|
+
throw new MatrixRTCMembershipParseError("RtcMembership", errors);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return true;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export async function computeRtcIdentityRaw(userId: string, deviceId: string, memberId: string): Promise<string> {
|
|
152
|
+
const hashInput = `${userId}|${deviceId}|${memberId}`;
|
|
153
|
+
const hashBuffer = await sha256(hashInput);
|
|
154
|
+
const hashedString = encodeUnpaddedBase64Url(hashBuffer);
|
|
155
|
+
return hashedString;
|
|
156
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 The Matrix.org Foundation C.I.C.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type IContent } from "../../matrix.ts";
|
|
18
|
+
import { type RTCCallIntent, type Transport } from "../types.ts";
|
|
19
|
+
import { MatrixRTCMembershipParseError } from "./common.ts";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* (MatrixRTC) session membership data.
|
|
23
|
+
* This represents the *OLD* form of MSC4143, which uses state events to store membership.
|
|
24
|
+
* Represents the `session` in the memberships section of an m.call.member event as it is on the wire.
|
|
25
|
+
**/
|
|
26
|
+
export type SessionMembershipData = {
|
|
27
|
+
/**
|
|
28
|
+
* The RTC application defines the type of the RTC session.
|
|
29
|
+
*/
|
|
30
|
+
"application": string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The id of this session.
|
|
34
|
+
* A session can never span over multiple rooms so this id is to distinguish between
|
|
35
|
+
* multiple session in one room. A room wide session that is not associated with a user,
|
|
36
|
+
* and therefore immune to creation race conflicts, uses the `call_id: ""`.
|
|
37
|
+
*/
|
|
38
|
+
"call_id": string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The Matrix device ID of this session. A single user can have multiple sessions on different devices.
|
|
42
|
+
*/
|
|
43
|
+
"device_id": string;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The focus selection system this user/membership is using.
|
|
47
|
+
* NOTE: This is still included for legacy reasons, but not consumed by the SDK.
|
|
48
|
+
*/
|
|
49
|
+
"focus_active": {
|
|
50
|
+
type: "livekit" | string;
|
|
51
|
+
focus_selection: "oldest_membership" | "multi_sfu" | string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A list of possible foci this user knows about. One of them might be used based on the focus_active
|
|
56
|
+
* selection system.
|
|
57
|
+
*/
|
|
58
|
+
"foci_preferred": Transport[];
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Optional field that contains the creation of the session. If it is undefined the creation
|
|
62
|
+
* is the `origin_server_ts` of the event itself. For updates to the event this property tracks
|
|
63
|
+
* the `origin_server_ts` of the initial join event.
|
|
64
|
+
* - If it is undefined it can be interpreted as a "Join".
|
|
65
|
+
* - If it is defined it can be interpreted as an "Update"
|
|
66
|
+
*/
|
|
67
|
+
"created_ts"?: number;
|
|
68
|
+
|
|
69
|
+
// Application specific data
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* If the `application` = `"m.call"` this defines if it is a room or user owned call.
|
|
73
|
+
* There can always be one room scoped call but multiple user owned calls (breakout sessions)
|
|
74
|
+
*/
|
|
75
|
+
"scope"?: "m.room" | "m.user";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Optionally we allow to define a delta to the `created_ts` that defines when the event is expired/invalid.
|
|
79
|
+
* This should be set to multiple hours. The only reason it exist is to deal with failed delayed events.
|
|
80
|
+
* (for example caused by a homeserver crashes)
|
|
81
|
+
**/
|
|
82
|
+
"expires"?: number;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The intent of the call from the perspective of this user. This may be an audio call, video call or
|
|
86
|
+
* something else.
|
|
87
|
+
*/
|
|
88
|
+
"m.call.intent"?: RTCCallIntent;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The id used on the media backend.
|
|
92
|
+
* (With livekit this is the participant identity on the LK SFU)
|
|
93
|
+
* This can be a UUID but right now it is `${this.matrixEventData.sender}:${data.device_id}`.
|
|
94
|
+
*
|
|
95
|
+
* It is compleatly valid to not set this field. Other clients will treat `undefined` as `${this.matrixEventData.sender}:${data.device_id}`
|
|
96
|
+
*/
|
|
97
|
+
"membershipID"?: string;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Validates that `data` matches the format expected by the legacy form of MSC4143.
|
|
102
|
+
* @param data The event content.
|
|
103
|
+
* @returns true if `data` is valid SessionMembershipData
|
|
104
|
+
* @throws {MatrixRTCMembershipParseError} if the content is not valid
|
|
105
|
+
*/
|
|
106
|
+
export const checkSessionsMembershipData = (data: IContent): data is SessionMembershipData => {
|
|
107
|
+
const prefix = " - ";
|
|
108
|
+
const errors: string[] = [];
|
|
109
|
+
if (typeof data.device_id !== "string") errors.push(prefix + "device_id must be string");
|
|
110
|
+
if (typeof data.call_id !== "string") errors.push(prefix + "call_id must be string");
|
|
111
|
+
if (typeof data.application !== "string") errors.push(prefix + "application must be a string");
|
|
112
|
+
if (data.focus_active === undefined) {
|
|
113
|
+
errors.push(prefix + "focus_active has an invalid type");
|
|
114
|
+
}
|
|
115
|
+
if (typeof data.focus_active?.type !== "string") {
|
|
116
|
+
errors.push(prefix + "focus_active.type must be a string");
|
|
117
|
+
}
|
|
118
|
+
if (
|
|
119
|
+
data.foci_preferred !== undefined &&
|
|
120
|
+
!(
|
|
121
|
+
Array.isArray(data.foci_preferred) &&
|
|
122
|
+
data.foci_preferred.every(
|
|
123
|
+
(f: Transport) => typeof f === "object" && f !== null && typeof f.type === "string",
|
|
124
|
+
)
|
|
125
|
+
)
|
|
126
|
+
) {
|
|
127
|
+
errors.push(prefix + "foci_preferred must be an array of transport objects");
|
|
128
|
+
}
|
|
129
|
+
// optional parameters
|
|
130
|
+
if (data.created_ts !== undefined && typeof data.created_ts !== "number") {
|
|
131
|
+
errors.push(prefix + "created_ts must be number");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// application specific data (we first need to check if they exist)
|
|
135
|
+
if (data.scope !== undefined && typeof data.scope !== "string") errors.push(prefix + "scope must be string");
|
|
136
|
+
|
|
137
|
+
if (data["m.call.intent"] !== undefined && typeof data["m.call.intent"] !== "string") {
|
|
138
|
+
errors.push(prefix + "m.call.intent must be a string");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (errors.length) {
|
|
142
|
+
throw new MatrixRTCMembershipParseError("SessionMembership", errors);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return true;
|
|
146
|
+
};
|
package/src/matrixrtc/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
2
|
+
Copyright 2023-2026 The Matrix.org Foundation C.I.C.
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -199,3 +199,29 @@ export interface Transport {
|
|
|
199
199
|
type: string;
|
|
200
200
|
[key: string]: unknown;
|
|
201
201
|
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Event content for a `m.rtc.slot` state event.
|
|
205
|
+
*/
|
|
206
|
+
export interface RtcSlotEventContent<T extends string = string> {
|
|
207
|
+
application: {
|
|
208
|
+
type: T;
|
|
209
|
+
// other application specific keys
|
|
210
|
+
[key: string]: unknown;
|
|
211
|
+
};
|
|
212
|
+
slot_id: string;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* The session description is used to identify a session. Used in the state event.
|
|
217
|
+
*/
|
|
218
|
+
export interface SlotDescription {
|
|
219
|
+
/**
|
|
220
|
+
* The application type. e.g. "m.call".
|
|
221
|
+
*/
|
|
222
|
+
application: string;
|
|
223
|
+
/**
|
|
224
|
+
* The application-specific slot ID. e.g. "ROOM".
|
|
225
|
+
*/
|
|
226
|
+
id: string;
|
|
227
|
+
}
|
package/src/matrixrtc/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2025 The Matrix.org Foundation C.I.C.
|
|
2
|
+
Copyright 2025-2026 The Matrix.org Foundation C.I.C.
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -15,7 +15,7 @@ limitations under the License.
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { getEncryptionKeyMapKey, type CallMembershipIdentityParts } from "./EncryptionManager.ts";
|
|
18
|
-
import {
|
|
18
|
+
import type { InboundEncryptionSession, EncryptionKeyMapKey, SlotDescription } from "./types.ts";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Detects when a key for a given index is outdated.
|
|
@@ -47,3 +47,25 @@ export class OutdatedKeyFilter {
|
|
|
47
47
|
return false;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Converts a slot ID into it's component application and ID portions.
|
|
53
|
+
* @param slotId e.g. `m.call#call_id`
|
|
54
|
+
* @throws If the format of `slotId` is invalid.
|
|
55
|
+
*/
|
|
56
|
+
export function slotIdToDescription(slotId: string): SlotDescription {
|
|
57
|
+
const [application, id, ...unexpectedAdditionalValues] = slotId.split("#");
|
|
58
|
+
if (unexpectedAdditionalValues.length) {
|
|
59
|
+
throw Error(
|
|
60
|
+
"MatrixRTC Slot IDs *must* only contain two components seperated by one '#'. Additional '#' characters detected.",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return { application, id };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Converts a SlotDescription into it's slot ID format.
|
|
68
|
+
*/
|
|
69
|
+
export function computeSlotId(slotDescription: SlotDescription): string {
|
|
70
|
+
return `${slotDescription.application}#${slotDescription.id}`;
|
|
71
|
+
}
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
CryptoEvent,
|
|
51
51
|
type CryptoEventHandlerMap,
|
|
52
52
|
DecryptionFailureCode,
|
|
53
|
+
DecryptionKeyDoesNotMatchError,
|
|
53
54
|
deriveRecoveryKeyFromPassphrase,
|
|
54
55
|
type DeviceIsolationMode,
|
|
55
56
|
DeviceIsolationModeKind,
|
|
@@ -1315,7 +1316,9 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, CryptoEventH
|
|
|
1315
1316
|
|
|
1316
1317
|
const backupDecryptionKey = RustSdkCryptoJs.BackupDecryptionKey.fromBase64(backupKey);
|
|
1317
1318
|
if (!decryptionKeyMatchesKeyBackupInfo(backupDecryptionKey, keyBackupInfo)) {
|
|
1318
|
-
throw new
|
|
1319
|
+
throw new DecryptionKeyDoesNotMatchError(
|
|
1320
|
+
"loadSessionBackupPrivateKeyFromSecretStorage: decryption key does not match backup info",
|
|
1321
|
+
);
|
|
1319
1322
|
}
|
|
1320
1323
|
|
|
1321
1324
|
await this.backupManager.saveBackupDecryptionKey(backupDecryptionKey, keyBackupInfo.version);
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { MatrixClient } from "../client.ts";
|
|
2
|
-
import { type ParticipantDeviceInfo, type Statistics } from "./types.ts";
|
|
3
|
-
import { type Logger } from "../logger.ts";
|
|
4
|
-
import { KeyTransportEvents, type KeyTransportEventsHandlerMap, type IKeyTransport } from "./IKeyTransport.ts";
|
|
5
|
-
import { type MatrixEvent } from "../models/event.ts";
|
|
6
|
-
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
|
7
|
-
import { type Room } from "../models/room.ts";
|
|
8
|
-
/**
|
|
9
|
-
* @deprecated This is depreacted and not used anymore. use the ToDeviceTransport
|
|
10
|
-
*/
|
|
11
|
-
export declare class RoomKeyTransport extends TypedEventEmitter<KeyTransportEvents, KeyTransportEventsHandlerMap> implements IKeyTransport {
|
|
12
|
-
private room;
|
|
13
|
-
private client;
|
|
14
|
-
private statistics;
|
|
15
|
-
private logger;
|
|
16
|
-
setParentLogger(parentLogger: Logger): void;
|
|
17
|
-
constructor(room: Pick<Room, "on" | "off" | "roomId">, client: Pick<MatrixClient, "sendEvent" | "getDeviceId" | "getUserId" | "cancelPendingEvent" | "decryptEventIfNeeded">, statistics: Statistics, parentLogger?: Logger);
|
|
18
|
-
start(): void;
|
|
19
|
-
stop(): void;
|
|
20
|
-
private consumeCallEncryptionEvent;
|
|
21
|
-
/** implements {@link IKeyTransport#sendKey} */
|
|
22
|
-
sendKey(keyBase64Encoded: string, index: number, members: ParticipantDeviceInfo[]): Promise<void>;
|
|
23
|
-
onEncryptionEvent(event: MatrixEvent): void;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=RoomKeyTransport.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RoomKeyTransport.d.ts","sourceRoot":"","sources":["../../src/matrixrtc/RoomKeyTransport.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAmC,KAAK,qBAAqB,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAG1G,OAAO,EAAwB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,KAAK,4BAA4B,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC/G,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,KAAK,IAAI,EAAa,MAAM,mBAAmB,CAAC;AAEzD;;GAEG;AACH,qBAAa,gBACT,SAAQ,iBAAiB,CAAC,kBAAkB,EAAE,4BAA4B,CAC1E,YAAW,aAAa;IAOpB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,UAAU;IAVtB,OAAO,CAAC,MAAM,CAAsB;IAC7B,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;gBAItC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,KAAK,GAAG,QAAQ,CAAC,EACzC,MAAM,EAAE,IAAI,CAChB,YAAY,EACZ,WAAW,GAAG,aAAa,GAAG,WAAW,GAAG,oBAAoB,GAAG,sBAAsB,CAC5F,EACO,UAAU,EAAE,UAAU,EAC9B,YAAY,CAAC,EAAE,MAAM;IAKlB,KAAK,IAAI,IAAI;IAGb,IAAI,IAAI,IAAI;YAIL,0BAA0B;IA4BxC,+CAA+C;IAClC,OAAO,CAAC,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BvG,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;CA2ErD"}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
|
-
/*
|
|
4
|
-
Copyright 2025 The Matrix.org Foundation C.I.C.
|
|
5
|
-
|
|
6
|
-
Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
-
you may not use this file except in compliance with the License.
|
|
8
|
-
You may obtain a copy of the License at
|
|
9
|
-
|
|
10
|
-
http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
-
|
|
12
|
-
Unless required by applicable law or agreed to in writing, software
|
|
13
|
-
distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
-
See the License for the specific language governing permissions and
|
|
16
|
-
limitations under the License.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { EventType } from "../@types/event.js";
|
|
20
|
-
import { logger as rootLogger } from "../logger.js";
|
|
21
|
-
import { KeyTransportEvents } from "./IKeyTransport.js";
|
|
22
|
-
import { TypedEventEmitter } from "../models/typed-event-emitter.js";
|
|
23
|
-
import { RoomEvent } from "../models/room.js";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* @deprecated This is depreacted and not used anymore. use the ToDeviceTransport
|
|
27
|
-
*/
|
|
28
|
-
export class RoomKeyTransport extends TypedEventEmitter {
|
|
29
|
-
setParentLogger(parentLogger) {
|
|
30
|
-
this.logger = parentLogger.getChild("[RoomKeyTransport]");
|
|
31
|
-
}
|
|
32
|
-
constructor(room, client, statistics, parentLogger) {
|
|
33
|
-
super();
|
|
34
|
-
this.room = room;
|
|
35
|
-
this.client = client;
|
|
36
|
-
this.statistics = statistics;
|
|
37
|
-
_defineProperty(this, "logger", rootLogger);
|
|
38
|
-
this.setParentLogger(parentLogger !== null && parentLogger !== void 0 ? parentLogger : rootLogger);
|
|
39
|
-
}
|
|
40
|
-
start() {
|
|
41
|
-
this.room.on(RoomEvent.Timeline, ev => void this.consumeCallEncryptionEvent(ev));
|
|
42
|
-
}
|
|
43
|
-
stop() {
|
|
44
|
-
this.room.off(RoomEvent.Timeline, ev => void this.consumeCallEncryptionEvent(ev));
|
|
45
|
-
}
|
|
46
|
-
consumeCallEncryptionEvent(event) {
|
|
47
|
-
var _arguments = arguments,
|
|
48
|
-
_this = this;
|
|
49
|
-
return _asyncToGenerator(function* () {
|
|
50
|
-
var isRetry = _arguments.length > 1 && _arguments[1] !== undefined ? _arguments[1] : false;
|
|
51
|
-
yield _this.client.decryptEventIfNeeded(event);
|
|
52
|
-
if (event.isDecryptionFailure()) {
|
|
53
|
-
if (!isRetry) {
|
|
54
|
-
_this.logger.warn("Decryption failed for event ".concat(event.getId(), ": ").concat(event.decryptionFailureReason, " will retry once only"));
|
|
55
|
-
// retry after 1 second. After this we give up.
|
|
56
|
-
setTimeout(() => void _this.consumeCallEncryptionEvent(event, true), 1000);
|
|
57
|
-
} else {
|
|
58
|
-
_this.logger.warn("Decryption failed for event ".concat(event.getId(), ": ").concat(event.decryptionFailureReason));
|
|
59
|
-
}
|
|
60
|
-
return;
|
|
61
|
-
} else if (isRetry) {
|
|
62
|
-
_this.logger.info("Decryption succeeded for event ".concat(event.getId(), " after retry"));
|
|
63
|
-
}
|
|
64
|
-
if (event.getType() !== EventType.CallEncryptionKeysPrefix) return Promise.resolve();
|
|
65
|
-
if (!_this.room) {
|
|
66
|
-
_this.logger.error("Got room state event for unknown room ".concat(event.getRoomId(), "!"));
|
|
67
|
-
return Promise.resolve();
|
|
68
|
-
}
|
|
69
|
-
_this.onEncryptionEvent(event);
|
|
70
|
-
})();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** implements {@link IKeyTransport#sendKey} */
|
|
74
|
-
sendKey(keyBase64Encoded, index, members) {
|
|
75
|
-
var _this2 = this;
|
|
76
|
-
return _asyncToGenerator(function* () {
|
|
77
|
-
// members not used in room transports as the keys are sent to all room members
|
|
78
|
-
var content = {
|
|
79
|
-
keys: [{
|
|
80
|
-
index: index,
|
|
81
|
-
key: keyBase64Encoded
|
|
82
|
-
}],
|
|
83
|
-
device_id: _this2.client.getDeviceId(),
|
|
84
|
-
call_id: "",
|
|
85
|
-
sent_ts: Date.now()
|
|
86
|
-
};
|
|
87
|
-
try {
|
|
88
|
-
yield _this2.client.sendEvent(_this2.room.roomId, EventType.CallEncryptionKeysPrefix, content);
|
|
89
|
-
} catch (error) {
|
|
90
|
-
_this2.logger.error("Failed to send call encryption keys", error);
|
|
91
|
-
var matrixError = error;
|
|
92
|
-
if (matrixError.event) {
|
|
93
|
-
// cancel the pending event: we'll just generate a new one with our latest
|
|
94
|
-
// keys when we resend
|
|
95
|
-
_this2.client.cancelPendingEvent(matrixError.event);
|
|
96
|
-
}
|
|
97
|
-
throw error;
|
|
98
|
-
}
|
|
99
|
-
})();
|
|
100
|
-
}
|
|
101
|
-
onEncryptionEvent(event) {
|
|
102
|
-
var userId = event.getSender();
|
|
103
|
-
var content = event.getContent();
|
|
104
|
-
var deviceId = content["device_id"];
|
|
105
|
-
var callId = content["call_id"];
|
|
106
|
-
if (!userId) {
|
|
107
|
-
this.logger.warn("Received m.call.encryption_keys with no userId: callId=".concat(callId));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// We currently only handle callId = "" (which is the default for room scoped calls)
|
|
112
|
-
if (callId !== "") {
|
|
113
|
-
this.logger.warn("Received m.call.encryption_keys with unsupported callId: userId=".concat(userId, ", deviceId=").concat(deviceId, ", callId=").concat(callId));
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (!Array.isArray(content.keys)) {
|
|
117
|
-
this.logger.warn("Received m.call.encryption_keys where keys wasn't an array: callId=".concat(callId));
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (userId === this.client.getUserId() && deviceId === this.client.getDeviceId()) {
|
|
121
|
-
// We store our own sender key in the same set along with keys from others, so it's
|
|
122
|
-
// important we don't allow our own keys to be set by one of these events (apart from
|
|
123
|
-
// the fact that we don't need it anyway because we already know our own keys).
|
|
124
|
-
this.logger.info("Ignoring our own keys event");
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
this.statistics.counters.roomEventEncryptionKeysReceived += 1;
|
|
128
|
-
var age = Date.now() - (typeof content.sent_ts === "number" ? content.sent_ts : event.getTs());
|
|
129
|
-
this.statistics.totals.roomEventEncryptionKeysReceivedTotalAge += age;
|
|
130
|
-
for (var key of content.keys) {
|
|
131
|
-
if (!key) {
|
|
132
|
-
this.logger.info("Ignoring false-y key in keys event");
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
var encryptionKey = key.key;
|
|
136
|
-
var encryptionKeyIndex = key.index;
|
|
137
|
-
if (!encryptionKey || encryptionKeyIndex === undefined || encryptionKeyIndex === null || callId === undefined || callId === null || typeof deviceId !== "string" || typeof callId !== "string" || typeof encryptionKey !== "string" || typeof encryptionKeyIndex !== "number") {
|
|
138
|
-
this.logger.warn("Malformed call encryption_key: userId=".concat(userId, ", deviceId=").concat(deviceId, ", encryptionKeyIndex=").concat(encryptionKeyIndex, " callId=").concat(callId));
|
|
139
|
-
} else {
|
|
140
|
-
this.logger.debug("onCallEncryption userId=".concat(userId, ":").concat(deviceId, " encryptionKeyIndex=").concat(encryptionKeyIndex, " age=").concat(age, "ms"));
|
|
141
|
-
this.emit(KeyTransportEvents.ReceivedKeys,
|
|
142
|
-
// Using `${userId}:${deviceId}` makes no sense (but works). It does not matter since the RoomKeyTransport is deprecated
|
|
143
|
-
{
|
|
144
|
-
userId,
|
|
145
|
-
deviceId,
|
|
146
|
-
memberId: "".concat(userId, ":").concat(deviceId)
|
|
147
|
-
}, encryptionKey, encryptionKeyIndex, event.getTs());
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
//# sourceMappingURL=RoomKeyTransport.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RoomKeyTransport.js","names":["EventType","logger","rootLogger","KeyTransportEvents","TypedEventEmitter","RoomEvent","RoomKeyTransport","setParentLogger","parentLogger","getChild","constructor","room","client","statistics","_defineProperty","start","on","Timeline","ev","consumeCallEncryptionEvent","stop","off","event","_arguments","arguments","_this","_asyncToGenerator","isRetry","length","undefined","decryptEventIfNeeded","isDecryptionFailure","warn","concat","getId","decryptionFailureReason","setTimeout","info","getType","CallEncryptionKeysPrefix","Promise","resolve","error","getRoomId","onEncryptionEvent","sendKey","keyBase64Encoded","index","members","_this2","content","keys","key","device_id","getDeviceId","call_id","sent_ts","Date","now","sendEvent","roomId","matrixError","cancelPendingEvent","userId","getSender","getContent","deviceId","callId","Array","isArray","getUserId","counters","roomEventEncryptionKeysReceived","age","getTs","totals","roomEventEncryptionKeysReceivedTotalAge","encryptionKey","encryptionKeyIndex","debug","emit","ReceivedKeys","memberId"],"sources":["../../src/matrixrtc/RoomKeyTransport.ts"],"sourcesContent":["/*\nCopyright 2025 The Matrix.org Foundation C.I.C.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nimport type { MatrixClient } from \"../client.ts\";\nimport { type EncryptionKeysEventContent, type ParticipantDeviceInfo, type Statistics } from \"./types.ts\";\nimport { EventType } from \"../@types/event.ts\";\nimport { type MatrixError } from \"../http-api/errors.ts\";\nimport { logger as rootLogger, type Logger } from \"../logger.ts\";\nimport { KeyTransportEvents, type KeyTransportEventsHandlerMap, type IKeyTransport } from \"./IKeyTransport.ts\";\nimport { type MatrixEvent } from \"../models/event.ts\";\nimport { TypedEventEmitter } from \"../models/typed-event-emitter.ts\";\nimport { type Room, RoomEvent } from \"../models/room.ts\";\n\n/**\n * @deprecated This is depreacted and not used anymore. use the ToDeviceTransport\n */\nexport class RoomKeyTransport\n extends TypedEventEmitter<KeyTransportEvents, KeyTransportEventsHandlerMap>\n implements IKeyTransport\n{\n private logger: Logger = rootLogger;\n public setParentLogger(parentLogger: Logger): void {\n this.logger = parentLogger.getChild(`[RoomKeyTransport]`);\n }\n public constructor(\n private room: Pick<Room, \"on\" | \"off\" | \"roomId\">,\n private client: Pick<\n MatrixClient,\n \"sendEvent\" | \"getDeviceId\" | \"getUserId\" | \"cancelPendingEvent\" | \"decryptEventIfNeeded\"\n >,\n private statistics: Statistics,\n parentLogger?: Logger,\n ) {\n super();\n this.setParentLogger(parentLogger ?? rootLogger);\n }\n public start(): void {\n this.room.on(RoomEvent.Timeline, (ev) => void this.consumeCallEncryptionEvent(ev));\n }\n public stop(): void {\n this.room.off(RoomEvent.Timeline, (ev) => void this.consumeCallEncryptionEvent(ev));\n }\n\n private async consumeCallEncryptionEvent(event: MatrixEvent, isRetry = false): Promise<void> {\n await this.client.decryptEventIfNeeded(event);\n\n if (event.isDecryptionFailure()) {\n if (!isRetry) {\n this.logger.warn(\n `Decryption failed for event ${event.getId()}: ${event.decryptionFailureReason} will retry once only`,\n );\n // retry after 1 second. After this we give up.\n setTimeout(() => void this.consumeCallEncryptionEvent(event, true), 1000);\n } else {\n this.logger.warn(`Decryption failed for event ${event.getId()}: ${event.decryptionFailureReason}`);\n }\n return;\n } else if (isRetry) {\n this.logger.info(`Decryption succeeded for event ${event.getId()} after retry`);\n }\n\n if (event.getType() !== EventType.CallEncryptionKeysPrefix) return Promise.resolve();\n\n if (!this.room) {\n this.logger.error(`Got room state event for unknown room ${event.getRoomId()}!`);\n return Promise.resolve();\n }\n\n this.onEncryptionEvent(event);\n }\n\n /** implements {@link IKeyTransport#sendKey} */\n public async sendKey(keyBase64Encoded: string, index: number, members: ParticipantDeviceInfo[]): Promise<void> {\n // members not used in room transports as the keys are sent to all room members\n const content: EncryptionKeysEventContent = {\n keys: [\n {\n index: index,\n key: keyBase64Encoded,\n },\n ],\n device_id: this.client.getDeviceId()!,\n call_id: \"\",\n sent_ts: Date.now(),\n };\n\n try {\n await this.client.sendEvent(this.room.roomId, EventType.CallEncryptionKeysPrefix, content);\n } catch (error) {\n this.logger.error(\"Failed to send call encryption keys\", error);\n const matrixError = error as MatrixError;\n if (matrixError.event) {\n // cancel the pending event: we'll just generate a new one with our latest\n // keys when we resend\n this.client.cancelPendingEvent(matrixError.event);\n }\n throw error;\n }\n }\n\n public onEncryptionEvent(event: MatrixEvent): void {\n const userId = event.getSender();\n const content = event.getContent<EncryptionKeysEventContent>();\n\n const deviceId = content[\"device_id\"];\n const callId = content[\"call_id\"];\n\n if (!userId) {\n this.logger.warn(`Received m.call.encryption_keys with no userId: callId=${callId}`);\n return;\n }\n\n // We currently only handle callId = \"\" (which is the default for room scoped calls)\n if (callId !== \"\") {\n this.logger.warn(\n `Received m.call.encryption_keys with unsupported callId: userId=${userId}, deviceId=${deviceId}, callId=${callId}`,\n );\n return;\n }\n\n if (!Array.isArray(content.keys)) {\n this.logger.warn(`Received m.call.encryption_keys where keys wasn't an array: callId=${callId}`);\n return;\n }\n\n if (userId === this.client.getUserId() && deviceId === this.client.getDeviceId()) {\n // We store our own sender key in the same set along with keys from others, so it's\n // important we don't allow our own keys to be set by one of these events (apart from\n // the fact that we don't need it anyway because we already know our own keys).\n this.logger.info(\"Ignoring our own keys event\");\n return;\n }\n\n this.statistics.counters.roomEventEncryptionKeysReceived += 1;\n const age = Date.now() - (typeof content.sent_ts === \"number\" ? content.sent_ts : event.getTs());\n this.statistics.totals.roomEventEncryptionKeysReceivedTotalAge += age;\n\n for (const key of content.keys) {\n if (!key) {\n this.logger.info(\"Ignoring false-y key in keys event\");\n continue;\n }\n\n const encryptionKey = key.key;\n const encryptionKeyIndex = key.index;\n\n if (\n !encryptionKey ||\n encryptionKeyIndex === undefined ||\n encryptionKeyIndex === null ||\n callId === undefined ||\n callId === null ||\n typeof deviceId !== \"string\" ||\n typeof callId !== \"string\" ||\n typeof encryptionKey !== \"string\" ||\n typeof encryptionKeyIndex !== \"number\"\n ) {\n this.logger.warn(\n `Malformed call encryption_key: userId=${userId}, deviceId=${deviceId}, encryptionKeyIndex=${encryptionKeyIndex} callId=${callId}`,\n );\n } else {\n this.logger.debug(\n `onCallEncryption userId=${userId}:${deviceId} encryptionKeyIndex=${encryptionKeyIndex} age=${age}ms`,\n );\n this.emit(\n KeyTransportEvents.ReceivedKeys,\n // Using `${userId}:${deviceId}` makes no sense (but works). It does not matter since the RoomKeyTransport is deprecated\n { userId, deviceId, memberId: `${userId}:${deviceId}` },\n encryptionKey,\n encryptionKeyIndex,\n event.getTs(),\n );\n }\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|