Skip to content

Commit 495628b

Browse files
authored
fix: Prevent E2EE key reset on startup (#32653)
1 parent 865a5aa commit 495628b

File tree

7 files changed

+40
-6
lines changed

7 files changed

+40
-6
lines changed

.changeset/nice-zebras-admire.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@rocket.chat/meteor": patch
3+
"@rocket.chat/rest-typings": patch
4+
---
5+
6+
Prevent E2EE key reset on startup due to possible race conditions

apps/meteor/app/api/server/v1/e2e.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ API.v1.addRoute(
113113
* type: string
114114
* private_key:
115115
* type: string
116+
* force:
117+
* type: boolean
116118
* responses:
117119
* 200:
118120
* content:
@@ -135,11 +137,12 @@ API.v1.addRoute(
135137
{
136138
async post() {
137139
// eslint-disable-next-line @typescript-eslint/naming-convention
138-
const { public_key, private_key } = this.bodyParams;
140+
const { public_key, private_key, force } = this.bodyParams;
139141

140142
await Meteor.callAsync('e2e.setUserPublicAndPrivateKeys', {
141143
public_key,
142144
private_key,
145+
force,
143146
});
144147

145148
return API.v1.success();

apps/meteor/app/e2e/client/rocketchat.e2e.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ class E2E extends Emitter {
156156
delete this.instancesByRoomId[rid];
157157
}
158158

159-
async persistKeys({ public_key, private_key }: KeyPair, password: string): Promise<void> {
159+
async persistKeys(
160+
{ public_key, private_key }: KeyPair,
161+
password: string,
162+
{ force }: { force: boolean } = { force: false },
163+
): Promise<void> {
160164
if (typeof public_key !== 'string' || typeof private_key !== 'string') {
161165
throw new Error('Failed to persist keys as they are not strings.');
162166
}
@@ -170,6 +174,7 @@ class E2E extends Emitter {
170174
await sdk.rest.post('/v1/e2e.setUserPublicAndPrivateKeys', {
171175
public_key,
172176
private_key: encodedPrivateKey,
177+
force,
173178
});
174179
}
175180

@@ -300,7 +305,7 @@ class E2E extends Emitter {
300305
}
301306

302307
async changePassword(newPassword: string): Promise<void> {
303-
await this.persistKeys(this.getKeysFromLocalStorage(), newPassword);
308+
await this.persistKeys(this.getKeysFromLocalStorage(), newPassword, { force: true });
304309

305310
if (Meteor._localStorage.getItem('e2e.randomPassword')) {
306311
Meteor._localStorage.setItem('e2e.randomPassword', newPassword);
@@ -316,7 +321,10 @@ class E2E extends Emitter {
316321
this.db_private_key = private_key;
317322
} catch (error) {
318323
this.setState(E2EEState.ERROR);
319-
return this.error('Error fetching RSA keys: ', error);
324+
this.error('Error fetching RSA keys: ', error);
325+
// Stop any process since we can't communicate with the server
326+
// to get the keys. This prevents new key generation
327+
throw error;
320328
}
321329
}
322330

apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor';
55
declare module '@rocket.chat/ui-contexts' {
66
// eslint-disable-next-line @typescript-eslint/naming-convention
77
interface ServerMethods {
8-
'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }: { public_key: string; private_key: string }): void;
8+
'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }: { public_key: string; private_key: string; force?: boolean }): void;
99
}
1010
}
1111

@@ -19,6 +19,16 @@ Meteor.methods<ServerMethods>({
1919
});
2020
}
2121

22+
if (!keyPair.force) {
23+
const keys = await Users.fetchKeysByUserId(userId);
24+
25+
if (keys.private_key && keys.public_key) {
26+
throw new Meteor.Error('error-keys-already-set', 'Keys already set', {
27+
method: 'e2e.setUserPublicAndPrivateKeys',
28+
});
29+
}
30+
}
31+
2232
await Users.setE2EPublicAndPrivateKeysByUserId(userId, {
2333
private_key: keyPair.private_key,
2434
public_key: keyPair.public_key,

apps/meteor/app/file-upload/client/lib/fileUploadHandler.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { Tracker } from 'meteor/tracker';
55
Tracker.autorun(() => {
66
const userId = Meteor.userId();
77

8-
if (userId) {
8+
// Check for Meteor.loggingIn to be reactive and ensure it will process only after login finishes
9+
// preventing race condition setting the rc_token as null forever
10+
if (userId && Meteor.loggingIn() === false) {
911
const secure = location.protocol === 'https:' ? '; secure' : '';
1012

1113
document.cookie = `rc_uid=${escape(userId)}; path=/${secure}`;

packages/rest-typings/src/v1/e2e.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const ajv = new Ajv({
88
type E2eSetUserPublicAndPrivateKeysProps = {
99
public_key: string;
1010
private_key: string;
11+
force?: boolean;
1112
};
1213

1314
const E2eSetUserPublicAndPrivateKeysSchema = {

packages/rest-typings/src/v1/e2e/e2eSetUserPublicAndPrivateKeysParamsPOST.ts

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const ajv = new Ajv({
77
export type e2eSetUserPublicAndPrivateKeysParamsPOST = {
88
public_key: string;
99
private_key: string;
10+
force?: boolean;
1011
};
1112

1213
const e2eSetUserPublicAndPrivateKeysParamsPOSTSchema = {
@@ -18,6 +19,9 @@ const e2eSetUserPublicAndPrivateKeysParamsPOSTSchema = {
1819
private_key: {
1920
type: 'string',
2021
},
22+
force: {
23+
type: 'boolean',
24+
},
2125
},
2226
additionalProperties: false,
2327
required: ['public_key', 'private_key'],

0 commit comments

Comments
 (0)