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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CallMembership.js","names":["MXID_PATTERN","deepCompare","slotDescriptionToId","slotIdToDescription","sha256","encodeUnpaddedBase64","DEFAULT_EXPIRE_DURATION","checkRtcMembershipData","data","errors","referenceUserId","_data$sticky_key","prefix","slot_id","push","split","length","member","user_id","test","device_id","id","application","type","includes","rtc_transports","undefined","Array","isArray","t","versions","every","v","sticky_key","msc4354_sticky_key","rel","event_id","rel_type","checkSessionsMembershipData","_data$focus_active","call_id","focus_active","foci_preferred","f","created_ts","scope","CallMembership","equal","a","b","membershipData","constructor","matrixEvent","rtcBackendIdentity","logger","_defineProperty","eventId","sender","ts","getId","getSender","getTs","Error","matrixEventData","getChild","concat","deviceId","computeRtcBackendIdentity","_asyncToGenerator","kind","computeRtcIdentityRaw","userId","memberId","membershipDataFromMatrixEvent","content","getContent","sessionErrors","rtcErrors","details","join","json","JSON","stringify","replaceAll","slotId","_this$logger2","compatibilityAdaptedId","_this$logger","info","callIntent","_this$logger3","intent","warn","slotDescription","applicationData","membershipID","_data$membershipID","createdTs","_data$created_ts","getAbsoluteExpiry","_data$expires","expires","getMsUntilExpiry","Date","now","isExpired","getTransport","oldestMembership","focus_selection","getFocusActive","transports"],"sources":["../../src/matrixrtc/CallMembership.ts"],"sourcesContent":["/*\nCopyright 2023 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 { MXID_PATTERN } from \"../models/room-member.ts\";\nimport { deepCompare } from \"../utils.ts\";\nimport { type LivekitFocusSelection } from \"./LivekitTransport.ts\";\nimport { slotDescriptionToId, slotIdToDescription, type SlotDescription } from \"./MatrixRTCSession.ts\";\nimport type { RTCCallIntent, Transport } from \"./types.ts\";\nimport { type MatrixEvent, type IContent } from \"../models/event.ts\";\nimport { type RelationType } from \"../@types/event.ts\";\nimport { sha256 } from \"../digest.ts\";\nimport { encodeUnpaddedBase64 } from \"../base64.ts\";\nimport { type Logger } from \"../logger.ts\";\n\n/**\n * The default duration in milliseconds that a membership is considered valid for.\n * Ordinarily the client responsible for the session will update the membership before it expires.\n * We use this duration as the fallback case where stale sessions are present for some reason.\n */\nexport const DEFAULT_EXPIRE_DURATION = 1000 * 60 * 60 * 4;\n\ntype CallScope = \"m.room\" | \"m.user\";\ntype Member = {\n user_id: string;\n device_id: string;\n /**\n * The id used on the media backend.\n * (With livekit this is the participant identity on the LK SFU)\n * This can be a UUID but right now it is `${this.matrixEventData.sender}:${data.device_id}`.\n */\n id: string;\n};\n\nexport interface RtcMembershipData {\n \"slot_id\": string;\n \"member\": Member;\n \"m.relates_to\"?: {\n event_id: string;\n rel_type: RelationType.Reference;\n };\n \"application\": {\n type: string;\n // other application specific keys\n [key: string]: unknown;\n };\n \"rtc_transports\": Transport[];\n \"versions\": string[];\n \"msc4354_sticky_key\"?: string;\n \"sticky_key\"?: string;\n}\n\nconst checkRtcMembershipData = (\n data: IContent,\n errors: string[],\n referenceUserId: string,\n): data is RtcMembershipData => {\n const prefix = \" - \";\n\n // required fields\n if (typeof data.slot_id !== \"string\") {\n errors.push(prefix + \"slot_id must be string\");\n } else {\n if (data.slot_id.split(\"#\").length !== 2) errors.push(prefix + 'slot_id must include exactly one \"#\"');\n }\n if (typeof data.member !== \"object\" || data.member === null) {\n errors.push(prefix + \"member must be an object\");\n } else {\n if (typeof data.member.user_id !== \"string\") errors.push(prefix + \"member.user_id must be string\");\n else if (!MXID_PATTERN.test(data.member.user_id)) errors.push(prefix + \"member.user_id must be a valid mxid\");\n // This is not what the spec enforces but there currently are no rules what power levels are required to\n // send a m.rtc.member event for a other user. So we add this check for simplicity and to avoid possible attacks until there\n // is a proper definition when this is allowed.\n else if (data.member.user_id !== referenceUserId) errors.push(prefix + \"member.user_id must match the sender\");\n if (typeof data.member.device_id !== \"string\") errors.push(prefix + \"member.device_id must be string\");\n if (typeof data.member.id !== \"string\") errors.push(prefix + \"member.id must be string\");\n }\n if (typeof data.application !== \"object\" || data.application === null) {\n errors.push(prefix + \"application must be an object\");\n } else {\n if (typeof data.application.type !== \"string\") {\n errors.push(prefix + \"application.type must be a string\");\n } else {\n if (data.application.type.includes(\"#\")) errors.push(prefix + 'application.type must not include \"#\"');\n }\n }\n if (data.rtc_transports === undefined || !Array.isArray(data.rtc_transports)) {\n errors.push(prefix + \"rtc_transports must be an array\");\n } else {\n // validate that each transport has at least a string 'type'\n for (const t of data.rtc_transports) {\n if (typeof t !== \"object\" || t === null || typeof (t as any).type !== \"string\") {\n errors.push(prefix + \"rtc_transports entries must be objects with a string type\");\n break;\n }\n }\n }\n if (data.versions === undefined || !Array.isArray(data.versions)) {\n errors.push(prefix + \"versions must be an array\");\n } else if (!data.versions.every((v) => typeof v === \"string\")) {\n errors.push(prefix + \"versions must be an array of strings\");\n }\n\n // optional fields\n if ((data.sticky_key ?? data.msc4354_sticky_key) === undefined) {\n errors.push(prefix + \"sticky_key or msc4354_sticky_key must be a defined\");\n }\n if (data.sticky_key !== undefined && typeof data.sticky_key !== \"string\") {\n errors.push(prefix + \"sticky_key must be a string\");\n }\n if (data.msc4354_sticky_key !== undefined && typeof data.msc4354_sticky_key !== \"string\") {\n errors.push(prefix + \"msc4354_sticky_key must be a string\");\n }\n if (\n data.sticky_key !== undefined &&\n data.msc4354_sticky_key !== undefined &&\n data.sticky_key !== data.msc4354_sticky_key\n ) {\n errors.push(prefix + \"sticky_key and msc4354_sticky_key must be equal if both are defined\");\n }\n if (data[\"m.relates_to\"] !== undefined) {\n const rel = data[\"m.relates_to\"] as RtcMembershipData[\"m.relates_to\"];\n if (typeof rel !== \"object\" || rel === null) {\n errors.push(prefix + \"m.relates_to must be an object if provided\");\n } else {\n if (typeof rel.event_id !== \"string\") errors.push(prefix + \"m.relates_to.event_id must be a string\");\n if (rel.rel_type !== \"m.reference\") errors.push(prefix + \"m.relates_to.rel_type must be m.reference\");\n }\n }\n\n return errors.length === 0;\n};\n\n/**\n * MSC4143 (MatrixRTC) session membership data.\n * Represents the `session` in the memberships section of an m.call.member event as it is on the wire.\n **/\nexport type SessionMembershipData = {\n /**\n * The RTC application defines the type of the RTC session.\n */\n \"application\": string;\n\n /**\n * The id of this session.\n * A session can never span over multiple rooms so this id is to distinguish between\n * multiple session in one room. A room wide session that is not associated with a user,\n * and therefore immune to creation race conflicts, uses the `call_id: \"\"`.\n */\n \"call_id\": string;\n\n /**\n * The Matrix device ID of this session. A single user can have multiple sessions on different devices.\n */\n \"device_id\": string;\n\n /**\n * The focus selection system this user/membership is using.\n */\n \"focus_active\": LivekitFocusSelection;\n\n /**\n * A list of possible foci this user knows about. One of them might be used based on the focus_active\n * selection system.\n */\n \"foci_preferred\": Transport[];\n\n /**\n * Optional field that contains the creation of the session. If it is undefined the creation\n * is the `origin_server_ts` of the event itself. For updates to the event this property tracks\n * the `origin_server_ts` of the initial join event.\n * - If it is undefined it can be interpreted as a \"Join\".\n * - If it is defined it can be interpreted as an \"Update\"\n */\n \"created_ts\"?: number;\n\n // Application specific data\n\n /**\n * If the `application` = `\"m.call\"` this defines if it is a room or user owned call.\n * There can always be one room scoped call but multiple user owned calls (breakout sessions)\n */\n \"scope\"?: CallScope;\n\n /**\n * Optionally we allow to define a delta to the `created_ts` that defines when the event is expired/invalid.\n * This should be set to multiple hours. The only reason it exist is to deal with failed delayed events.\n * (for example caused by a homeserver crashes)\n **/\n \"expires\"?: number;\n\n /**\n * The intent of the call from the perspective of this user. This may be an audio call, video call or\n * something else.\n */\n \"m.call.intent\"?: RTCCallIntent;\n /**\n * The sticky key in case of a sticky event. This string encodes the application + device_id indicating the used slot + device.\n */\n \"msc4354_sticky_key\"?: string;\n\n /**\n * The id used on the media backend.\n * (With livekit this is the participant identity on the LK SFU)\n * This can be a UUID but right now it is `${this.matrixEventData.sender}:${data.device_id}`.\n *\n * It is compleatly valid to not set this field. Other clients will treat `undefined` as `${this.matrixEventData.sender}:${data.device_id}`\n */\n \"membershipID\"?: string;\n};\n\nconst checkSessionsMembershipData = (data: IContent, errors: string[]): data is SessionMembershipData => {\n const prefix = \" - \";\n if (typeof data.device_id !== \"string\") errors.push(prefix + \"device_id must be string\");\n if (typeof data.call_id !== \"string\") errors.push(prefix + \"call_id must be string\");\n if (typeof data.application !== \"string\") errors.push(prefix + \"application must be a string\");\n if (typeof data.focus_active?.type !== \"string\") errors.push(prefix + \"focus_active.type must be a string\");\n if (data.focus_active === undefined) {\n errors.push(prefix + \"focus_active has an invalid type\");\n }\n if (\n data.foci_preferred !== undefined &&\n !(\n Array.isArray(data.foci_preferred) &&\n data.foci_preferred.every(\n (f: Transport) => typeof f === \"object\" && f !== null && typeof f.type === \"string\",\n )\n )\n ) {\n errors.push(prefix + \"foci_preferred must be an array of transport objects\");\n }\n // optional parameters\n if (data.created_ts !== undefined && typeof data.created_ts !== \"number\") {\n errors.push(prefix + \"created_ts must be number\");\n }\n\n // application specific data (we first need to check if they exist)\n if (data.scope !== undefined && typeof data.scope !== \"string\") errors.push(prefix + \"scope must be string\");\n\n if (data[\"m.call.intent\"] !== undefined && typeof data[\"m.call.intent\"] !== \"string\") {\n errors.push(prefix + \"m.call.intent must be a string\");\n }\n\n return errors.length === 0;\n};\n\ntype MembershipData = { kind: \"rtc\"; data: RtcMembershipData } | { kind: \"session\"; data: SessionMembershipData };\n// TODO: Rename to RtcMembership once we removed the legacy SessionMembership from this file.\nexport class CallMembership {\n public static equal(a?: CallMembership, b?: CallMembership): boolean {\n return deepCompare(a?.membershipData, b?.membershipData);\n }\n\n private logger?: Logger;\n /** The parsed data from the Matrix event.\n * To access checked eventId and sender from the matrixEvent.\n * Class construction will fail if these values cannot get obtained. */\n private readonly matrixEventData: { eventId: string; sender: string; ts: number };\n\n public constructor(\n /** The required parts of the Matrix event that this membership is based on */\n matrixEvent: Pick<MatrixEvent, \"getId\" | \"getSender\" | \"getTs\">,\n\n /**\n * The type checked membership data {data: (content of the matrix event), kind: (type hint)}\n *\n */\n private readonly membershipData: MembershipData,\n\n /**\n *\n * Anonymized identity to use with the RTC backend.\n *\n * The rtcBackendIdentity is a hashed version of all the identity parts:\n * `sha256(${this.userId}|${this.deviceId}|${this.memberId})`\n *\n * It is used to anonymize the identity of the user in the RTC backend.\n */\n public readonly rtcBackendIdentity: string,\n /**\n * The constructor will automatically create a properly tagged child logger instance.\n */\n logger?: Logger,\n ) {\n const [eventId, sender, ts] = [matrixEvent.getId(), matrixEvent.getSender(), matrixEvent.getTs()];\n if (eventId === undefined) throw new Error(\"parentEvent is missing eventId field\");\n if (sender === undefined) throw new Error(\"parentEvent is missing sender field\");\n\n this.matrixEventData = { eventId, sender, ts };\n\n this.logger = logger?.getChild(`[CallMembership ${sender}:${this.deviceId}]`);\n }\n\n /**\n * sha256(`${this.userId}|${this.deviceId}|${this.memberId}`) for sticky events (kind = rtc)\n * `${this.userId}:${this.deviceId}` for state events (kind = session)\n */\n public static async computeRtcBackendIdentity(\n matrixEvent: Pick<MatrixEvent, \"getSender\">,\n membershipData: MembershipData,\n ): Promise<string> {\n const { kind, data } = membershipData;\n switch (kind) {\n case \"rtc\": {\n return CallMembership.computeRtcIdentityRaw(data.member.user_id, data.member.device_id, data.member.id);\n }\n case \"session\":\n return `${matrixEvent.getSender()}:${data.device_id}`;\n }\n }\n\n public static async computeRtcIdentityRaw(userId: string, deviceId: string, memberId: string): Promise<string> {\n return encodeUnpaddedBase64(await sha256(`${userId}|${deviceId}|${memberId}`));\n }\n\n public static membershipDataFromMatrixEvent(matrixEvent: MatrixEvent): MembershipData {\n const [eventId, sender, content] = [matrixEvent.getId(), matrixEvent.getSender(), matrixEvent.getContent()];\n\n if (eventId === undefined) throw new Error(\"parentEvent is missing eventId field\");\n if (sender === undefined) throw new Error(\"parentEvent is missing sender field\");\n\n const sessionErrors: string[] = [];\n const rtcErrors: string[] = [];\n if (checkSessionsMembershipData(content, sessionErrors)) {\n return { kind: \"session\", data: content };\n } else if (checkRtcMembershipData(content, rtcErrors, sender)) {\n return { kind: \"rtc\", data: content };\n } else {\n const details =\n sessionErrors.length < rtcErrors.length\n ? `Does not match MSC4143 m.call.member:\\n${sessionErrors.join(\"\\n\")}\\n\\n`\n : `Does not match MSC4143 m.rtc.member:\\n${rtcErrors.join(\"\\n\")}\\n\\n`;\n const json = \"\\nevent:\\n\" + JSON.stringify(content).replaceAll('\"', \"'\");\n throw Error(`unknown CallMembership data.\\n` + details + json);\n }\n }\n\n /** @deprecated use userId instead */\n public get sender(): string {\n return this.userId;\n }\n public get userId(): string {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.member.user_id;\n case \"session\":\n default:\n return this.matrixEventData.sender;\n }\n }\n\n public get eventId(): string {\n return this.matrixEventData.eventId;\n }\n\n /**\n * The ID of the MatrixRTC slot that this membership belongs to (format `{application}#{id}`).\n * This is computed in case SessionMembershipData is used.\n */\n public get slotId(): string {\n const { kind, data } = this.membershipData;\n if (data.application === \"m.call\") {\n switch (kind) {\n case \"rtc\":\n return data.slot_id;\n case \"session\":\n default: {\n const [application, id] = [this.application, data.call_id];\n\n // INFO_SLOT_ID_LEGACY_CASE (search for all occurances of this INFO to get the full picture)\n // The spec got changed to use `\"ROOM\"` instead of `\"\"` empyt string for the implicit default call.\n // State events still are sent with `\"\"` however. To find other events that should end up in the same call,\n // we use the slotId.\n // Since the CallMembership is the public representation of a rtc.member event, we just pretend it is a\n // \"ROOM\" slotId/call_id.\n // This makes all the remote members work with just this simple trick.\n //\n // We of course now need to be careful when sending legacy events (state events)\n // They get a slotDescription containing \"ROOM\" since this is what we use starting at the time this comment\n // is commited.\n //\n // See the Other INFO_SLOT_ID_LEGACY_CASE comments to see where we revert back to \"\" just before sending the event.\n let compatibilityAdaptedId: string;\n if (id === \"\") {\n compatibilityAdaptedId = \"ROOM\";\n this.logger?.info(\"use slotId compat hack emptyString -> ROOM\");\n } else {\n compatibilityAdaptedId = id;\n }\n return slotDescriptionToId({\n application,\n id: compatibilityAdaptedId,\n });\n }\n }\n }\n\n this.logger?.info(\"NOT using slotId compat hack emptyString -> ROOM\");\n // This is what the function should look like for any other application that did not\n // go through a `\"\"`=> `\"ROOM\"` rename\n switch (kind) {\n case \"rtc\":\n return data.slot_id;\n case \"session\":\n default: {\n const [application, id] = [this.application, data.call_id];\n return slotDescriptionToId({ application, id });\n }\n }\n }\n\n public get deviceId(): string {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.member.device_id;\n case \"session\":\n default:\n return data.device_id;\n }\n }\n\n public get callIntent(): RTCCallIntent | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\": {\n const intent = data.application[\"m.call.intent\"];\n if (typeof intent === \"string\") {\n return intent;\n }\n this.logger?.warn(\"RTC membership has invalid m.call.intent\");\n return undefined;\n }\n case \"session\":\n default:\n return data[\"m.call.intent\"];\n }\n }\n\n /**\n * Parsed `slot_id` (format `{application}#{id}`) into its components (application and id).\n */\n public get slotDescription(): SlotDescription {\n return slotIdToDescription(this.slotId);\n }\n\n public get application(): string {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.application.type;\n case \"session\":\n default:\n return data.application;\n }\n }\n public get applicationData(): { type: string; [key: string]: unknown } {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.application;\n case \"session\":\n default:\n return { \"type\": data.application, \"m.call.intent\": data[\"m.call.intent\"] };\n }\n }\n\n /** @deprecated scope is not used and will be removed in future versions. replaced by application specific types.*/\n public get scope(): CallScope | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return undefined;\n case \"session\":\n default:\n return data.scope;\n }\n }\n /**\n * @deprecated renamed to `memberId`\n */\n public get membershipID(): string {\n return this.memberId;\n }\n\n /**\n * This computes the membership ID for the membership.\n * For the sticky event based rtcSessionData this is trivial it is `member.id`.\n * This is not supposed to be used to identity on an rtc backend. This is just a nouance for\n * a generated (sha256) anonymised identity. Only send `rtcBackendIdentity` to any rtc backend service.\n *\n * For the legacy sessionMemberEvents it is a bit more complex. Here we sometimes do not have this data\n * in the event content and we expected the SFU and the client to use `${this.matrixEventData.sender}:${data.device_id}`.\n *\n * So if there is no membershipID we use the hard coded jwt id default (`${this.matrixEventData.sender}:${data.device_id}`)\n * value (used until version 0.16.0)\n *\n * It is also possible for a session event to set a custom membershipID. in that case this will be used.\n */\n public get memberId(): string {\n // the createdTs behaves equivalent to the membershipID.\n // we only need the field for the legacy member events where we needed to update them\n // synapse ignores sending state events if they have the same content.\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.member.id;\n case \"session\":\n default:\n return (\n // best case we have a client already publishing the right custom membershipId\n data.membershipID ??\n // alternativly we use the hard coded jwt id defuatl value (used until version 0.16.0)\n `${this.matrixEventData.sender}:${data.device_id}`\n );\n }\n }\n\n public createdTs(): number {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n // TODO we need to read the referenced (relation) event if available to get the real created_ts\n return this.matrixEventData.ts;\n case \"session\":\n default:\n return data.created_ts ?? this.matrixEventData.ts;\n }\n }\n\n /**\n * Gets the absolute expiry timestamp of the membership.\n * @returns The absolute expiry time of the membership as a unix timestamp in milliseconds or undefined if not applicable\n */\n public getAbsoluteExpiry(): number | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return undefined;\n case \"session\":\n default:\n // TODO: calculate this from the MatrixRTCSession join configuration directly\n return this.createdTs() + (data.expires ?? DEFAULT_EXPIRE_DURATION);\n }\n }\n\n /**\n * @returns The number of milliseconds until the membership expires or undefined if applicable\n */\n public getMsUntilExpiry(): number | undefined {\n const { kind } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return undefined;\n case \"session\":\n default:\n // Assume that local clock is sufficiently in sync with other clocks in the distributed system.\n // We used to try and adjust for the local clock being skewed, but there are cases where this is not accurate.\n // The current implementation allows for the local clock to be -infinity to +MatrixRTCSession.MEMBERSHIP_EXPIRY_TIME/2\n return this.getAbsoluteExpiry()! - Date.now();\n }\n }\n\n /**\n * @returns true if the membership has expired, otherwise false\n */\n public isExpired(): boolean {\n const { kind } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return false;\n case \"session\":\n default:\n return this.getMsUntilExpiry()! <= 0;\n }\n }\n\n /**\n * ## RTC Membership\n * Gets the primary transport to use for this RTC membership (m.rtc.member).\n * This will return the primary transport that is used by this call membership to publish their media.\n * Directly relates to the `rtc_transports` field.\n *\n * ## Legacy session membership\n * In case of a legacy session membership (m.call.member) this will return the selected transport where\n * media is published. How this selection happens depends on the `focus_active` field of the session membership.\n * If the `focus_selection` is `oldest_membership` this will return the transport of the oldest membership\n * in the room (based on the `created_ts` field of the session membership).\n * If the `focus_selection` is `multi_sfu` it will return the first transport of the `foci_preferred` list.\n * (`multi_sfu` is equivalent to how `m.rtc.member` `rtc_transports` work).\n * @param oldestMembership For backwards compatibility with session membership (legacy). Unused in case of RTC membership.\n * Always required to make the consumer not care if it deals with RTC or session memberships.\n * @returns The transport this membership uses to publish media or undefined if no transport is available.\n */\n public getTransport(oldestMembership: CallMembership): Transport | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.rtc_transports[0];\n case \"session\":\n switch (data.focus_active.focus_selection) {\n case \"multi_sfu\":\n return data.foci_preferred[0];\n case \"oldest_membership\":\n if (CallMembership.equal(this, oldestMembership)) return data.foci_preferred[0];\n if (oldestMembership !== undefined) return oldestMembership.getTransport(oldestMembership);\n break;\n }\n }\n return undefined;\n }\n\n /**\n * The focus_active filed of the session membership (m.call.member).\n * @deprecated focus_active is not used and will be removed in future versions.\n */\n public getFocusActive(): LivekitFocusSelection | undefined {\n const { kind, data } = this.membershipData;\n if (kind === \"session\") return data.focus_active;\n return undefined;\n }\n /**\n * The value of the `rtc_transports` field for RTC memberships (m.rtc.member).\n * Or the value of the `foci_preferred` field for legacy session memberships (m.call.member).\n */\n public get transports(): Transport[] {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.rtc_transports;\n case \"session\":\n default:\n return data.foci_preferred;\n }\n }\n public get kind(): MembershipData[\"kind\"] {\n return this.membershipData.kind;\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"CallMembership.js","names":["deepCompare","logger","computeSlotId","slotIdToDescription","checkRtcMembershipData","computeRtcIdentityRaw","checkSessionsMembershipData","MatrixRTCMembershipParseError","EventType","DEFAULT_EXPIRE_DURATION","MembershipKind","CallMembership","membershipDataFromMatrixEvent","matrixEvent","sender","getSender","evType","getType","data","getContent","undefined","Error","RTCMembership","kind","RTC","GroupCallMemberPrefix","Session","concat","ex","debug","parseFromEvent","_this","_asyncToGenerator","membershipData","rtcBackendIdentity","member","user_id","device_id","id","equal","a","b","constructor","_defineProperty","eventId","getId","getChild","deviceId","matrixEventData","userId","slotId","_this$logger2","application","slot_id","call_id","compatibilityAdaptedId","_this$logger","info","callIntent","intent","applicationData","warn","slotDescription","slice","type","length","scope","memberId","_data$membershipID","membershipID","createdTs","_data$created_ts","getTs","created_ts","getAbsoluteExpiry","_data$expires","expires","getMsUntilExpiry","absExpiry","Date","now","isExpired","getTransport","oldestMembership","rtc_transports","focus_active","focus_selection","foci_preferred","transports"],"sources":["../../src/matrixrtc/CallMembership.ts"],"sourcesContent":["/*\nCopyright 2023-2026 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 { deepCompare } from \"../utils.ts\";\nimport { type RTCCallIntent, type Transport, type SlotDescription } from \"./types.ts\";\nimport { type MatrixEvent } from \"../models/event.ts\";\nimport { type Logger, logger } from \"../logger.ts\";\nimport { computeSlotId, slotIdToDescription } from \"./utils.ts\";\nimport {\n checkRtcMembershipData,\n computeRtcIdentityRaw,\n type RtcMembershipData,\n checkSessionsMembershipData,\n type SessionMembershipData,\n MatrixRTCMembershipParseError,\n} from \"./membershipData/index.ts\";\nimport { EventType } from \"../@types/event.ts\";\n\n/**\n * The default duration in milliseconds that a membership is considered valid for.\n * Ordinarily the client responsible for the session will update the membership before it expires.\n * We use this duration as the fallback case where stale sessions are present for some reason.\n */\nexport const DEFAULT_EXPIRE_DURATION = 1000 * 60 * 60 * 4;\n\n/**\n * Describes the source event type that provided the membership data.\n */\nenum MembershipKind {\n /**\n * The modern MSC4143 format event.\n */\n RTC = \"rtc\",\n /**\n * The legacy call event type.\n */\n Session = \"session\",\n}\n\ntype MembershipData =\n | { kind: MembershipKind.RTC; data: RtcMembershipData }\n | { kind: MembershipKind.Session; data: SessionMembershipData };\n\ntype LimitedEvent = Pick<MatrixEvent, \"getId\" | \"getSender\" | \"getTs\" | \"getType\" | \"getContent\">;\n// TODO: Rename to RtcMembership once we removed the legacy SessionMembership is removed, to avoid confusion.\nexport class CallMembership {\n /**\n * Parse the membershipdata from a call membership event.\n * @param matrixEvent The Matrix event to read.\n * @returns MembershipData in either MembershipKind.RTC or MembershipKind.Session format.\n * @throws If the content is neither format.\n */\n public static membershipDataFromMatrixEvent(matrixEvent: LimitedEvent): MembershipData {\n const sender = matrixEvent.getSender();\n const evType = matrixEvent.getType();\n const data = matrixEvent.getContent();\n if (sender === undefined) throw new Error(\"matrixEvent is missing sender field\");\n try {\n // Event types are strictly checked here.\n if (evType === EventType.RTCMembership && checkRtcMembershipData(data, sender)) {\n return { kind: MembershipKind.RTC, data };\n } else if (evType === EventType.GroupCallMemberPrefix && checkSessionsMembershipData(data)) {\n return { kind: MembershipKind.Session, data };\n } else {\n throw Error(`'${evType} is not a known call membership type`);\n }\n } catch (ex) {\n if (ex instanceof MatrixRTCMembershipParseError) {\n logger.debug(\"CallMembership.MatrixRTCMembershipParseError provided invalid data\", data);\n }\n throw ex;\n }\n }\n\n /**\n * Parse the contents of a MatrixEvent and create a CallMembership instance.\n * @param matrixEvent The Matrix event to read.\n */\n public static async parseFromEvent(matrixEvent: LimitedEvent): Promise<CallMembership> {\n const membershipData: MembershipData = this.membershipDataFromMatrixEvent(matrixEvent);\n const rtcBackendIdentity =\n membershipData.kind === MembershipKind.RTC\n ? await computeRtcIdentityRaw(\n membershipData.data.member.user_id,\n membershipData.data.member.device_id,\n membershipData.data.member.id,\n )\n : `${matrixEvent.getSender()}:${membershipData.data.device_id}`;\n return new CallMembership(matrixEvent, membershipData, rtcBackendIdentity);\n }\n\n public static equal(a?: CallMembership, b?: CallMembership): boolean {\n return deepCompare(a?.membershipData, b?.membershipData);\n }\n\n private logger: Logger;\n\n /** The parsed data from the Matrix event.\n * To access checked eventId and sender from the matrixEvent.\n * Class construction will fail if these values cannot get obtained. */\n private readonly matrixEventData: { eventId: string; sender: string };\n\n /**\n * Use `parseFromEvent`.\n * Constructor should only be used by tests.\n * @private\n * @param matrixEvent\n * @param membershipData\n * @param rtcBackendIdentity\n */\n public constructor(\n /** The Matrix event that this membership is based on */\n private readonly matrixEvent: LimitedEvent,\n private readonly membershipData: MembershipData,\n public readonly rtcBackendIdentity: string,\n ) {\n const eventId = matrixEvent.getId();\n const sender = matrixEvent.getSender();\n\n if (eventId === undefined) throw new Error(\"parentEvent is missing eventId field\");\n if (sender === undefined) throw new Error(\"parentEvent is missing sender field\");\n\n this.logger = logger.getChild(`[CallMembership ${sender}:${this.deviceId}]`);\n this.matrixEventData = { eventId, sender };\n }\n\n /** @deprecated use userId instead */\n public get sender(): string {\n return this.userId;\n }\n\n public get userId(): string {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return data.member.user_id;\n case MembershipKind.Session:\n default:\n return this.matrixEventData.sender;\n }\n }\n\n public get eventId(): string {\n return this.matrixEventData.eventId;\n }\n\n /**\n * The ID of the MatrixRTC slot that this membership belongs to (format `{application}#{id}`).\n * This is computed in case SessionMembershipData is used.\n */\n public get slotId(): string {\n const { kind, data } = this.membershipData;\n if (data.application === \"m.call\") {\n switch (kind) {\n case MembershipKind.RTC:\n return data.slot_id;\n case MembershipKind.Session:\n default: {\n const [application, id] = [data.application, data.call_id];\n\n // INFO_SLOT_ID_LEGACY_CASE (search for all occurances of this INFO to get the full picture)\n // The spec got changed to use `\"ROOM\"` instead of `\"\"` empyt string for the implicit default call.\n // State events still are sent with `\"\"` however. To find other events that should end up in the same call,\n // we use the slotId.\n // Since the CallMembership is the public representation of a rtc.member event, we just pretend it is a\n // \"ROOM\" slotId/call_id.\n // This makes all the remote members work with just this simple trick.\n //\n // We of course now need to be careful when sending legacy events (state events)\n // They get a slotDescription containing \"ROOM\" since this is what we use starting at the time this comment\n // is commited.\n //\n // See the Other INFO_SLOT_ID_LEGACY_CASE comments to see where we revert back to \"\" just before sending the event.\n let compatibilityAdaptedId: string;\n if (id === \"\") {\n compatibilityAdaptedId = \"ROOM\";\n this.logger?.info(\"use slotId compat hack emptyString -> ROOM\");\n } else {\n compatibilityAdaptedId = id;\n }\n return computeSlotId({\n application,\n id: compatibilityAdaptedId,\n });\n }\n }\n }\n\n this.logger?.info(\"NOT using slotId compat hack emptyString -> ROOM\");\n // This is what the function should look like for any other application that did not\n // go through a `\"\"`=> `\"ROOM\"` rename\n switch (kind) {\n case MembershipKind.RTC:\n return data.slot_id;\n case MembershipKind.Session:\n default:\n return computeSlotId({ application: data.application, id: data.call_id });\n }\n }\n\n public get deviceId(): string {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return data.member.device_id;\n case MembershipKind.Session:\n default:\n return data.device_id;\n }\n }\n\n public get callIntent(): RTCCallIntent | undefined {\n const intent = this.applicationData[\"m.call.intent\"];\n if (typeof intent === \"string\") {\n return intent;\n }\n this.logger.warn(\"RTC membership has invalid m.call.intent\");\n return undefined;\n }\n\n /**\n * Parsed `slot_id` (format `{application}#{id}`) into its components (application and id).\n */\n public get slotDescription(): SlotDescription {\n const { kind, data } = this.membershipData;\n if (kind === MembershipKind.RTC) {\n const id = data.slot_id.slice(`${data.application.type}#`.length);\n return { application: data.application.type, id };\n }\n return slotIdToDescription(this.slotId);\n }\n\n /**\n * The application `type`.\n * @deprecated Use @see applicationData\n */\n public get application(): string {\n return this.applicationData.type;\n }\n\n /**\n * Information about the application being used for the RTC session.\n * May contain extra keys specific to the application.\n */\n public get applicationData(): { type: string; [key: string]: unknown } {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return data.application;\n case MembershipKind.Session:\n default:\n // SessionData does not have application data as such. We return specific\n // properties in use by other getters in this class, for compatibility.\n return { \"type\": data.application, \"m.call.intent\": data[\"m.call.intent\"] };\n }\n }\n\n /** @deprecated scope is not used and will be removed in future versions. replaced by application specific types.*/\n public get scope(): SessionMembershipData[\"scope\"] | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return undefined;\n case MembershipKind.Session:\n default:\n return data.scope;\n }\n }\n\n /**\n * This computes the membership ID for the membership.\n * For the sticky event based rtcSessionData this is trivial it is `member.id`.\n * This is not supposed to be used to identity on an rtc backend. This is just a nouance for\n * a generated (sha256) anonymised identity. Only send `rtcBackendIdentity` to any rtc backend service.\n *\n * For the legacy sessionMemberEvents it is a bit more complex. Here we sometimes do not have this data\n * in the event content and we expected the SFU and the client to use `${this.matrixEventData.sender}:${data.device_id}`.\n *\n * So if there is no membershipID we use the hard coded jwt id default (`${this.matrixEventData.sender}:${data.device_id}`)\n * value (used until version 0.16.0)\n *\n * It is also possible for a session event to set a custom membershipID. in that case this will be used.\n */\n public get memberId(): string {\n // the createdTs behaves equivalent to the membershipID.\n // we only need the field for the legacy member events where we needed to update them\n // synapse ignores sending state events if they have the same content.\n const { kind, data } = this.membershipData;\n switch (kind) {\n case \"rtc\":\n return data.member.id;\n case \"session\":\n return (\n // best case we have a client already publishing the right custom membershipId\n data.membershipID ??\n // alternativly we use the hard coded jwt id defuatl value (used until version 0.16.0)\n `${this.matrixEventData.sender}:${data.device_id}`\n );\n default:\n throw Error(\"Not possible to get memberID without knowing the membership event kind\");\n }\n }\n\n /**\n * @deprecated renamed to `memberId`\n */\n public get membershipID(): string {\n return this.memberId;\n }\n\n public createdTs(): number {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n // TODO we need to read the referenced (relation) event if available to get the real created_ts\n return this.matrixEvent.getTs();\n case MembershipKind.Session:\n default:\n return data.created_ts ?? this.matrixEvent.getTs();\n }\n }\n\n /**\n * Gets the absolute expiry timestamp of the membership.\n * @returns The absolute expiry time of the membership as a unix timestamp in milliseconds or undefined if not applicable\n */\n public getAbsoluteExpiry(): number | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return undefined;\n case MembershipKind.Session:\n default:\n // TODO: calculate this from the MatrixRTCSession join configuration directly\n return this.createdTs() + (data.expires ?? DEFAULT_EXPIRE_DURATION);\n }\n }\n\n /**\n * @returns The number of milliseconds until the membership expires or undefined if applicable\n * @deprecated Not used by RTC events.\n */\n public getMsUntilExpiry(): number | undefined {\n const { kind } = this.membershipData;\n if (kind === MembershipKind.Session) {\n const absExpiry = this.getAbsoluteExpiry();\n if (absExpiry) {\n // Assume that local clock is sufficiently in sync with other clocks in the distributed system.\n // We used to try and adjust for the local clock being skewed, but there are cases where this is not accurate.\n // The current implementation allows for the local clock to be -infinity to +MatrixRTCSession.MEMBERSHIP_EXPIRY_TIME/2\n return absExpiry - Date.now();\n }\n }\n return undefined;\n }\n\n /**\n * @returns true if the membership has expired, otherwise false\n */\n public isExpired(): boolean {\n const { kind } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return false;\n case MembershipKind.Session:\n default:\n return this.getMsUntilExpiry()! <= 0;\n }\n }\n\n /**\n * ## RTC Membership\n * Gets the primary transport to use for this RTC membership (m.rtc.member).\n * This will return the primary transport that is used by this call membership to publish their media.\n * Directly relates to the `rtc_transports` field.\n *\n * ## Legacy session membership\n * In case of a legacy session membership (m.call.member) this will return the selected transport where\n * media is published. How this selection happens depends on the `focus_active` field of the session membership.\n * If the `focus_selection` is `oldest_membership` this will return the transport of the oldest membership\n * in the room (based on the `created_ts` field of the session membership).\n * If the `focus_selection` is `multi_sfu` it will return the first transport of the `foci_preferred` list.\n * (`multi_sfu` is equivalent to how `m.rtc.member` `rtc_transports` work).\n * @param oldestMembership For backwards compatibility with session membership (legacy). Unused in case of RTC membership.\n * Always required to make the consumer not care if it deals with RTC or session memberships.\n * @returns The transport this membership uses to publish media or undefined if no transport is available.\n */\n public getTransport(oldestMembership: CallMembership): Transport | undefined {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return data.rtc_transports[0];\n case MembershipKind.Session:\n switch (data.focus_active.focus_selection) {\n case \"oldest_membership\":\n if (CallMembership.equal(this, oldestMembership)) return data.foci_preferred[0];\n if (oldestMembership !== undefined) return oldestMembership.getTransport(oldestMembership);\n break;\n case \"multi_sfu\":\n return data.foci_preferred[0];\n default:\n // `focus_selection` not understood.\n return undefined;\n }\n break;\n default:\n return undefined;\n }\n }\n\n /**\n * The value of the `rtc_transports` field for RTC memberships (m.rtc.member).\n * Or the value of the `foci_preferred` field for legacy session memberships (m.call.member).\n */\n public get transports(): Transport[] {\n const { kind, data } = this.membershipData;\n switch (kind) {\n case MembershipKind.RTC:\n return data.rtc_transports;\n case MembershipKind.Session:\n default:\n return data.foci_preferred;\n }\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { type Logger } from "../logger.ts";
|
|
2
1
|
import { type EncryptionConfig } from "./MatrixRTCSession.ts";
|
|
3
2
|
import { type CallMembership } from "./CallMembership.ts";
|
|
4
|
-
import { type
|
|
5
|
-
import { type EncryptionKeyMapKey, type Statistics } from "./types.ts";
|
|
3
|
+
import { type EncryptionKeyMapKey } from "./types.ts";
|
|
6
4
|
/**
|
|
7
5
|
* The string used for the keys in the the encryption key map.
|
|
8
6
|
* `@bob:examle.org:DEVICEID(UUIDRANDOM_MEMBERID_RANDOMUUID)`
|
|
@@ -43,86 +41,4 @@ export interface IEncryptionManager {
|
|
|
43
41
|
}>>;
|
|
44
42
|
}
|
|
45
43
|
export type CallMembershipIdentityParts = Pick<CallMembership, "userId" | "deviceId" | "memberId">;
|
|
46
|
-
/**
|
|
47
|
-
* This class implements the IEncryptionManager interface,
|
|
48
|
-
* and takes care of managing the encryption keys of all rtc members:
|
|
49
|
-
* - generate new keys for the local user and send them to other participants
|
|
50
|
-
* - track all keys of all other members and update livekit.
|
|
51
|
-
*
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
export declare class EncryptionManager implements IEncryptionManager {
|
|
55
|
-
private membership;
|
|
56
|
-
private getMemberships;
|
|
57
|
-
private transport;
|
|
58
|
-
private statistics;
|
|
59
|
-
private onEncryptionKeysChanged;
|
|
60
|
-
private manageMediaKeys;
|
|
61
|
-
private keysEventUpdateTimeout?;
|
|
62
|
-
private makeNewKeyTimeout?;
|
|
63
|
-
private setNewKeyTimeouts;
|
|
64
|
-
private get updateEncryptionKeyThrottle();
|
|
65
|
-
private get makeKeyDelay();
|
|
66
|
-
private get useKeyDelay();
|
|
67
|
-
private encryptionKeys;
|
|
68
|
-
private lastEncryptionKeyUpdateRequest?;
|
|
69
|
-
private lastMembershipFingerprints;
|
|
70
|
-
private latestGeneratedKeyIndex;
|
|
71
|
-
private joinConfig;
|
|
72
|
-
private logger;
|
|
73
|
-
constructor(membership: CallMembershipIdentityParts, getMemberships: () => CallMembership[], transport: IKeyTransport, statistics: Statistics, onEncryptionKeysChanged: (keyBin: Uint8Array<ArrayBuffer>, encryptionKeyIndex: number, membership: CallMembershipIdentityParts, rtcBackendIdentity: string) => void, parentLogger?: Logger);
|
|
74
|
-
private rtcBackendIdentityFromMembershipParts;
|
|
75
|
-
getEncryptionKeys(): ReadonlyMap<EncryptionKeyMapKey, ReadonlyArray<{
|
|
76
|
-
key: Uint8Array<ArrayBuffer>;
|
|
77
|
-
keyIndex: number;
|
|
78
|
-
membership: CallMembershipIdentityParts;
|
|
79
|
-
rtcBackendIdentity: string;
|
|
80
|
-
}>>;
|
|
81
|
-
private joined;
|
|
82
|
-
join(joinConfig: EncryptionConfig): void;
|
|
83
|
-
leave(): void;
|
|
84
|
-
onMembershipsUpdate(oldMemberships: CallMembership[]): void;
|
|
85
|
-
/**
|
|
86
|
-
* Generate a new sender key and add it at the next available index
|
|
87
|
-
* @param delayBeforeUse - If true, wait for a short period before setting the key for the
|
|
88
|
-
* media encryptor to use. If false, set the key immediately.
|
|
89
|
-
* @returns The index of the new key
|
|
90
|
-
*/
|
|
91
|
-
private makeNewSenderKey;
|
|
92
|
-
/**
|
|
93
|
-
* Requests that we resend our current keys to the room. May send a keys event immediately
|
|
94
|
-
* or queue for alter if one has already been sent recently.
|
|
95
|
-
*/
|
|
96
|
-
private requestSendCurrentKey;
|
|
97
|
-
/**
|
|
98
|
-
* Get the known encryption keys for a given participant device.
|
|
99
|
-
*
|
|
100
|
-
* @param membership - The membership identity parts of the participant
|
|
101
|
-
* @returns The encryption keys for the given participant, or undefined if they are not known.
|
|
102
|
-
*/
|
|
103
|
-
private getKeysForParticipant;
|
|
104
|
-
/**
|
|
105
|
-
* Re-sends the encryption keys room event
|
|
106
|
-
*/
|
|
107
|
-
private sendEncryptionKeysEvent;
|
|
108
|
-
onNewKeyReceived: KeyTransportEventListener;
|
|
109
|
-
private storeLastMembershipFingerprints;
|
|
110
|
-
private getNewEncryptionKeyIndex;
|
|
111
|
-
/**
|
|
112
|
-
* Sets an encryption key at a specified index for a participant.
|
|
113
|
-
* The encryption keys for the local participant are also stored here under the
|
|
114
|
-
* user and device ID of the local participant.
|
|
115
|
-
* If the key is older than the existing key at the index, it will be ignored.
|
|
116
|
-
* @param userId - The user ID of the participant
|
|
117
|
-
* @param deviceId - Device ID of the participant
|
|
118
|
-
* @param encryptionKeyIndex - The index of the key to set
|
|
119
|
-
* @param encryptionKeyString - The string representation of the key to set in base64
|
|
120
|
-
* @param timestamp - The timestamp of the key. We assume that these are monotonic for each participant device.
|
|
121
|
-
* @param delayBeforeUse - If true, delay before emitting a key changed event. Useful when setting
|
|
122
|
-
* encryption keys for the local participant to allow time for the key to
|
|
123
|
-
* be distributed.
|
|
124
|
-
*/
|
|
125
|
-
private setEncryptionKey;
|
|
126
|
-
private onRotateKeyTimeout;
|
|
127
|
-
}
|
|
128
44
|
//# sourceMappingURL=EncryptionManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EncryptionManager.d.ts","sourceRoot":"","sources":["../../src/matrixrtc/EncryptionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"EncryptionManager.d.ts","sourceRoot":"","sources":["../../src/matrixrtc/EncryptionManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,2BAA2B,GAAG,mBAAmB,CAEnG;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;;;;OAKG;IACH,IAAI,CAAC,UAAU,EAAE,gBAAgB,GAAG,SAAS,GAAG,IAAI,CAAC;IAErD;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;;;OAIG;IACH,mBAAmB,CAAC,cAAc,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAE5D;;;;OAIG;IACH,iBAAiB,IAAI,WAAW,CAC5B,mBAAmB,EACnB,aAAa,CAAC;QACV,GAAG,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;QAC7B,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,2BAA2B,CAAC;QACxC,kBAAkB,EAAE,MAAM,CAAC;KAC9B,CAAC,CACL,CAAC;CACL;AAED,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC,CAAC"}
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
|
-
import { logger as rootLogger } from "../logger.js";
|
|
4
|
-
import { secureRandomBase64Url } from "../randomstring.js";
|
|
5
|
-
import { decodeBase64, encodeUnpaddedBase64 } from "../base64.js";
|
|
6
|
-
import { safeGetRetryAfterMs } from "../http-api/errors.js";
|
|
7
|
-
import { KeyTransportEvents } from "./IKeyTransport.js";
|
|
8
|
-
import { isMyMembership } from "./types.js";
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
2
|
* The string used for the keys in the the encryption key map.
|
|
12
3
|
* `@bob:examle.org:DEVICEID(UUIDRANDOM_MEMBERID_RANDOMUUID)`
|
|
@@ -19,312 +10,4 @@ export function getEncryptionKeyMapKey(membership) {
|
|
|
19
10
|
* This interface is for testing and for making it possible to interchange the encryption manager.
|
|
20
11
|
* @internal
|
|
21
12
|
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* This class implements the IEncryptionManager interface,
|
|
25
|
-
* and takes care of managing the encryption keys of all rtc members:
|
|
26
|
-
* - generate new keys for the local user and send them to other participants
|
|
27
|
-
* - track all keys of all other members and update livekit.
|
|
28
|
-
*
|
|
29
|
-
* @internal
|
|
30
|
-
*/
|
|
31
|
-
export class EncryptionManager {
|
|
32
|
-
get updateEncryptionKeyThrottle() {
|
|
33
|
-
var _this$joinConfig$upda, _this$joinConfig;
|
|
34
|
-
return (_this$joinConfig$upda = (_this$joinConfig = this.joinConfig) === null || _this$joinConfig === void 0 ? void 0 : _this$joinConfig.updateEncryptionKeyThrottle) !== null && _this$joinConfig$upda !== void 0 ? _this$joinConfig$upda : 3000;
|
|
35
|
-
}
|
|
36
|
-
get makeKeyDelay() {
|
|
37
|
-
var _this$joinConfig$make, _this$joinConfig2;
|
|
38
|
-
return (_this$joinConfig$make = (_this$joinConfig2 = this.joinConfig) === null || _this$joinConfig2 === void 0 ? void 0 : _this$joinConfig2.makeKeyDelay) !== null && _this$joinConfig$make !== void 0 ? _this$joinConfig$make : 3000;
|
|
39
|
-
}
|
|
40
|
-
get useKeyDelay() {
|
|
41
|
-
var _this$joinConfig$useK, _this$joinConfig3;
|
|
42
|
-
return (_this$joinConfig$useK = (_this$joinConfig3 = this.joinConfig) === null || _this$joinConfig3 === void 0 ? void 0 : _this$joinConfig3.useKeyDelay) !== null && _this$joinConfig$useK !== void 0 ? _this$joinConfig$useK : 5000;
|
|
43
|
-
}
|
|
44
|
-
constructor(membership, getMemberships, transport, statistics, onEncryptionKeysChanged, parentLogger) {
|
|
45
|
-
var _this = this;
|
|
46
|
-
this.membership = membership;
|
|
47
|
-
this.getMemberships = getMemberships;
|
|
48
|
-
this.transport = transport;
|
|
49
|
-
this.statistics = statistics;
|
|
50
|
-
this.onEncryptionKeysChanged = onEncryptionKeysChanged;
|
|
51
|
-
_defineProperty(this, "manageMediaKeys", false);
|
|
52
|
-
_defineProperty(this, "keysEventUpdateTimeout", void 0);
|
|
53
|
-
_defineProperty(this, "makeNewKeyTimeout", void 0);
|
|
54
|
-
_defineProperty(this, "setNewKeyTimeouts", new Set());
|
|
55
|
-
_defineProperty(this, "encryptionKeys", new Map());
|
|
56
|
-
_defineProperty(this, "lastEncryptionKeyUpdateRequest", void 0);
|
|
57
|
-
// We use this to store the last membership fingerprints we saw, so we can proactively re-send encryption keys
|
|
58
|
-
// if it looks like a membership has been updated.
|
|
59
|
-
_defineProperty(this, "lastMembershipFingerprints", void 0);
|
|
60
|
-
_defineProperty(this, "latestGeneratedKeyIndex", -1);
|
|
61
|
-
_defineProperty(this, "joinConfig", void 0);
|
|
62
|
-
_defineProperty(this, "logger", void 0);
|
|
63
|
-
_defineProperty(this, "joined", false);
|
|
64
|
-
/**
|
|
65
|
-
* Re-sends the encryption keys room event
|
|
66
|
-
*/
|
|
67
|
-
_defineProperty(this, "sendEncryptionKeysEvent", /*#__PURE__*/function () {
|
|
68
|
-
var _ref = _asyncToGenerator(function* (indexToSend) {
|
|
69
|
-
if (_this.keysEventUpdateTimeout !== undefined) {
|
|
70
|
-
clearTimeout(_this.keysEventUpdateTimeout);
|
|
71
|
-
_this.keysEventUpdateTimeout = undefined;
|
|
72
|
-
}
|
|
73
|
-
_this.lastEncryptionKeyUpdateRequest = Date.now();
|
|
74
|
-
if (!_this.joined) return;
|
|
75
|
-
var myKeys = _this.getKeysForParticipant(_this.membership);
|
|
76
|
-
if (!myKeys) {
|
|
77
|
-
_this.logger.warn("Tried to send encryption keys event but no keys found!");
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
if (typeof indexToSend !== "number" && _this.latestGeneratedKeyIndex === -1) {
|
|
81
|
-
_this.logger.warn("Tried to send encryption keys event but no current key index found!");
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
var keyIndexToSend = indexToSend !== null && indexToSend !== void 0 ? indexToSend : _this.latestGeneratedKeyIndex;
|
|
85
|
-
_this.logger.info("Try sending encryption keys event. keyIndexToSend=".concat(keyIndexToSend, " (method parameter: ").concat(indexToSend, ")"));
|
|
86
|
-
var keyToSend = myKeys[keyIndexToSend];
|
|
87
|
-
try {
|
|
88
|
-
_this.statistics.counters.roomEventEncryptionKeysSent += 1;
|
|
89
|
-
var targets = _this.getMemberships().filter(membership => {
|
|
90
|
-
return membership.sender != undefined;
|
|
91
|
-
}).map(membership => {
|
|
92
|
-
return {
|
|
93
|
-
userId: membership.sender,
|
|
94
|
-
deviceId: membership.deviceId,
|
|
95
|
-
membershipTs: membership.createdTs()
|
|
96
|
-
};
|
|
97
|
-
});
|
|
98
|
-
yield _this.transport.sendKey(encodeUnpaddedBase64(keyToSend), keyIndexToSend, targets);
|
|
99
|
-
_this.logger.debug("sendEncryptionKeysEvent participantId=".concat(_this.membership.userId, ":").concat(_this.membership.deviceId, " numKeys=").concat(myKeys.length, " currentKeyIndex=").concat(_this.latestGeneratedKeyIndex, " keyIndexToSend=").concat(keyIndexToSend));
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (_this.keysEventUpdateTimeout === undefined) {
|
|
102
|
-
var resendDelay = safeGetRetryAfterMs(error, 5000);
|
|
103
|
-
_this.logger.warn("Failed to send m.call.encryption_key, retrying in ".concat(resendDelay), error);
|
|
104
|
-
_this.keysEventUpdateTimeout = setTimeout(() => void _this.sendEncryptionKeysEvent(), resendDelay);
|
|
105
|
-
} else {
|
|
106
|
-
_this.logger.info("Not scheduling key resend as another re-send is already pending");
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
return function (_x) {
|
|
111
|
-
return _ref.apply(this, arguments);
|
|
112
|
-
};
|
|
113
|
-
}());
|
|
114
|
-
_defineProperty(this, "onNewKeyReceived", (membership, keyBase64Encoded, index, timestamp) => {
|
|
115
|
-
this.logger.debug("Received key over key transport ".concat(membership.userId, ":").concat(membership.deviceId, " at index ").concat(index));
|
|
116
|
-
this.setEncryptionKey(membership, index, keyBase64Encoded, timestamp);
|
|
117
|
-
});
|
|
118
|
-
_defineProperty(this, "onRotateKeyTimeout", () => {
|
|
119
|
-
if (!this.manageMediaKeys) return;
|
|
120
|
-
this.makeNewKeyTimeout = undefined;
|
|
121
|
-
this.logger.info("Making new sender key for key rotation");
|
|
122
|
-
var newKeyIndex = this.makeNewSenderKey(true);
|
|
123
|
-
// send immediately: if we're about to start sending with a new key, it's
|
|
124
|
-
// important we get it out to others as soon as we can.
|
|
125
|
-
void this.sendEncryptionKeysEvent(newKeyIndex);
|
|
126
|
-
});
|
|
127
|
-
this.logger = (parentLogger !== null && parentLogger !== void 0 ? parentLogger : rootLogger).getChild("[EncryptionManager]");
|
|
128
|
-
}
|
|
129
|
-
rtcBackendIdentityFromMembershipParts(membership) {
|
|
130
|
-
// Implement logic to construct rtcBackendIdentity from membership parts
|
|
131
|
-
return "".concat(membership.userId, ":").concat(membership.deviceId);
|
|
132
|
-
}
|
|
133
|
-
getEncryptionKeys() {
|
|
134
|
-
var keysMap = new Map();
|
|
135
|
-
for (var [userId, userKeyEntry] of this.encryptionKeys) {
|
|
136
|
-
var keys = userKeyEntry.map((entry, index) => ({
|
|
137
|
-
key: entry.key,
|
|
138
|
-
membership: entry.membership,
|
|
139
|
-
keyIndex: index,
|
|
140
|
-
rtcBackendIdentity: this.rtcBackendIdentityFromMembershipParts(entry.membership)
|
|
141
|
-
}));
|
|
142
|
-
keysMap.set(userId, keys);
|
|
143
|
-
}
|
|
144
|
-
return keysMap;
|
|
145
|
-
}
|
|
146
|
-
join(joinConfig) {
|
|
147
|
-
var _this$joinConfig$mana, _this$joinConfig4, _this$joinConfig5;
|
|
148
|
-
this.joinConfig = joinConfig;
|
|
149
|
-
this.joined = true;
|
|
150
|
-
this.manageMediaKeys = (_this$joinConfig$mana = (_this$joinConfig4 = this.joinConfig) === null || _this$joinConfig4 === void 0 ? void 0 : _this$joinConfig4.manageMediaKeys) !== null && _this$joinConfig$mana !== void 0 ? _this$joinConfig$mana : this.manageMediaKeys;
|
|
151
|
-
this.transport.on(KeyTransportEvents.ReceivedKeys, this.onNewKeyReceived);
|
|
152
|
-
this.transport.start();
|
|
153
|
-
if ((_this$joinConfig5 = this.joinConfig) !== null && _this$joinConfig5 !== void 0 && _this$joinConfig5.manageMediaKeys) {
|
|
154
|
-
this.makeNewSenderKey();
|
|
155
|
-
this.requestSendCurrentKey();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
leave() {
|
|
159
|
-
// clear our encryption keys as we're done with them now (we'll
|
|
160
|
-
// make new keys if we rejoin). We leave keys for other participants
|
|
161
|
-
// as they may still be using the same ones.
|
|
162
|
-
this.encryptionKeys.set(getEncryptionKeyMapKey(this.membership), []);
|
|
163
|
-
this.transport.off(KeyTransportEvents.ReceivedKeys, this.onNewKeyReceived);
|
|
164
|
-
this.transport.stop();
|
|
165
|
-
if (this.makeNewKeyTimeout !== undefined) {
|
|
166
|
-
clearTimeout(this.makeNewKeyTimeout);
|
|
167
|
-
this.makeNewKeyTimeout = undefined;
|
|
168
|
-
}
|
|
169
|
-
for (var t of this.setNewKeyTimeouts) {
|
|
170
|
-
clearTimeout(t);
|
|
171
|
-
}
|
|
172
|
-
this.setNewKeyTimeouts.clear();
|
|
173
|
-
this.manageMediaKeys = false;
|
|
174
|
-
this.joined = false;
|
|
175
|
-
}
|
|
176
|
-
onMembershipsUpdate(oldMemberships) {
|
|
177
|
-
if (this.manageMediaKeys && this.joined) {
|
|
178
|
-
var oldMembershipIds = new Set(oldMemberships.filter(m => !isMyMembership(m, this.membership.userId, this.membership.deviceId)).map(getEncryptionKeyMapKey));
|
|
179
|
-
var newMembershipIds = new Set(this.getMemberships().filter(m => !isMyMembership(m, this.membership.userId, this.membership.deviceId)).map(getEncryptionKeyMapKey));
|
|
180
|
-
|
|
181
|
-
// We can use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference
|
|
182
|
-
// for this once available
|
|
183
|
-
var anyLeft = Array.from(oldMembershipIds).some(x => !newMembershipIds.has(x));
|
|
184
|
-
var anyJoined = Array.from(newMembershipIds).some(x => !oldMembershipIds.has(x));
|
|
185
|
-
var oldFingerprints = this.lastMembershipFingerprints;
|
|
186
|
-
// always store the fingerprints of these latest memberships
|
|
187
|
-
this.storeLastMembershipFingerprints();
|
|
188
|
-
if (anyLeft) {
|
|
189
|
-
if (this.makeNewKeyTimeout) {
|
|
190
|
-
// existing rotation in progress, so let it complete
|
|
191
|
-
} else {
|
|
192
|
-
this.logger.debug("Member(s) have left: queueing sender key rotation");
|
|
193
|
-
this.makeNewKeyTimeout = setTimeout(this.onRotateKeyTimeout, this.makeKeyDelay);
|
|
194
|
-
}
|
|
195
|
-
} else if (anyJoined) {
|
|
196
|
-
this.logger.debug("New member(s) have joined: re-sending keys");
|
|
197
|
-
this.requestSendCurrentKey();
|
|
198
|
-
} else if (oldFingerprints) {
|
|
199
|
-
// does it look like any of the members have updated their memberships?
|
|
200
|
-
var newFingerprints = this.lastMembershipFingerprints;
|
|
201
|
-
|
|
202
|
-
// We can use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference
|
|
203
|
-
// for this once available
|
|
204
|
-
var candidateUpdates = Array.from(oldFingerprints).some(x => !newFingerprints.has(x)) || Array.from(newFingerprints).some(x => !oldFingerprints.has(x));
|
|
205
|
-
if (candidateUpdates) {
|
|
206
|
-
this.logger.debug("Member(s) have updated/reconnected: re-sending keys to everyone");
|
|
207
|
-
this.requestSendCurrentKey();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Generate a new sender key and add it at the next available index
|
|
215
|
-
* @param delayBeforeUse - If true, wait for a short period before setting the key for the
|
|
216
|
-
* media encryptor to use. If false, set the key immediately.
|
|
217
|
-
* @returns The index of the new key
|
|
218
|
-
*/
|
|
219
|
-
makeNewSenderKey() {
|
|
220
|
-
var delayBeforeUse = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
|
|
221
|
-
var encryptionKey = secureRandomBase64Url(16);
|
|
222
|
-
var encryptionKeyIndex = this.getNewEncryptionKeyIndex();
|
|
223
|
-
this.logger.info("Generated new key at index " + encryptionKeyIndex);
|
|
224
|
-
this.setEncryptionKey(this.membership, encryptionKeyIndex, encryptionKey, Date.now(), delayBeforeUse);
|
|
225
|
-
return encryptionKeyIndex;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Requests that we resend our current keys to the room. May send a keys event immediately
|
|
230
|
-
* or queue for alter if one has already been sent recently.
|
|
231
|
-
*/
|
|
232
|
-
requestSendCurrentKey() {
|
|
233
|
-
if (!this.manageMediaKeys) return;
|
|
234
|
-
if (this.lastEncryptionKeyUpdateRequest && this.lastEncryptionKeyUpdateRequest + this.updateEncryptionKeyThrottle > Date.now()) {
|
|
235
|
-
this.logger.info("Last encryption key event sent too recently: postponing");
|
|
236
|
-
if (this.keysEventUpdateTimeout === undefined) {
|
|
237
|
-
this.keysEventUpdateTimeout = setTimeout(() => void this.sendEncryptionKeysEvent(), this.updateEncryptionKeyThrottle);
|
|
238
|
-
}
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
void this.sendEncryptionKeysEvent();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Get the known encryption keys for a given participant device.
|
|
246
|
-
*
|
|
247
|
-
* @param membership - The membership identity parts of the participant
|
|
248
|
-
* @returns The encryption keys for the given participant, or undefined if they are not known.
|
|
249
|
-
*/
|
|
250
|
-
getKeysForParticipant(membership) {
|
|
251
|
-
var _this$encryptionKeys$;
|
|
252
|
-
return (_this$encryptionKeys$ = this.encryptionKeys.get(getEncryptionKeyMapKey(membership))) === null || _this$encryptionKeys$ === void 0 ? void 0 : _this$encryptionKeys$.map(entry => entry.key);
|
|
253
|
-
}
|
|
254
|
-
storeLastMembershipFingerprints() {
|
|
255
|
-
this.lastMembershipFingerprints = new Set(this.getMemberships().filter(m => !isMyMembership(m, this.membership.userId, this.membership.deviceId)).map(m => "".concat(getEncryptionKeyMapKey(m), ":").concat(m.createdTs())));
|
|
256
|
-
}
|
|
257
|
-
getNewEncryptionKeyIndex() {
|
|
258
|
-
if (this.latestGeneratedKeyIndex === -1) {
|
|
259
|
-
return 0;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// maximum key index is 255
|
|
263
|
-
return (this.latestGeneratedKeyIndex + 1) % 256;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Sets an encryption key at a specified index for a participant.
|
|
268
|
-
* The encryption keys for the local participant are also stored here under the
|
|
269
|
-
* user and device ID of the local participant.
|
|
270
|
-
* If the key is older than the existing key at the index, it will be ignored.
|
|
271
|
-
* @param userId - The user ID of the participant
|
|
272
|
-
* @param deviceId - Device ID of the participant
|
|
273
|
-
* @param encryptionKeyIndex - The index of the key to set
|
|
274
|
-
* @param encryptionKeyString - The string representation of the key to set in base64
|
|
275
|
-
* @param timestamp - The timestamp of the key. We assume that these are monotonic for each participant device.
|
|
276
|
-
* @param delayBeforeUse - If true, delay before emitting a key changed event. Useful when setting
|
|
277
|
-
* encryption keys for the local participant to allow time for the key to
|
|
278
|
-
* be distributed.
|
|
279
|
-
*/
|
|
280
|
-
setEncryptionKey(membership, encryptionKeyIndex, encryptionKeyString, timestamp) {
|
|
281
|
-
var delayBeforeUse = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
|
|
282
|
-
this.logger.debug("Setting encryption key for ".concat(membership.userId, ":").concat(membership.deviceId, " at index ").concat(encryptionKeyIndex));
|
|
283
|
-
var keyBin = decodeBase64(encryptionKeyString);
|
|
284
|
-
var mapKey = getEncryptionKeyMapKey(membership);
|
|
285
|
-
if (!this.encryptionKeys.has(mapKey)) {
|
|
286
|
-
this.encryptionKeys.set(mapKey, []);
|
|
287
|
-
}
|
|
288
|
-
var participantKeys = this.encryptionKeys.get(mapKey);
|
|
289
|
-
var existingKeyAtIndex = participantKeys[encryptionKeyIndex];
|
|
290
|
-
if (existingKeyAtIndex) {
|
|
291
|
-
if (existingKeyAtIndex.timestamp > timestamp) {
|
|
292
|
-
this.logger.info("Ignoring new key at index ".concat(encryptionKeyIndex, " for ").concat(mapKey, " as it is older than existing known key"));
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
if (keysEqual(existingKeyAtIndex.key, keyBin)) {
|
|
296
|
-
existingKeyAtIndex.timestamp = timestamp;
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (membership.userId === this.membership.userId && membership.deviceId === this.membership.deviceId) {
|
|
301
|
-
// It is important to already update the latestGeneratedKeyIndex here
|
|
302
|
-
// NOT IN THE `delayBeforeUse` `setTimeout`.
|
|
303
|
-
// Even though this is where we call onEncryptionKeysChanged and set the key in EC (and livekit).
|
|
304
|
-
// It needs to happen here because we will send the key before the timeout has passed and sending
|
|
305
|
-
// the key will use latestGeneratedKeyIndex as the index. if we update it in the `setTimeout` callback
|
|
306
|
-
// it will use the wrong index (index - 1)!
|
|
307
|
-
this.latestGeneratedKeyIndex = encryptionKeyIndex;
|
|
308
|
-
}
|
|
309
|
-
participantKeys[encryptionKeyIndex] = {
|
|
310
|
-
key: keyBin,
|
|
311
|
-
timestamp,
|
|
312
|
-
membership: membership
|
|
313
|
-
};
|
|
314
|
-
if (delayBeforeUse) {
|
|
315
|
-
var useKeyTimeout = setTimeout(() => {
|
|
316
|
-
this.setNewKeyTimeouts.delete(useKeyTimeout);
|
|
317
|
-
this.logger.info("Delayed-emitting key changed event for ".concat(mapKey, " index ").concat(encryptionKeyIndex));
|
|
318
|
-
this.onEncryptionKeysChanged(keyBin, encryptionKeyIndex, membership, this.rtcBackendIdentityFromMembershipParts(membership));
|
|
319
|
-
}, this.useKeyDelay);
|
|
320
|
-
this.setNewKeyTimeouts.add(useKeyTimeout);
|
|
321
|
-
} else {
|
|
322
|
-
this.onEncryptionKeysChanged(keyBin, encryptionKeyIndex, membership, this.rtcBackendIdentityFromMembershipParts(membership));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
function keysEqual(a, b) {
|
|
327
|
-
if (a === b) return true;
|
|
328
|
-
return !!a && !!b && a.length === b.length && a.every((x, i) => x === b[i]);
|
|
329
|
-
}
|
|
330
13
|
//# sourceMappingURL=EncryptionManager.js.map
|