Skip to content

Commit

Permalink
refactor!: Room's Key ID generation (#33329)
Browse files Browse the repository at this point in the history
Co-authored-by: Hugo Costa <hugocarreiracosta@gmail.com>
Co-authored-by: Guilherme Gazzo <guilhermegazzo@gmail.com>
  • Loading branch information
3 people committed Oct 17, 2024
1 parent 04cee2a commit 24f16be
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 44 deletions.
8 changes: 8 additions & 0 deletions .changeset/spicy-eggs-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@rocket.chat/meteor": major
---

Randomizes `e2eKeyId` generation instead of derive it from encoded key. Previously, we used the stringified & encoded version of the key to extract a keyID, however this generated the same keyID for all rooms. As we didn't use this keyID, and rooms didn't have the capability of having multiple keys, this was harmless.
This PR introduces a new way of generating that identifier, making it random and unique, so multiple room keys can be used on the same room as long as the keyID is different.

NOTE: new E2EE rooms created _after_ this PR is merged will not be compatible with older versions of Rocket.Chat. Old rooms created before this update will continue to be compatible.
7 changes: 7 additions & 0 deletions apps/meteor/app/e2e/client/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,10 @@ export async function generateMnemonicPhrase(n, sep = ' ') {
}
return result.join(sep);
}

export async function createSha256Hash(data) {
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(data));
return Array.from(new Uint8Array(hash))
.map((b) => b.toString(16).padStart(2, '0'))
.join('');
}
16 changes: 11 additions & 5 deletions apps/meteor/app/e2e/client/rocketchat.e2e.room.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
readFileAsArrayBuffer,
encryptAESCTR,
generateAESCTRKey,
createSha256Hash,
} from './helper';
import { log, logError } from './logger';
import { e2e } from './rocketchat.e2e';
Expand Down Expand Up @@ -67,12 +68,13 @@ export class E2ERoom extends Emitter {

[PAUSED] = undefined;

constructor(userId, roomId, t) {
constructor(userId, room) {
super();

this.userId = userId;
this.roomId = roomId;
this.typeOfRoom = t;
this.roomId = room._id;
this.typeOfRoom = room.t;
this.roomKeyId = room.e2eKeyId;

this.once(E2ERoomState.READY, () => this.decryptPendingMessages());
this.once(E2ERoomState.READY, () => this.decryptSubscription());
Expand Down Expand Up @@ -280,7 +282,11 @@ export class E2ERoom extends Emitter {
return false;
}

this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);
// When a new e2e room is created, it will be initialized without an e2e key id
// This will prevent new rooms from storing `undefined` as the keyid
if (!this.keyID) {
this.keyID = this.roomKeyId || (await createSha256Hash(this.sessionKeyExportedString)).slice(0, 12);
}

// Import session key for use.
try {
Expand Down Expand Up @@ -308,7 +314,7 @@ export class E2ERoom extends Emitter {
try {
const sessionKeyExported = await exportJWKKey(this.groupSessionKey);
this.sessionKeyExportedString = JSON.stringify(sessionKeyExported);
this.keyID = Base64.encode(this.sessionKeyExportedString).slice(0, 12);
this.keyID = (await createSha256Hash(this.sessionKeyExportedString)).slice(0, 12);

await sdk.call('e2e.setRoomKeyID', this.roomId, this.keyID);
await this.encryptKeyForOtherParticipants();
Expand Down
80 changes: 41 additions & 39 deletions apps/meteor/app/e2e/client/rocketchat.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,52 +145,54 @@ class E2E extends Emitter {
this.log('observing subscriptions');
}

observeSubscriptions() {
this.observable?.stop();
async onSubscriptionChanged(sub: ISubscription) {
this.log('Subscription changed', sub);
if (!sub.encrypted && !sub.E2EKey) {
this.removeInstanceByRoomId(sub.rid);
return;
}

this.observable = Subscriptions.find().observe({
changed: (sub: ISubscription) => {
setTimeout(async () => {
this.log('Subscription changed', sub);
if (!sub.encrypted && !sub.E2EKey) {
this.removeInstanceByRoomId(sub.rid);
return;
}
const e2eRoom = await this.getInstanceByRoomId(sub.rid);
if (!e2eRoom) {
return;
}

const e2eRoom = await this.getInstanceByRoomId(sub.rid);
if (!e2eRoom) {
return;
}
if (sub.E2ESuggestedKey) {
if (await e2eRoom.importGroupKey(sub.E2ESuggestedKey)) {
await this.acceptSuggestedKey(sub.rid);
e2eRoom.keyReceived();
} else {
console.warn('Invalid E2ESuggestedKey, rejecting', sub.E2ESuggestedKey);
await this.rejectSuggestedKey(sub.rid);
}
}

if (sub.E2ESuggestedKey) {
if (await e2eRoom.importGroupKey(sub.E2ESuggestedKey)) {
await this.acceptSuggestedKey(sub.rid);
e2eRoom.keyReceived();
} else {
console.warn('Invalid E2ESuggestedKey, rejecting', sub.E2ESuggestedKey);
await this.rejectSuggestedKey(sub.rid);
}
}
sub.encrypted ? e2eRoom.resume() : e2eRoom.pause();

sub.encrypted ? e2eRoom.resume() : e2eRoom.pause();
// Cover private groups and direct messages
if (!e2eRoom.isSupportedRoomType(sub.t)) {
e2eRoom.disable();
return;
}

// Cover private groups and direct messages
if (!e2eRoom.isSupportedRoomType(sub.t)) {
e2eRoom.disable();
return;
}
if (sub.E2EKey && e2eRoom.isWaitingKeys()) {
e2eRoom.keyReceived();
return;
}

if (sub.E2EKey && e2eRoom.isWaitingKeys()) {
e2eRoom.keyReceived();
return;
}
if (!e2eRoom.isReady()) {
return;
}

if (!e2eRoom.isReady()) {
return;
}
await e2eRoom.decryptSubscription();
}

await e2eRoom.decryptSubscription();
}, 0);
observeSubscriptions() {
this.observable?.stop();

this.observable = Subscriptions.find().observe({
changed: (sub: ISubscription) => {
setTimeout(() => this.onSubscriptionChanged(sub), 0);
},
added: (sub: ISubscription) => {
setTimeout(async () => {
Expand Down Expand Up @@ -263,7 +265,7 @@ class E2E extends Emitter {
}

if (!this.instancesByRoomId[rid]) {
this.instancesByRoomId[rid] = new E2ERoom(Meteor.userId(), rid, room.t);
this.instancesByRoomId[rid] = new E2ERoom(Meteor.userId(), room);
}

return this.instancesByRoomId[rid];
Expand Down

0 comments on commit 24f16be

Please sign in to comment.