1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-09 10:22:46 +03:00

Resend MatrixRTC encryption keys if a membership has changed (#4343)

* Resend MatrixRTC encryption keys if a membership has changed

* JSDoc

* Update src/matrixrtc/MatrixRTCSession.ts

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>

* Add note about using Set. symmetricDifference() when available

* Always store latest fingerprints

Should reduce unnecessary retransmits

* Refactor

---------

Co-authored-by: Andrew Ferrazzutti <andrewf@element.io>
This commit is contained in:
Hugh Nimmo-Smith
2024-08-14 15:08:52 +01:00
committed by GitHub
parent 78cbf7cd28
commit c65ef03567
2 changed files with 263 additions and 9 deletions

View File

@@ -138,6 +138,10 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
private encryptionKeys = new Map<string, Array<Uint8Array>>();
private lastEncryptionKeyUpdateRequest?: number;
// We use this to store the last membership fingerprints we saw, so we can proactively re-send encryption keys
// if it looks like a membership has been updated.
private lastMembershipFingerprints: Set<string> | undefined;
/**
* The callId (sessionId) of the call.
*
@@ -636,6 +640,14 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
}
};
private isMyMembership = (m: CallMembership): boolean =>
m.sender === this.client.getUserId() && m.deviceId === this.client.getDeviceId();
/**
* Examines the latest call memberships and handles any encryption key sending or rotation that is needed.
*
* This function should be called when the room members or call memberships might have changed.
*/
public onMembershipUpdate = (): void => {
const oldMemberships = this.memberships;
this.memberships = MatrixRTCSession.callMembershipsForRoom(this.room);
@@ -651,19 +663,22 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
this.emit(MatrixRTCSessionEvent.MembershipsChanged, oldMemberships, this.memberships);
}
const isMyMembership = (m: CallMembership): boolean =>
m.sender === this.client.getUserId() && m.deviceId === this.client.getDeviceId();
if (this.manageMediaKeys && this.isJoined() && this.makeNewKeyTimeout === undefined) {
const oldMebershipIds = new Set(
oldMemberships.filter((m) => !isMyMembership(m)).map(getParticipantIdFromMembership),
const oldMembershipIds = new Set(
oldMemberships.filter((m) => !this.isMyMembership(m)).map(getParticipantIdFromMembership),
);
const newMebershipIds = new Set(
this.memberships.filter((m) => !isMyMembership(m)).map(getParticipantIdFromMembership),
const newMembershipIds = new Set(
this.memberships.filter((m) => !this.isMyMembership(m)).map(getParticipantIdFromMembership),
);
const anyLeft = Array.from(oldMebershipIds).some((x) => !newMebershipIds.has(x));
const anyJoined = Array.from(newMebershipIds).some((x) => !oldMebershipIds.has(x));
// We can use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference
// for this once available
const anyLeft = Array.from(oldMembershipIds).some((x) => !newMembershipIds.has(x));
const anyJoined = Array.from(newMembershipIds).some((x) => !oldMembershipIds.has(x));
const oldFingerprints = this.lastMembershipFingerprints;
// always store the fingerprints of these latest memberships
this.storeLastMembershipFingerprints();
if (anyLeft) {
logger.debug(`Member(s) have left: queueing sender key rotation`);
@@ -671,12 +686,33 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
} else if (anyJoined) {
logger.debug(`New member(s) have joined: re-sending keys`);
this.requestKeyEventSend();
} else if (oldFingerprints) {
// does it look like any of the members have updated their memberships?
const newFingerprints = this.lastMembershipFingerprints!;
// We can use https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference
// for this once available
const candidateUpdates =
Array.from(oldFingerprints).some((x) => !newFingerprints.has(x)) ||
Array.from(newFingerprints).some((x) => !oldFingerprints.has(x));
if (candidateUpdates) {
logger.debug(`Member(s) have updated/reconnected: re-sending keys`);
this.requestKeyEventSend();
}
}
}
this.setExpiryTimer();
};
private storeLastMembershipFingerprints(): void {
this.lastMembershipFingerprints = new Set(
this.memberships
.filter((m) => !this.isMyMembership(m))
.map((m) => `${getParticipantIdFromMembership(m)}:${m.membershipID}:${m.createdTs()}`),
);
}
/**
* Constructs our own membership
* @param prevMembership - The previous value of our call membership, if any