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.
Files changed (93) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/@types/event.d.ts +1 -1
  3. package/lib/@types/event.d.ts.map +1 -1
  4. package/lib/@types/event.js +1 -1
  5. package/lib/@types/event.js.map +1 -1
  6. package/lib/client.d.ts.map +1 -1
  7. package/lib/client.js +250 -246
  8. package/lib/client.js.map +1 -1
  9. package/lib/crypto-api/index.d.ts +13 -2
  10. package/lib/crypto-api/index.d.ts.map +1 -1
  11. package/lib/crypto-api/index.js +11 -0
  12. package/lib/crypto-api/index.js.map +1 -1
  13. package/lib/logger.d.ts +5 -5
  14. package/lib/logger.d.ts.map +1 -1
  15. package/lib/logger.js.map +1 -1
  16. package/lib/matrixrtc/CallMembership.d.ts +49 -145
  17. package/lib/matrixrtc/CallMembership.d.ts.map +1 -1
  18. package/lib/matrixrtc/CallMembership.js +157 -265
  19. package/lib/matrixrtc/CallMembership.js.map +1 -1
  20. package/lib/matrixrtc/EncryptionManager.d.ts +1 -85
  21. package/lib/matrixrtc/EncryptionManager.d.ts.map +1 -1
  22. package/lib/matrixrtc/EncryptionManager.js +0 -317
  23. package/lib/matrixrtc/EncryptionManager.js.map +1 -1
  24. package/lib/matrixrtc/MatrixRTCSession.d.ts +18 -22
  25. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
  26. package/lib/matrixrtc/MatrixRTCSession.js +48 -76
  27. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
  28. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts +2 -1
  29. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts.map +1 -1
  30. package/lib/matrixrtc/MatrixRTCSessionManager.js +3 -2
  31. package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
  32. package/lib/matrixrtc/MembershipManager.d.ts +10 -4
  33. package/lib/matrixrtc/MembershipManager.d.ts.map +1 -1
  34. package/lib/matrixrtc/MembershipManager.js +10 -4
  35. package/lib/matrixrtc/MembershipManager.js.map +1 -1
  36. package/lib/matrixrtc/RTCEncryptionManager.d.ts +6 -7
  37. package/lib/matrixrtc/RTCEncryptionManager.d.ts.map +1 -1
  38. package/lib/matrixrtc/RTCEncryptionManager.js +4 -7
  39. package/lib/matrixrtc/RTCEncryptionManager.js.map +1 -1
  40. package/lib/matrixrtc/index.d.ts +1 -0
  41. package/lib/matrixrtc/index.d.ts.map +1 -1
  42. package/lib/matrixrtc/index.js.map +1 -1
  43. package/lib/matrixrtc/membershipData/common.d.ts +8 -0
  44. package/lib/matrixrtc/membershipData/common.d.ts.map +1 -0
  45. package/lib/matrixrtc/membershipData/common.js +26 -0
  46. package/lib/matrixrtc/membershipData/common.js.map +1 -0
  47. package/lib/matrixrtc/membershipData/index.d.ts +4 -0
  48. package/lib/matrixrtc/membershipData/index.d.ts.map +1 -0
  49. package/lib/matrixrtc/membershipData/index.js +20 -0
  50. package/lib/matrixrtc/membershipData/index.js.map +1 -0
  51. package/lib/matrixrtc/membershipData/rtc.d.ts +33 -0
  52. package/lib/matrixrtc/membershipData/rtc.d.ts.map +1 -0
  53. package/lib/matrixrtc/membershipData/rtc.js +137 -0
  54. package/lib/matrixrtc/membershipData/rtc.js.map +1 -0
  55. package/lib/matrixrtc/membershipData/session.d.ts +77 -0
  56. package/lib/matrixrtc/membershipData/session.d.ts.map +1 -0
  57. package/lib/matrixrtc/membershipData/session.js +62 -0
  58. package/lib/matrixrtc/membershipData/session.js.map +1 -0
  59. package/lib/matrixrtc/types.d.ts +23 -0
  60. package/lib/matrixrtc/types.d.ts.map +1 -1
  61. package/lib/matrixrtc/types.js +9 -1
  62. package/lib/matrixrtc/types.js.map +1 -1
  63. package/lib/matrixrtc/utils.d.ts +11 -1
  64. package/lib/matrixrtc/utils.d.ts.map +1 -1
  65. package/lib/matrixrtc/utils.js +24 -1
  66. package/lib/matrixrtc/utils.js.map +1 -1
  67. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
  68. package/lib/rust-crypto/rust-crypto.js +2 -2
  69. package/lib/rust-crypto/rust-crypto.js.map +1 -1
  70. package/package.json +5 -7
  71. package/src/@types/event.ts +2 -2
  72. package/src/client.ts +5 -3
  73. package/src/crypto-api/index.ts +17 -2
  74. package/src/logger.ts +5 -5
  75. package/src/matrixrtc/CallMembership.ts +159 -373
  76. package/src/matrixrtc/EncryptionManager.ts +1 -417
  77. package/src/matrixrtc/MatrixRTCSession.ts +82 -122
  78. package/src/matrixrtc/MatrixRTCSessionManager.ts +5 -3
  79. package/src/matrixrtc/MembershipManager.ts +14 -17
  80. package/src/matrixrtc/RTCEncryptionManager.ts +7 -10
  81. package/src/matrixrtc/index.ts +1 -0
  82. package/src/matrixrtc/membershipData/common.ts +27 -0
  83. package/src/matrixrtc/membershipData/index.ts +19 -0
  84. package/src/matrixrtc/membershipData/rtc.ts +156 -0
  85. package/src/matrixrtc/membershipData/session.ts +146 -0
  86. package/src/matrixrtc/types.ts +27 -1
  87. package/src/matrixrtc/utils.ts +24 -2
  88. package/src/rust-crypto/rust-crypto.ts +4 -1
  89. package/lib/matrixrtc/RoomKeyTransport.d.ts +0 -25
  90. package/lib/matrixrtc/RoomKeyTransport.d.ts.map +0 -1
  91. package/lib/matrixrtc/RoomKeyTransport.js +0 -152
  92. package/lib/matrixrtc/RoomKeyTransport.js.map +0 -1
  93. package/src/matrixrtc/RoomKeyTransport.ts +0 -189
