Skip to content

Commit

Permalink
Don't share full key history for RTC per-participant encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
hughns committed Sep 16, 2024
1 parent 53b599f commit a52fb3e
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 16 deletions.
4 changes: 3 additions & 1 deletion spec/unit/matrixrtc/MatrixRTCSession.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,7 @@ describe("MatrixRTCSession", () => {
sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true });
const firstKeysPayload = await keysSentPromise1;
expect(firstKeysPayload.keys).toHaveLength(1);
expect(firstKeysPayload.keys[0].index).toEqual(0);

sendEventMock.mockClear();

Expand All @@ -962,7 +963,8 @@ describe("MatrixRTCSession", () => {

const secondKeysPayload = await keysSentPromise2;

expect(secondKeysPayload.keys).toHaveLength(2);
expect(secondKeysPayload.keys).toHaveLength(1);
expect(secondKeysPayload.keys[0].index).toEqual(1);
expect(onMyEncryptionKeyChanged).toHaveBeenCalledTimes(2);
} finally {
jest.useRealTimers();
Expand Down
50 changes: 35 additions & 15 deletions src/matrixrtc/MatrixRTCSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
// if it looks like a membership has been updated.
private lastMembershipFingerprints: Set<string> | undefined;

private currentEncryptionKeyIndex = -1;

/**
* The callId (sessionId) of the call.
*
Expand Down Expand Up @@ -467,10 +469,16 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
const useKeyTimeout = setTimeout(() => {
this.setNewKeyTimeouts.delete(useKeyTimeout);
logger.info(`Delayed-emitting key changed event for ${participantId} idx ${encryptionKeyIndex}`);
if (userId === this.client.getUserId() && deviceId === this.client.getDeviceId()) {
this.currentEncryptionKeyIndex = encryptionKeyIndex;
}
this.emit(MatrixRTCSessionEvent.EncryptionKeyChanged, keyBin, encryptionKeyIndex, participantId);
}, USE_KEY_DELAY);
this.setNewKeyTimeouts.add(useKeyTimeout);
} else {
if (userId === this.client.getUserId() && deviceId === this.client.getDeviceId()) {
this.currentEncryptionKeyIndex = encryptionKeyIndex;
}
this.emit(MatrixRTCSessionEvent.EncryptionKeyChanged, keyBin, encryptionKeyIndex, participantId);
}
}
Expand All @@ -479,8 +487,9 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
* Generate a new sender key and add it at the next available index
* @param delayBeforeUse - If true, wait for a short period before setting the key for the
* media encryptor to use. If false, set the key immediately.
* @returns The index of the new key
*/
private makeNewSenderKey(delayBeforeUse = false): void {
private makeNewSenderKey(delayBeforeUse = false): number {
const userId = this.client.getUserId();
const deviceId = this.client.getDeviceId();

Expand All @@ -491,6 +500,7 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
const encryptionKeyIndex = this.getNewEncryptionKeyIndex();
logger.info("Generated new key at index " + encryptionKeyIndex);
this.setEncryptionKey(userId, deviceId, encryptionKeyIndex, encryptionKey, Date.now(), delayBeforeUse);
return encryptionKeyIndex;
}

/**
Expand All @@ -517,17 +527,17 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
/**
* Re-sends the encryption keys room event
*/
private sendEncryptionKeysEvent = async (): Promise<void> => {
private sendEncryptionKeysEvent = async (indexToSend?: number): Promise<void> => {
if (this.keysEventUpdateTimeout !== undefined) {
clearTimeout(this.keysEventUpdateTimeout);
this.keysEventUpdateTimeout = undefined;
}
this.lastEncryptionKeyUpdateRequest = Date.now();

logger.info("Sending encryption keys event");

if (!this.isJoined()) return;

logger.info(`Sending encryption keys event. indexToSend=${indexToSend}`);

const userId = this.client.getUserId();
const deviceId = this.client.getDeviceId();

Expand All @@ -541,20 +551,30 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
return;
}

if (typeof indexToSend !== "number" && this.currentEncryptionKeyIndex === -1) {
logger.warn("Tried to send encryption keys event but no current key index found!");
return;
}

const keyIndexToSend = typeof indexToSend === "number" ? indexToSend : this.currentEncryptionKeyIndex;
const keyToSend = myKeys[keyIndexToSend];

try {
await this.client.sendEvent(this.room.roomId, EventType.CallEncryptionKeysPrefix, {
keys: myKeys.map((key, index) => {
return {
index,
key: encodeUnpaddedBase64(key),
};
}),
const content: EncryptionKeysEventContent = {
keys: [
{
index: keyIndexToSend,
key: encodeUnpaddedBase64(keyToSend),
},
],
device_id: deviceId,
call_id: "",
} as EncryptionKeysEventContent);
};

await this.client.sendEvent(this.room.roomId, EventType.CallEncryptionKeysPrefix, content);

logger.debug(
`Embedded-E2EE-LOG updateEncryptionKeyEvent participantId=${userId}:${deviceId} numSent=${myKeys.length}`,
`Embedded-E2EE-LOG updateEncryptionKeyEvent participantId=${userId}:${deviceId} numKeys=${myKeys.length} currentKeyIndex=${this.currentEncryptionKeyIndex} keyIndexToSend=${keyIndexToSend}`,
this.encryptionKeys,
);
} catch (error) {
Expand Down Expand Up @@ -1030,9 +1050,9 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M

this.makeNewKeyTimeout = undefined;
logger.info("Making new sender key for key rotation");
this.makeNewSenderKey(true);
const newKeyIndex = this.makeNewSenderKey(true);
// send immediately: if we're about to start sending with a new key, it's
// important we get it out to others as soon as we can.
this.sendEncryptionKeysEvent();
this.sendEncryptionKeysEvent(newKeyIndex);
};
}

0 comments on commit a52fb3e

Please sign in to comment.