fix: Prevent E2EE key reset on startup (#32653)

pull/32316/head^2
Rodrigo Nascimento 2 years ago committed by GitHub
parent 865a5aac90
commit 495628bce0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      .changeset/nice-zebras-admire.md
  2. 5
      apps/meteor/app/api/server/v1/e2e.ts
  3. 14
      apps/meteor/app/e2e/client/rocketchat.e2e.ts
  4. 12
      apps/meteor/app/e2e/server/methods/setUserPublicAndPrivateKeys.ts
  5. 4
      apps/meteor/app/file-upload/client/lib/fileUploadHandler.ts
  6. 1
      packages/rest-typings/src/v1/e2e.ts
  7. 4
      packages/rest-typings/src/v1/e2e/e2eSetUserPublicAndPrivateKeysParamsPOST.ts

@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/rest-typings": patch
---
Prevent E2EE key reset on startup due to possible race conditions

@ -113,6 +113,8 @@ API.v1.addRoute(
* type: string
* private_key:
* type: string
* force:
* type: boolean
* responses:
* 200:
* content:
@ -135,11 +137,12 @@ API.v1.addRoute(
{
async post() {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { public_key, private_key } = this.bodyParams;
const { public_key, private_key, force } = this.bodyParams;
await Meteor.callAsync('e2e.setUserPublicAndPrivateKeys', {
public_key,
private_key,
force,
});
return API.v1.success();

@ -156,7 +156,11 @@ class E2E extends Emitter {
delete this.instancesByRoomId[rid];
}
async persistKeys({ public_key, private_key }: KeyPair, password: string): Promise<void> {
async persistKeys(
{ public_key, private_key }: KeyPair,
password: string,
{ force }: { force: boolean } = { force: false },
): Promise<void> {
if (typeof public_key !== 'string' || typeof private_key !== 'string') {
throw new Error('Failed to persist keys as they are not strings.');
}
@ -170,6 +174,7 @@ class E2E extends Emitter {
await sdk.rest.post('/v1/e2e.setUserPublicAndPrivateKeys', {
public_key,
private_key: encodedPrivateKey,
force,
});
}
@ -300,7 +305,7 @@ class E2E extends Emitter {
}
async changePassword(newPassword: string): Promise<void> {
await this.persistKeys(this.getKeysFromLocalStorage(), newPassword);
await this.persistKeys(this.getKeysFromLocalStorage(), newPassword, { force: true });
if (Meteor._localStorage.getItem('e2e.randomPassword')) {
Meteor._localStorage.setItem('e2e.randomPassword', newPassword);
@ -316,7 +321,10 @@ class E2E extends Emitter {
this.db_private_key = private_key;
} catch (error) {
this.setState(E2EEState.ERROR);
return this.error('Error fetching RSA keys: ', error);
this.error('Error fetching RSA keys: ', error);
// Stop any process since we can't communicate with the server
// to get the keys. This prevents new key generation
throw error;
}
}

@ -5,7 +5,7 @@ import { Meteor } from 'meteor/meteor';
declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }: { public_key: string; private_key: string }): void;
'e2e.setUserPublicAndPrivateKeys'({ public_key, private_key }: { public_key: string; private_key: string; force?: boolean }): void;
}
}
@ -19,6 +19,16 @@ Meteor.methods<ServerMethods>({
});
}
if (!keyPair.force) {
const keys = await Users.fetchKeysByUserId(userId);
if (keys.private_key && keys.public_key) {
throw new Meteor.Error('error-keys-already-set', 'Keys already set', {
method: 'e2e.setUserPublicAndPrivateKeys',
});
}
}
await Users.setE2EPublicAndPrivateKeysByUserId(userId, {
private_key: keyPair.private_key,
public_key: keyPair.public_key,

@ -5,7 +5,9 @@ import { Tracker } from 'meteor/tracker';
Tracker.autorun(() => {
const userId = Meteor.userId();
if (userId) {
// Check for Meteor.loggingIn to be reactive and ensure it will process only after login finishes
// preventing race condition setting the rc_token as null forever
if (userId && Meteor.loggingIn() === false) {
const secure = location.protocol === 'https:' ? '; secure' : '';
document.cookie = `rc_uid=${escape(userId)}; path=/${secure}`;

@ -8,6 +8,7 @@ const ajv = new Ajv({
type E2eSetUserPublicAndPrivateKeysProps = {
public_key: string;
private_key: string;
force?: boolean;
};
const E2eSetUserPublicAndPrivateKeysSchema = {

@ -7,6 +7,7 @@ const ajv = new Ajv({
export type e2eSetUserPublicAndPrivateKeysParamsPOST = {
public_key: string;
private_key: string;
force?: boolean;
};
const e2eSetUserPublicAndPrivateKeysParamsPOSTSchema = {
@ -18,6 +19,9 @@ const e2eSetUserPublicAndPrivateKeysParamsPOSTSchema = {
private_key: {
type: 'string',
},
force: {
type: 'boolean',
},
},
additionalProperties: false,
required: ['public_key', 'private_key'],

Loading…
Cancel
Save