@@ -1,11 +1,6 @@
1
- import { type Logger, logger as rootLogger } from "../logger.ts";
2
1
  import { type EncryptionConfig } from "./MatrixRTCSession.ts";
3
- import { secureRandomBase64Url } from "../randomstring.ts";
4
- import { decodeBase64, encodeUnpaddedBase64 } from "../base64.ts";
5
- import { safeGetRetryAfterMs } from "../http-api/errors.ts";
6
2
  import { type CallMembership } from "./CallMembership.ts";
7
- import { type KeyTransportEventListener, KeyTransportEvents, type IKeyTransport } from "./IKeyTransport.ts";
8
- import { isMyMembership, type EncryptionKeyMapKey, type Statistics } from "./types.ts";
3
+ import { type EncryptionKeyMapKey } from "./types.ts";
9
4
 
10
5
  /**
11
6
  * The string used for the keys in the the encryption key map.
@@ -57,414 +52,3 @@ export interface IEncryptionManager {
57
52
  }
58
53
 
59
54
  export type CallMembershipIdentityParts = Pick<CallMembership, "userId" | "deviceId" | "memberId">;
60
-
61
- /**
62
- * This class implements the IEncryptionManager interface,
63
- * and takes care of managing the encryption keys of all rtc members:
64
- * - generate new keys for the local user and send them to other participants
65
- * - track all keys of all other members and update livekit.
66
- *
67
- * @internal
68
- */
69
- export class EncryptionManager implements IEncryptionManager {
70
- private manageMediaKeys = false;
71
- private keysEventUpdateTimeout?: ReturnType<typeof setTimeout>;
72
- private makeNewKeyTimeout?: ReturnType<typeof setTimeout>;
73
- private setNewKeyTimeouts = new Set<ReturnType<typeof setTimeout>>();
74
-
75
- private get updateEncryptionKeyThrottle(): number {
76
- return this.joinConfig?.updateEncryptionKeyThrottle ?? 3_000;
77
- }
78
-
79
- private get makeKeyDelay(): number {
80
- return this.joinConfig?.makeKeyDelay ?? 3_000;
81
- }
82
-
83
- private get useKeyDelay(): number {
84
- return this.joinConfig?.useKeyDelay ?? 5_000;
85
- }
86
-
87
- private encryptionKeys = new Map<
88
- string,
89
- Array<{ key: Uint8Array<ArrayBuffer>; timestamp: number; membership: CallMembershipIdentityParts }>
90
- >();
91
- private lastEncryptionKeyUpdateRequest?: number;
92
-
93
- // We use this to store the last membership fingerprints we saw, so we can proactively re-send encryption keys
94
- // if it looks like a membership has been updated.
95
- private lastMembershipFingerprints: Set<string> | undefined;
96
-
97
- private latestGeneratedKeyIndex = -1;
98
- private joinConfig: EncryptionConfig | undefined;
99
- private logger: Logger;
100
-
101
- public constructor(
102
- private membership: CallMembershipIdentityParts,
103
- private getMemberships: () => CallMembership[],
104
- private transport: IKeyTransport,
105
- private statistics: Statistics,
106
- private onEncryptionKeysChanged: (
107
- keyBin: Uint8Array<ArrayBuffer>,
108
- encryptionKeyIndex: number,
109
- membership: CallMembershipIdentityParts,
110
- rtcBackendIdentity: string,
111
- ) => void,
112
- parentLogger?: Logger,
113
- ) {
114
- this.logger = (parentLogger ?? rootLogger).getChild(`[EncryptionManager]`);
115
- }
116
-
117
- private rtcBackendIdentityFromMembershipParts(membership: CallMembershipIdentityParts): string {
118
- // Implement logic to construct rtcBackendIdentity from membership parts
119
- return `${membership.userId}:${membership.deviceId}`;
120
- }
121
-
122
- public getEncryptionKeys(): ReadonlyMap<
123
- EncryptionKeyMapKey,
124
- ReadonlyArray<{
125
- key: Uint8Array<ArrayBuffer>;
126
- keyIndex: number;
127
- membership: CallMembershipIdentityParts;
128
- rtcBackendIdentity: string;
129
- }>
130
- > {
131
- const keysMap = new Map<
132
- EncryptionKeyMapKey,
133
- ReadonlyArray<{
134
- key: Uint8Array<ArrayBuffer>;
135
- keyIndex: number;
136
- membership: CallMembershipIdentityParts;
137
- rtcBackendIdentity: string;
138
- }>
139
- >();
140
- for (const [userId, userKeyEntry] of this.encryptionKeys) {
141
- const keys = userKeyEntry.map((entry, index) => ({
142
- key: entry.key,
143
- membership: entry.membership,
144
- keyIndex: index,
145
- rtcBackendIdentity: this.rtcBackendIdentityFromMembershipParts(entry.membership),
146
- }));
147
- keysMap.set(userId as EncryptionKeyMapKey, keys);
148
- }
149
- return keysMap;
150
- }
151
-
152
- private joined = false;
153
-
154
- public join(joinConfig: EncryptionConfig): void {
155
- this.joinConfig = joinConfig;
156
- this.joined = true;
157
- this.manageMediaKeys = this.joinConfig?.manageMediaKeys ?? this.manageMediaKeys;
158
-
159
- this.transport.on(KeyTransportEvents.ReceivedKeys, this.onNewKeyReceived);
160
-
161
- this.transport.start();
162
- if (this.joinConfig?.manageMediaKeys) {
163
- this.makeNewSenderKey();
164
- this.requestSendCurrentKey();
165
- }
166
- }
167
-
168
- public leave(): void {
169
- // clear our encryption keys as we're done with them now (we'll
170
- // make new keys if we rejoin). We leave keys for other participants
171
- // as they may still be using the same ones.
172
- this.encryptionKeys.set(getEncryptionKeyMapKey(this.membership), []);
173
- this.transport.off(KeyTransportEvents.ReceivedKeys, this.onNewKeyReceived);
174
- this.transport.stop();
175
-
176
- if (this.makeNewKeyTimeout !== undefined) {
177
- clearTimeout(this.makeNewKeyTimeout);
178
- this.makeNewKeyTimeout = undefined;
179
- }
180
- for (const t of this.setNewKeyTimeouts) {
181
- clearTimeout(t);
182
- }
183
- this.setNewKeyTimeouts.clear();
184
-
185
- this.manageMediaKeys = false;
186
- this.joined = false;
187
- }
188
-
189
- public onMembershipsUpdate(oldMemberships: CallMembership[]): void {
190
- if (this.manageMediaKeys && this.joined) {
191
- const oldMembershipIds = new Set(
192
- oldMemberships
193
- .filter((m) => !isMyMembership(m, this.membership.userId, this.membership.deviceId))
194
- .map(getEncryptionKeyMapKey),
195
- );
196
- const newMembershipIds = new Set(
197
- this.getMemberships()
198
- .filter((m) => !isMyMembership(m, this.membership.userId, this.membership.deviceId))
199
- .map(getEncryptionKeyMapKey),
200
- );
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
- const anyLeft = Array.from(oldMembershipIds).some((x) => !newMembershipIds.has(x));
205
- const anyJoined = Array.from(newMembershipIds).some((x) => !oldMembershipIds.has(x));
206
-
207
- const oldFingerprints = this.lastMembershipFingerprints;
208
- // always store the fingerprints of these latest memberships
209
- this.storeLastMembershipFingerprints();
210
-
211
- if (anyLeft) {
212
- if (this.makeNewKeyTimeout) {
213
- // existing rotation in progress, so let it complete
214
- } else {
215
- this.logger.debug(`Member(s) have left: queueing sender key rotation`);
216
- this.makeNewKeyTimeout = setTimeout(this.onRotateKeyTimeout, this.makeKeyDelay);
217
- }
218
- } else if (anyJoined) {
219
- this.logger.debug(`New member(s) have joined: re-sending keys`);
220
- this.requestSendCurrentKey();
221
- } else if (oldFingerprints) {
222
- // does it look like any of the members have updated their memberships?
223
- const newFingerprints = this.lastMembershipFingerprints!;
224
-
225
- // We can use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference
226
- // for this once available
227
- const candidateUpdates =
228
- Array.from(oldFingerprints).some((x) => !newFingerprints.has(x)) ||
229
- Array.from(newFingerprints).some((x) => !oldFingerprints.has(x));
230
- if (candidateUpdates) {
231
- this.logger.debug(`Member(s) have updated/reconnected: re-sending keys to everyone`);
232
- this.requestSendCurrentKey();
233
- }
234
- }
235
- }
236
- }
237
-
238
- /**
239
- * Generate a new sender key and add it at the next available index
240
- * @param delayBeforeUse - If true, wait for a short period before setting the key for the
241
- * media encryptor to use. If false, set the key immediately.
242
- * @returns The index of the new key
243
- */
244
- private makeNewSenderKey(delayBeforeUse = false): number {
245
- const encryptionKey = secureRandomBase64Url(16);
246
- const encryptionKeyIndex = this.getNewEncryptionKeyIndex();
247
- this.logger.info("Generated new key at index " + encryptionKeyIndex);
248
- this.setEncryptionKey(this.membership, encryptionKeyIndex, encryptionKey, Date.now(), delayBeforeUse);
249
- return encryptionKeyIndex;
250
- }
251
-
252
- /**
253
- * Requests that we resend our current keys to the room. May send a keys event immediately
254
- * or queue for alter if one has already been sent recently.
255
- */
256
- private requestSendCurrentKey(): void {
257
- if (!this.manageMediaKeys) return;
258
-
259
- if (
260
- this.lastEncryptionKeyUpdateRequest &&
261
- this.lastEncryptionKeyUpdateRequest + this.updateEncryptionKeyThrottle > Date.now()
262
- ) {
263
- this.logger.info("Last encryption key event sent too recently: postponing");
264
- if (this.keysEventUpdateTimeout === undefined) {
265
- this.keysEventUpdateTimeout = setTimeout(
266
- () => void this.sendEncryptionKeysEvent(),
267
- this.updateEncryptionKeyThrottle,
268
- );
269
- }
270
- return;
271
- }
272
-
273
- void this.sendEncryptionKeysEvent();
274
- }
275
-
276
- /**
277
- * Get the known encryption keys for a given participant device.
278
- *
279
- * @param membership - The membership identity parts of the participant
280
- * @returns The encryption keys for the given participant, or undefined if they are not known.
281
- */
282
- private getKeysForParticipant(membership: CallMembershipIdentityParts): Array<Uint8Array<ArrayBuffer>> | undefined {
283
- return this.encryptionKeys.get(getEncryptionKeyMapKey(membership))?.map((entry) => entry.key);
284
- }
285
-
286
- /**
287
- * Re-sends the encryption keys room event
288
- */
289
- private sendEncryptionKeysEvent = async (indexToSend?: number): Promise<void> => {
290
- if (this.keysEventUpdateTimeout !== undefined) {
291
- clearTimeout(this.keysEventUpdateTimeout);
292
- this.keysEventUpdateTimeout = undefined;
293
- }
294
- this.lastEncryptionKeyUpdateRequest = Date.now();
295
-
296
- if (!this.joined) return;
297
-
298
- const myKeys = this.getKeysForParticipant(this.membership);
299
-
300
- if (!myKeys) {
301
- this.logger.warn("Tried to send encryption keys event but no keys found!");
302
- return;
303
- }
304
-
305
- if (typeof indexToSend !== "number" && this.latestGeneratedKeyIndex === -1) {
306
- this.logger.warn("Tried to send encryption keys event but no current key index found!");
307
- return;
308
- }
309
-
310
- const keyIndexToSend = indexToSend ?? this.latestGeneratedKeyIndex;
311
-
312
- this.logger.info(
313
- `Try sending encryption keys event. keyIndexToSend=${keyIndexToSend} (method parameter: ${indexToSend})`,
314
- );
315
- const keyToSend = myKeys[keyIndexToSend];
316
-
317
- try {
318
- this.statistics.counters.roomEventEncryptionKeysSent += 1;
319
- const targets = this.getMemberships()
320
- .filter((membership) => {
321
- return membership.sender != undefined;
322
- })
323
- .map((membership) => {
324
- return {
325
- userId: membership.sender!,
326
- deviceId: membership.deviceId,
327
- membershipTs: membership.createdTs(),
328
- };
329
- });
330
- await this.transport.sendKey(encodeUnpaddedBase64(keyToSend), keyIndexToSend, targets);
331
- this.logger.debug(
332
- `sendEncryptionKeysEvent participantId=${this.membership.userId}:${this.membership.deviceId} numKeys=${myKeys.length} currentKeyIndex=${this.latestGeneratedKeyIndex} keyIndexToSend=${keyIndexToSend}`,
333
- );
334
- } catch (error) {
335
- if (this.keysEventUpdateTimeout === undefined) {
336
- const resendDelay = safeGetRetryAfterMs(error, 5000);
337
- this.logger.warn(`Failed to send m.call.encryption_key, retrying in ${resendDelay}`, error);
338
- this.keysEventUpdateTimeout = setTimeout(() => void this.sendEncryptionKeysEvent(), resendDelay);
339
- } else {
340
- this.logger.info("Not scheduling key resend as another re-send is already pending");
341
- }
342
- }
343
- };
344
-
345
- public onNewKeyReceived: KeyTransportEventListener = (membership, keyBase64Encoded, index, timestamp) => {
346
- this.logger.debug(
347
- `Received key over key transport ${membership.userId}:${membership.deviceId} at index ${index}`,
348
- );
349
- this.setEncryptionKey(membership, index, keyBase64Encoded, timestamp);
350
- };
351
-
352
- private storeLastMembershipFingerprints(): void {
353
- this.lastMembershipFingerprints = new Set(
354
- this.getMemberships()
355
- .filter((m) => !isMyMembership(m, this.membership.userId, this.membership.deviceId))
356
- .map((m) => `${getEncryptionKeyMapKey(m)}:${m.createdTs()}`),
357
- );
358
- }
359
-
360
- private getNewEncryptionKeyIndex(): number {
361
- if (this.latestGeneratedKeyIndex === -1) {
362
- return 0;
363
- }
364
-
365
- // maximum key index is 255
366
- return (this.latestGeneratedKeyIndex + 1) % 256;
367
- }
368
-
369
- /**
370
- * Sets an encryption key at a specified index for a participant.
371
- * The encryption keys for the local participant are also stored here under the
372
- * user and device ID of the local participant.
373
- * If the key is older than the existing key at the index, it will be ignored.
374
- * @param userId - The user ID of the participant
375
- * @param deviceId - Device ID of the participant
376
- * @param encryptionKeyIndex - The index of the key to set
377
- * @param encryptionKeyString - The string representation of the key to set in base64
378
- * @param timestamp - The timestamp of the key. We assume that these are monotonic for each participant device.
379
- * @param delayBeforeUse - If true, delay before emitting a key changed event. Useful when setting
380
- * encryption keys for the local participant to allow time for the key to
381
- * be distributed.
382
- */
383
- private setEncryptionKey(
384
- membership: CallMembershipIdentityParts,
385
- encryptionKeyIndex: number,
386
- encryptionKeyString: string,
387
- timestamp: number,
388
- delayBeforeUse = false,
389
- ): void {
390
- this.logger.debug(
391
- `Setting encryption key for ${membership.userId}:${membership.deviceId} at index ${encryptionKeyIndex}`,
392
- );
393
- const keyBin = decodeBase64(encryptionKeyString);
394
-
395
- const mapKey = getEncryptionKeyMapKey(membership);
396
- if (!this.encryptionKeys.has(mapKey)) {
397
- this.encryptionKeys.set(mapKey, []);
398
- }
399
- const participantKeys = this.encryptionKeys.get(mapKey)!;
400
-
401
- const existingKeyAtIndex = participantKeys[encryptionKeyIndex];
402
-
403
- if (existingKeyAtIndex) {
404
- if (existingKeyAtIndex.timestamp > timestamp) {
405
- this.logger.info(
406
- `Ignoring new key at index ${encryptionKeyIndex} for ${mapKey} as it is older than existing known key`,
407
- );
408
- return;
409
- }
410
-
411
- if (keysEqual(existingKeyAtIndex.key, keyBin)) {
412
- existingKeyAtIndex.timestamp = timestamp;
413
- return;
414
- }
415
- }
416
-
417
- if (membership.userId === this.membership.userId && membership.deviceId === this.membership.deviceId) {
418
- // It is important to already update the latestGeneratedKeyIndex here
419
- // NOT IN THE `delayBeforeUse` `setTimeout`.
420
- // Even though this is where we call onEncryptionKeysChanged and set the key in EC (and livekit).
421
- // It needs to happen here because we will send the key before the timeout has passed and sending
422
- // the key will use latestGeneratedKeyIndex as the index. if we update it in the `setTimeout` callback
423
- // it will use the wrong index (index - 1)!
424
- this.latestGeneratedKeyIndex = encryptionKeyIndex;
425
- }
426
- participantKeys[encryptionKeyIndex] = {
427
- key: keyBin,
428
- timestamp,
429
- membership: membership,
430
- };
431
-
432
- if (delayBeforeUse) {
433
- const useKeyTimeout = setTimeout(() => {
434
- this.setNewKeyTimeouts.delete(useKeyTimeout);
435
- this.logger.info(`Delayed-emitting key changed event for ${mapKey} index ${encryptionKeyIndex}`);
436
-
437
- this.onEncryptionKeysChanged(
438
- keyBin,
439
- encryptionKeyIndex,
440
- membership,
441
- this.rtcBackendIdentityFromMembershipParts(membership),
442
- );
443
- }, this.useKeyDelay);
444
- this.setNewKeyTimeouts.add(useKeyTimeout);
445
- } else {
446
- this.onEncryptionKeysChanged(
447
- keyBin,
448
- encryptionKeyIndex,
449
- membership,
450
- this.rtcBackendIdentityFromMembershipParts(membership),
451
- );
452
- }
453
- }
454
-
455
- private onRotateKeyTimeout = (): void => {
456
- if (!this.manageMediaKeys) return;
457
-
458
- this.makeNewKeyTimeout = undefined;
459
- this.logger.info("Making new sender key for key rotation");
460
- const newKeyIndex = this.makeNewSenderKey(true);
461
- // send immediately: if we're about to start sending with a new key, it's
462
- // important we get it out to others as soon as we can.
463
- void this.sendEncryptionKeysEvent(newKeyIndex);
464
- };
465
- }
466
-
467
- function keysEqual(a: Uint8Array | undefined, b: Uint8Array | undefined): boolean {
468
- if (a === b) return true;
469
- return !!a && !!b && a.length === b.length && a.every((x, i) => x === b[i]);
470
- }